Git使用上手

Git使用上手

一、起步

1.1 安装

Windows、Linux请自行前往官网,按照说明完成

这里说一下Mac版本,分两种情况:

  1. 使用Apple提供的git发行版:这种情况使用的是Xcode内的Command Line Tools
  2. 使用homebrew安装,强烈建议.因为Xcode内的git工具不太完整,缺少gitk、HTML格式的文档等

1.2 最小化配置

配置user.name和user.email

1
2
git config --global user.name 'your_name'
git config --global user.email 'your_email@domain.com'

1.2.1 config的三个作用域

1
2
3
git config --local  # 只针对某仓库
git config --global # 对当前用户的所有仓库有效
git config --system # 对该系统所有登陆的用户有效

显示config的配置,加--list

1
2
3
git config --list --local
git config --list --global
git config --list --system

1.3 使用本地HTML文档

1
2
# 例如查看git log命令的使用文档
git help log --web

二、建一个Git仓库

2.1 初始化

此时存在两种场景:

  1. 项目代码已经存在,要把项目加入Git版本控制
  2. 新建一个项目并使用Git来管理

针对第一种情况,我们使用如下命令:

1
2
cd 项目代码所在的文件夹
git init

针对第二种情况,则是使用如下命令:

1
2
3
cd 某个文件夹
git init your_project # git会在当前目录下创建和项目名称同名的文件夹
cd your_project

2.2 将文件加入Git版本控制

1
git add 文件名 # 如果不将文件纳入版本控制,后续操作则会报错

然后查看一下当前的状态:

1
git status # 可查看当前git项目的状态,如哪些文件被修改,而哪些文件未被版本管理

此时的文件,是存在于临时的缓存(暂存区)当中,要真正提交还需要执行以下命令:

1
git commit -m "变更说明"

这样我们就真正的将一个文件提交进版本了,我们还可以看一下当前的日志:

1
2
3
4
5
6
7
8
git log

# git log还有几种使用方法,例如
git log 分支名 # 查看指定分支的历史;默认显示当前分支
git log --all # 显示所有分支的版本历史;默认显示当前分支
git log --oneline # 简洁显示变更历史,每次变更只显示一行
git log -n2 # 显示最近的两次变更
git log --all --graph # 在上面的基础上,添加图形化的版本演进

2.2.1 新建和切换分支

1
2
3
4
5
6
7
8
git checkout -b 分支名

# 这个命令其实相当于创建分支和切换分支两条命令(因为-b参数)
git branch 分支名 # 创建分支
git checkout 分支名 # 切换分支

# 查看当前工作分支
git branch -av

2.3 回退修改

1
git reset --hard # 彻底回退,暂存区和所有未提交的变更记录都会被清理

2.4 文件重命名

1
git mv 文件原名 文件现名

2.5 比较版本的差异

我们可以先查看一下历史:

1
2
3
4
5
6
7
$ git log --oneline

af289f0 (HEAD -> master) add OnlyMaster.rs
1f39a38 add Hello.c
1d294ca (test) hello.py renamed to Hello.py
5f06285 add hello.py
7499e49 add ReadMe
1
2
3
4
5
6
7
8
9
10
$ git diff 7499e49 5f06285

diff --git a/hello.py b/hello.py
new file mode 100644
index 0000000..ce47b77
--- /dev/null
+++ b/hello.py
@@ -0,0 +1 @@
+print("hello")
\ No newline at end of file

如果是使用当前版本进行比较,我们可以使用HEAD来指代:

1
2
3
4
$ git diff 5f06285 HEAD

# 和当前版本的上一个版本比较
$ git diff HEAD~1 HEAD # 其中数字可以替换,也可以使用~~来代表~2

2.6 删除分支

1
2
3
4
$ git branch -d <BranchName>

# 有时候-d会删除不了,如果确认没有风险,可以使用-D
$ git branch -D <BranchName>

三、工作区与暂存区的认识

Git当中,如果仅对文件作了add,但没有执行commit的话,文件会被存储进暂存区,而我们对文件进行创作修改的区域被称为工作区

当我们将文件提交进暂存区后,依然可以在工作区继续修改我们的文件,而此时暂存区内的文件是独立于工作区

这也就意味着,我们随时可以用暂存区内的版本覆盖工作区(如对工作区的修改不满意),而又不需要提交到版本控制,这在实践当中非常实用

补充:

1
git add -u # 将项目中所有工作区的文件一起提交到暂存区,而不需要一个个写文件名

四、图形化工具的介绍

直接在命令行输入gitk即可.

在MacOS上,如果使用的是Xcode提供的git是没有gitk工具的,需要使用brew安装一下

同时如果使用的是MacOS的暗主题,gitk的菜单栏上的文字会因为和底色相同的原因看不见,需要将鼠标移上去

五、了解.git目录

5.1 HEAD文件

首先,我们进入.git目录,尝试查看一下HEAD文件的内容:

1
2
$ cat HEAD
ref: refs/heads/master

我们会发现HEAD文件其实是一个引用,而且指向的位置和当前工作的分支有关,如当前工作分支为master

而假设我们当前切换分支到了test,那么此时HEAD文件就会指向test分支

5.2 config文件

该文件下除了保存了该仓库下的核心配置之外,如果设置了仓库级别的用户配置,也会写入该文件

例如:

1
git config --local user.username "test" # 修改当前仓库的用户名

该文件内的内容则会变为:

1
2
3
4
5
6
7
8
9
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
username = test

5.3 refs目录

在refs目录中,存在两个子目录:

  1. heads:里面有各个分支的子文件
  2. tags:里面有各个标签(可看作是里程碑)的子文件

先看heads目录,我们先查看一下master文件的内容:

1
2
$ cat master
1d294ca4f3dd9a0d2adef1528f18afd8ef3ca3d3

我们还可以查看该文件是什么类型的:

1
2
$ git cat-file -t master # -t参数可以查看类型
commit

同样的,在tag目录下的文件里面存放的也是hash值,文件类型属于tag

通过以下命令可以查看该tag是存放的哪一个commit对象:

1
git cat-file -p tag名或tag的hash # -p参数可以查看内容

5.4 objects目录

我们进入objects目录后,会看到很多两个字符组成的文件夹,以及pack(打包目录)

例如:

1
2
3
4
5
6
7
8
9
$ ls -la
.
..
06
1d
3d
3e
info
pack

我们随便挑选一个两个字符的目录进去,会发现内部文件是这样的:

1
2
3
4
5
$ cd 06
$ ls -la
.
..
0fede4878a49e95fc444e9a243fa77edc710f4

此时我们可以使用命令查看一下该对象的类型:

1
2
3
# 注意:此处的hash为[两位长度的目录名 + 目录内文件名]
$ git cat-file -t 060fede4878a49e95fc444e9a243fa77edc710f4
tree

tree即目录类型

然后,我们可以查看该对象的内容,使用以下命令(参数: -p):

1
2
3
$ git cat-file -p 060fede4878a49e95fc444e9a243fa77edc710f4
100644 blob 827ae020300b109c1e0e7ee58891e592d85df66c README.md
100644 blob ce47b771f4fdb0c612745ca4b7c36695f3853f7c hello.py

blob即是文件对象类型

我们可以进一步查看该blob类型:

1
2
3
4
5
6
$ git cat-file -t 827ae020300b109c1e0e7ee58891e592d85df66c # 查看类型
blob

$ git cat-file -p 827ae020300b109c1e0e7ee58891e592d85df66c # 查看内容
版本1:
1+1=2

至此,我们已经接触了git的如下几种类型:

  1. commit
  2. tag
  3. tree
  4. blob

提示:git中,只要两个文件内容相同,那么对于git就是唯一的一个blob

5.4.1 commit、tree、blob三者的关系

我们可以通过git log命令查询到每一次的commit的hash,如果我们查询某次commit的内容:

1
2
3
4
5
6
$ git cat-file -p 7499e499b62642764e31475f # hash不需要输入完整,只要能让git唯一找到目标就可以
tree 3d5799c550fa4b815545f0255ad83d16debce7f0
author SherlockGy <sherlockgy@live.com> 1574435790 +0800
committer SherlockGy <sherlockgy@live.com> 1574435790 +0800

add ReadMe

我们会发现,commit对象中永远存储了一个tree,该tree代表截止该的commit为止,此git项目的所有内容,我们可以继续查看该tree的内容:

1
2
$ git cat-file -p 3d5799c550fa4b815545f02
100644 blob 827ae020300b109c1e0e7ee58891e592d85df66c README.md

也就是说,此commit提交后,我们的git项目中仅有一个文件,因此显示为一个blob对象;如果此时还有子目录的话,会显示为一个tree对象

这就是三种对象的关系

六、使用分离头指针

现在假设有这样一个场景,我们要基于master的倒数第二个commit来创建一个分支,由于我们并不是基于最新的commit,所以要先把分支切换到commit上,而不是branch上,这就是分离头指针

不过千万要谨慎操作,因为分离后的branch是没有名字的,如果不赋予名字,后面会导致修改丢失.

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 切换到一个commit上,而不是branch,从而实现分离头指针
$ git checkout 1d294ca4f3dd9a0d2adef1528f18afd8ef3ca3d3

注意:正在切换到 '1d294ca4f3dd9a0d2adef1528f18afd8ef3ca3d3'。

您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以在切换
回一个分支时,丢弃在此状态下所做的提交而不对分支造成影响。

如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在 switch 命令
中添加参数 -c 来实现(现在或稍后)。例如:

git switch -c <新分支名>

或者撤销此操作:

git switch -

通过将配置变量 advice.detachedHead 设置为 false 来关闭此建议

HEAD 目前位于 1d294ca hello.py renamed to Hello.py

然后我们查看一下当前branch的情况:

1
2
3
4
5
6
$ git branch

* (头指针分离于 1d294ca)
master
test
test2

现在让我们来创建一个分离后的文件吧:

1
2
3
$ echo "我是分离头指针版本独有的文件内容" > 分离头指针.txt
$ git add 分离头指针.txt
$ git commit -m "add 分离头指针.txt"

此时我们的新分支是没有名字的,如果我们切换到其他分支,有可能会导致该分支上的修改失效

这里我们尝试切换到master分支:

1
2
3
4
5
6
7
8
9
10
11
12
$ git checkout master

警告:您正丢下 1 个提交,未和任何分支关联:

718daed add 分离头指针.txt

如果您想要通过创建新分支保存它,这可能是一个好时候。
如下操作:

git branch <新分支名> 718daed

切换到分支 'master'

所以这里我们按照提示,给没有名字的新分支一个名字:

1
2
3
4
5
6
7
8
$ git branch 实验分离头指针分支 718daed

$ git branch

* master
test
test2
实验分离头指针分支

然后我们来一起看一下当前的log:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$ git log --all --graph

* commit 718daedc590da60e771debf95657de8639d4e1db (实验分离头指针分支)
| Author: SherlockGy <sherlockgy@live.com>
| Date: Sun Nov 24 15:58:00 2019 +0800
|
| add 分离头指针.txt
|
| * commit af289f05ffcba29d35eaaa30e6105244ed67b0ed (HEAD -> master)
| | Author: SherlockGy <sherlockgy@live.com>
| | Date: Sun Nov 24 15:51:37 2019 +0800
| |
| | add OnlyMaster.rs
| |
| | * commit 5467beb8fdc754ddb14e31dfcd58790dfd9337fb (test2)
| |/ Author: SherlockGy <sherlockgy@live.com>
| | Date: Sun Nov 24 15:48:25 2019 +0800
| |
| | add HelloAgain.go
| |
| * commit 1f39a383ff3c1140affc1e128b0de17b1c909238
|/ Author: SherlockGy <sherlockgy@live.com>
| Date: Sun Nov 24 15:22:02 2019 +0800
|
| add Hello.c
|
* commit 1d294ca4f3dd9a0d2adef1528f18afd8ef3ca3d3 (test)
| Author: SherlockGy <sherlockgy@live.com>
| Date: Sat Nov 23 20:26:10 2019 +0800
|
| hello.py renamed to Hello.py
|
* commit 5f0628574c79dbc9ebfb1856ec882a80e5946c2b
| Author: SherlockGy <sherlockgy@live.com>
| Date: Sat Nov 23 19:37:30 2019 +0800
|
| add hello.py
|
* commit 7499e499b62642764e31475f4119dc253ca2f151
Author: SherlockGy <sherlockgy@live.com>
Date: Fri Nov 22 23:16:30 2019 +0800

add ReadMe

由此可见,我们顺利得在master之前的版本上创建了分支