放牛的

放牛的日子,是人生初恋的诗...

0%

git探秘

1. 仓库初始化

1.1 准备空文件夹

为了探索git,我们建一个新的文件夹,在这里,可以随便怎么搞

1
2
3
4
5
6
7
8
9
10
11
12
# 删除临时文件夹(如果存在的话),创建一个临时文件夹,并进入 
➜ rm -rf tmp && mkdir tmp && cd $_

# 显示当前文件夹的路径
➜ pwd
/Users/lewis/tmp

# 目前,里面是空的,一个空文件夹准备好了
➜ ls -al
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:47 .
drwxr-xr-x+ 128 lewis staff 4096 3 4 09:48 ..

1.2 git初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 在这个文件夹中,建立git仓库
➜ git init
Initialized empty Git repository in /Users/lewis/tmp/.git/

# 看到多了一个 .git 文件夹
➜ ls -al
total 0
drwxr-xr-x 3 lewis staff 96 3 4 09:41 .
drwxr-xr-x+ 128 lewis staff 4096 3 4 09:44 ..
drwxr-xr-x 9 lewis staff 288 3 4 09:43 .git

# 看看这个文件夹里有什么,嗯,有3个文件 和 4个文件夹
➜ ls -al .git
total 24
drwxr-xr-x 9 lewis staff 288 3 4 10:10 .
drwxr-xr-x 3 lewis staff 96 3 4 09:53 ..
-rw-r--r-- 1 lewis staff 23 3 4 09:53 HEAD
-rw-r--r-- 1 lewis staff 137 3 4 09:53 config
-rw-r--r-- 1 lewis staff 73 3 4 09:53 description
drwxr-xr-x 13 lewis staff 416 3 4 09:53 hooks
drwxr-xr-x 3 lewis staff 96 3 4 09:53 info
drwxr-xr-x 4 lewis staff 128 3 4 09:53 objects
drwxr-xr-x 4 lewis staff 128 3 4 09:53 refs

1.3 .git文件夹初探

这些都是干嘛用的呢?通过文件名,有一些好猜的,和一些不太好理解的

  • 好猜的,不妨先猜一下,

    • config:配置文件
    • description:描述文件,具体描述什么还不清楚,感觉不是核心
    • hooks:钩子文件夹,应该是做扩展功能用的,在主流程上加些钩子
    • info:存的是一些信息,什么信息要看一下
  • 不好猜的

    • HEAD:头?你个大头鬼啊。。。
    • objects:存的是对象?什么是对象?
    • refs:references的简写?存的是对象的引用?
  • 感觉好猜的部分,是辅助功能,不好猜的才是核心

1.3.1 辅助功能

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
44
45
46
47
48
# config里面存了什么?
➜ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
# 感觉就是些配置,能看懂一个 ignorecase,是忽略大小写,总之,这是配置文件

# description文件呢?
➜ cat .git/description
Unnamed repository; edit this file 'description' to name the repository.
# 看描述,是给仓库命名用的,先不管它

# 看看hooks文件夹
➜ ls -al .git/hooks
total 96
drwxr-xr-x 13 lewis staff 416 3 4 09:53 .
drwxr-xr-x 9 lewis staff 288 3 4 10:24 ..
-rwxr-xr-x 1 lewis staff 478 3 4 09:53 applypatch-msg.sample
-rwxr-xr-x 1 lewis staff 896 3 4 09:53 commit-msg.sample
-rwxr-xr-x 1 lewis staff 3327 3 4 09:53 fsmonitor-watchman.sample
-rwxr-xr-x 1 lewis staff 189 3 4 09:53 post-update.sample
-rwxr-xr-x 1 lewis staff 424 3 4 09:53 pre-applypatch.sample
-rwxr-xr-x 1 lewis staff 1638 3 4 09:53 pre-commit.sample
-rwxr-xr-x 1 lewis staff 1348 3 4 09:53 pre-push.sample
-rwxr-xr-x 1 lewis staff 4898 3 4 09:53 pre-rebase.sample
-rwxr-xr-x 1 lewis staff 544 3 4 09:53 pre-receive.sample
-rwxr-xr-x 1 lewis staff 1492 3 4 09:53 prepare-commit-msg.sample
-rwxr-xr-x 1 lewis staff 3610 3 4 09:53 update.sample
# 里面都是些示例文件,和猜测一样,是正常流程的钩子

# info文件夹下,只有一个文件
➜ tmp git:(master) ls -al .git/info
total 8
drwxr-xr-x 3 lewis staff 96 3 4 09:53 .
drwxr-xr-x 9 lewis staff 288 3 4 10:26 ..
-rw-r--r-- 1 lewis staff 240 3 4 09:53 exclude
# exclude文件里的内容,看不太懂,感觉是排除规则之类的
➜ cat .git/info/exclude
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

1.3.2 核心功能

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
# HEAD 文件,这意思,是指向refs文件夹吗?
➜ cat .git/HEAD
ref: refs/heads/master

# 那就先看refs里面有什么吧!
➜ tmp git:(master) ls -alR .git/refs
total 0
drwxr-xr-x 4 lewis staff 128 3 4 09:53 .
drwxr-xr-x 9 lewis staff 288 3 4 10:34 ..
drwxr-xr-x 2 lewis staff 64 3 4 09:53 heads
drwxr-xr-x 2 lewis staff 64 3 4 09:53 tags

.git/refs/heads:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..

.git/refs/tags:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..
# refs里面有两个子文件夹,heads 和 tags,目前都是空的
# 有个heads,看来HEAD很可能是指向这个里面的

# objects里面,也是两个空文件夹
➜ tmp git:(master) ls -alR .git/objects
total 0
drwxr-xr-x 4 lewis staff 128 3 4 09:53 .
drwxr-xr-x 9 lewis staff 288 3 4 10:37 ..
drwxr-xr-x 2 lewis staff 64 3 4 09:53 info
drwxr-xr-x 2 lewis staff 64 3 4 09:53 pack

.git/objects/info:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..

.git/objects/pack:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..

2. 初识git

2.1 创建文件

1
2
3
4
5
# 创建一个空文件,取名为 hello.txt
➜ touch hello.txt
➜ ll
total 0
-rw-r--r-- 1 lewis staff 0B 3 4 10:46 hello.txt

2.2 交给git管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 加到git中
➜ git add hello.txt
# 此时,refs 和 objects无变化,但多出了一个index文件
➜ ls -al .git/
total 32
drwxr-xr-x 10 lewis staff 320 3 4 10:47 .
drwxr-xr-x 4 lewis staff 128 3 4 10:46 ..
-rw-r--r-- 1 lewis staff 23 3 4 09:53 HEAD
-rw-r--r-- 1 lewis staff 137 3 4 09:53 config
-rw-r--r-- 1 lewis staff 73 3 4 09:53 description
drwxr-xr-x 13 lewis staff 416 3 4 09:53 hooks
-rw-r--r-- 1 lewis staff 104 3 4 10:47 index
drwxr-xr-x 3 lewis staff 96 3 4 09:53 info
drwxr-xr-x 5 lewis staff 160 3 4 10:47 objects
drwxr-xr-x 4 lewis staff 128 3 4 09:53 refs
➜ cat .git/index
DIRC^_????^_????YI?????⛲??CK?)?wZ???S? hello.txt?kEO?
?Z.?!P?a?5e?8%
# index这个文件中有乱码,看来存的是二进制
# 能看到hello.txt,说明git add后,改动是存到这个文件的

结论一:

git add 指令,改动了「.git/index」这个文件。

2.3 提交为一个版本

1
2
3
4
5
# 提交
➜ git commit -m 'first file'
[master (root-commit) 528022a] first file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello.txt

2.4 初步分析

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 看看index,感觉有改动,但看不出来具体做了什么
➜ tmp git:(master) cat .git/index
DIRC^_????^_????YI?????⛲??CK?)?wZ???S? hello.txtTREE1 0
:1???[B??B???%
# refs文件夹中,在heads里,多了一个master文件
➜ tmp git:(master) ls -alR .git/refs
total 0
drwxr-xr-x 4 lewis staff 128 3 4 09:53 .
drwxr-xr-x 12 lewis staff 384 3 4 11:06 ..
drwxr-xr-x 3 lewis staff 96 3 4 10:48 heads
drwxr-xr-x 2 lewis staff 64 3 4 09:53 tags

.git/refs/heads:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:48 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..
-rw-r--r-- 1 lewis staff 41 3 4 10:48 master

.git/refs/tags:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..

# objects文件夹中,多了3个文件夹,每个文件夹里都出现了一个文件
➜ tmp git:(master) ls -alR .git/objects
total 0
drwxr-xr-x 7 lewis staff 224 3 4 10:48 .
drwxr-xr-x 12 lewis staff 384 3 4 10:50 ..
drwxr-xr-x 3 lewis staff 96 3 4 10:48 52
drwxr-xr-x 3 lewis staff 96 3 4 10:47 e6
drwxr-xr-x 3 lewis staff 96 3 4 10:48 fc
drwxr-xr-x 2 lewis staff 64 3 4 09:53 info
drwxr-xr-x 2 lewis staff 64 3 4 09:53 pack

.git/objects/52:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:48 .
drwxr-xr-x 7 lewis staff 224 3 4 10:48 ..
-r--r--r-- 1 lewis staff 127 3 4 10:48 8022a97b413fae3f4a8cc0e308e5506a598ea3

.git/objects/e6:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:47 .
drwxr-xr-x 7 lewis staff 224 3 4 10:48 ..
-r--r--r-- 1 lewis staff 15 3 4 10:47 9de29bb2d1d6434b8b29ae775ad8c2e48c5391

.git/objects/fc:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:48 .
drwxr-xr-x 7 lewis staff 224 3 4 10:48 ..
-r--r--r-- 1 lewis staff 54 3 4 10:48 b545d5746547a597811b7441ed8eba307be1ff

.git/objects/info:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 7 lewis staff 224 3 4 10:48 ..

.git/objects/pack:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 7 lewis staff 224 3 4 10:48 ..

结论二:

git commit时,对「.git/index」文件有改动;

git commit时,在「.git/objects」文件夹下,会增加文件;

2.5 引用分析

1
2
3
4
5
6
7
8
9
10
11
12
# 有没有发现,refs里面多出来的文件,正是HEAD文件所指向的?
➜ cat .git/HEAD
ref: refs/heads/master
➜ ls -al .git/refs/heads/master
-rw-r--r-- 1 lewis staff 41 3 4 10:48 .git/refs/heads/master

# 那这个master文件里面又存了什么?
➜ tmp git:(master) cat .git/refs/heads/master
528022a97b413fae3f4a8cc0e308e5506a598ea3
# 看着有点眼熟,确认一下
➜ tmp git:(master) ls -al .git/objects/52/8022a97b413fae3f4a8cc0e308e5506a598ea3
-r--r--r-- 1 lewis staff 127 3 4 10:48 .git/objects/52/8022a97b413fae3f4a8cc0e308e5506a598ea3

结论三:

「.git/HEAD」是一个指针文件,可以指向「.git/refs」中的文件;

「.git/refs/heads/master」文件也是一个指针,指向「.git/objects」里面的一个文件;

2.6 对象分析

1
2
3
4
5
6
7
# 用了个不熟悉的命令,它是查看这一串对象中存储内容的一个指令
➜ git cat-file -p 528022a97b413fae3f4a8cc0e308e5506a598ea3
tree fcb545d5746547a597811b7441ed8eba307be1ff
author Lewis <fangniude@gmail.com> 1583290125 +0800
committer Lewis <fangniude@gmail.com> 1583290125 +0800

first file
  • 可以看到,文件中有committerfirst file,说明这个对象存储的是commit的信息,就叫它commit对象
  • 还有一个tree,指向了另一个对象,那就叫它tree对象
1
2
3
# 看看 tree对象 中存了什么
➜ git cat-file -p fcb545d5746547a597811b7441ed8eba307be1ff
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.txt
  • tree对象 指向了第3个对象,感觉就是我们刚才新建的hello.txt文件
1
2
# 看看hello.txt 文件
➜ git cat-file -p e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • 这个文件里面是空的,我们刚才的确没有写内容进去

结论四:

「.git/objects」里面,会存储commit对象、tree对象 和 blob对象;

commit对象,对应的是一个commit,会指向一个tree对象;

tree对象对应的是文件夹,还存储了其中文件的名字 和 这些文件的指针;

blob对象存储文件的内容

2.7 第1次提交图

1
2
3
4
5
6
7
graph LR

T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

M((master)) -.-> C1
H((HEAD)) -.-> M

2.8 第2次提交

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 再建一个空的文件
➜ touch hello.copy
# 交给git管理
➜ git add hello.copy
# 提交
➜ git commit -m 'add hello.copy'
[master 9222323] add hello.copy
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello.copy

# 继续分析,master文件指向的对象变了
➜ cat .git/refs/heads/master
92223238a21c13bec2663fe349e8ea1dbfab97bb

# 这是新的commit对象,除了指向tree对象之外,还指向了一个parent
➜ git cat-file -p 92223238a21c13bec2663fe349e8ea1dbfab97bb
tree 598293c40ffff2b5d26ab7f9d78601e8d8d05d94
parent 528022a97b413fae3f4a8cc0e308e5506a598ea3
author Lewis <fangniude@gmail.com> 1583295073 +0800
committer Lewis <fangniude@gmail.com> 1583295073 +0800

add hello.copy

# parent对象就是上一次commit
➜ tmp git:(master) git cat-file -p 528022a97b413fae3f4a8cc0e308e5506a598ea3
tree fcb545d5746547a597811b7441ed8eba307be1ff
author Lewis <fangniude@gmail.com> 1583290125 +0800
committer Lewis <fangniude@gmail.com> 1583290125 +0800

first file

# tree对象里面,有了两个文件,两个文件指向了同一个blob对象
➜ tmp git:(master) git cat-file -p 598293c40ffff2b5d26ab7f9d78601e8d8d05d94
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.copy
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.txt

# 不妨看看此时的objects,变成5个对象,多了一个commit对象 和 一个tree对象
➜ tmp git:(master) ls -alR .git/objects
total 0
drwxr-xr-x 9 lewis staff 288 3 4 12:11 .
drwxr-xr-x 12 lewis staff 384 3 4 12:29 ..
drwxr-xr-x 3 lewis staff 96 3 4 10:48 52
drwxr-xr-x 3 lewis staff 96 3 4 12:11 59
drwxr-xr-x 3 lewis staff 96 3 4 12:11 92
drwxr-xr-x 3 lewis staff 96 3 4 10:47 e6
drwxr-xr-x 3 lewis staff 96 3 4 10:48 fc
drwxr-xr-x 2 lewis staff 64 3 4 09:53 info
drwxr-xr-x 2 lewis staff 64 3 4 09:53 pack

.git/objects/52:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:48 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..
-r--r--r-- 1 lewis staff 127 3 4 10:48 8022a97b413fae3f4a8cc0e308e5506a598ea3

.git/objects/59:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 12:11 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..
-r--r--r-- 1 lewis staff 63 3 4 12:11 8293c40ffff2b5d26ab7f9d78601e8d8d05d94

.git/objects/92:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 12:11 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..
-r--r--r-- 1 lewis staff 160 3 4 12:11 223238a21c13bec2663fe349e8ea1dbfab97bb

.git/objects/e6:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:47 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..
-r--r--r-- 1 lewis staff 15 3 4 12:11 9de29bb2d1d6434b8b29ae775ad8c2e48c5391

.git/objects/fc:
total 8
drwxr-xr-x 3 lewis staff 96 3 4 10:48 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..
-r--r--r-- 1 lewis staff 54 3 4 10:48 b545d5746547a597811b7441ed8eba307be1ff

.git/objects/info:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..

.git/objects/pack:
total 0
drwxr-xr-x 2 lewis staff 64 3 4 09:53 .
drwxr-xr-x 9 lewis staff 288 3 4 12:11 ..

结论五:

增加一个commit后,master这个指针会指向新的commit;

commit对象可以指向其parent,即上一次提交;

同一个文件(内容相同的对象)只存储一份;

2.9 第2次提交图

1
2
3
4
5
6
7
8
9
10
11
12
graph LR
T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

T2[/第2次提交tree/] -->|hello.txt| F1
T2 -->|hell.copy| F1
C2[第2次提交] -->T2

C2 -->C1

M((master)) -.-> C2
H((HEAD)) -.-> M

3. 分支branch

3.1 建dev分支

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
# 看看当前有哪些分支
➜ git branch
* master
(END)
# 看到只有一个master分支,前面有个`*`代表当前在这个分支

# 创建dev分支
➜ git branch dev
➜ git branch
dev
* master
(END)

# 切换到dev分支
➜ git checkout dev
Switched to branch 'dev'
➜ git branch
* dev
master
(END)

# 看看现在HEAD在哪儿?
➜ cat .git/HEAD
ref: refs/heads/dev

# 多出来一个dev文件,其中的内容 和 master完成一样
➜ cat .git/refs/heads/dev
92223238a21c13bec2663fe349e8ea1dbfab97bb
➜ cat .git/refs/heads/master
92223238a21c13bec2663fe349e8ea1dbfab97bb

# 看看 .git/refs/heads 文件夹下的内容
➜ ls -al .git/refs/heads
total 16
drwxr-xr-x 4 lewis staff 128 3 4 14:31 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..
-rw-r--r-- 1 lewis staff 41 3 4 14:31 dev
-rw-r--r-- 1 lewis staff 41 3 4 12:11 master
1
2
3
4
5
6
7
8
9
10
11
12
13
graph LR
T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

T2[/第2次提交tree/] -->|hello.txt| F1
T2 -->|hell.copy| F1
C2[第2次提交] -->T2

C2 -->C1

M((master)) -.-> C2
D((dev)) -.-> C2
H((HEAD)) -.-> D

结论六:

分支是通过「refs/heads」下面的文件来管理,一个文件一个分支;

HEAD 指向哪儿,代表当前在哪个分支

3.2 dev分支提交

注意: 为了简化,我们在dev分支,先退回「第1次提交」,然后再做「第3次提交」

1
2
3
4
5
6
7
8
9
10
11
# 回到第1次提交
➜ git reset --hard 528022a97b413fae3f4a8cc0e308e5506a598ea3
HEAD is now at 528022a first file

# 验证一下,的确只有一个文件
➜ tmp git:(dev) ls -al
total 0
drwxr-xr-x 4 lewis staff 128 3 4 14:54 .
drwxr-xr-x+ 128 lewis staff 4096 3 4 14:55 ..
drwxr-xr-x 13 lewis staff 416 3 4 14:55 .git
-rw-r--r-- 1 lewis staff 0 3 4 10:46 hello.txt

然后,增加一个文件夹,并在其中增加个文件

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
44
45
46
47
48
49
50
51
52
53
# 新建 testBranch文件夹
➜ mkdir testBranch
# 添加个有内容的文件
➜ echo 'this is dev file' > testBranch/devAdd.txt
# 确认下,文件添加成功了,其中有内容
➜ cat testBranch/devAdd.txt
this is dev file

# 加到index中
➜ git add testBranch/devAdd.txt

# 第3次提交
➜ git commit -m 'third commit'
[dev 1616454] third commit
1 file changed, 1 insertion(+)
create mode 100644 testBranch/devAdd.txt

# 当前是怎么样的呢?
# 先看HEAD,还是指向dev
➜ tmp git:(dev) cat .git/HEAD
ref: refs/heads/dev

# dev指向了第3次提交
➜ tmp git:(dev) cat .git/refs/heads/dev
161645439e35c5db8f4755641ca98815c69a9d63
➜ tmp git:(dev) git cat-file -p 161645439e35c5db8f4755641ca98815c69a9d63
tree fb54f679949f2593bb83d4179f8c7d058393235c
parent 528022a97b413fae3f4a8cc0e308e5506a598ea3
author Lewis <fangniude@gmail.com> 1583306810 +0800
committer Lewis <fangniude@gmail.com> 1583306810 +0800

third commit

# 第3次提交的parent,是第1次提交,没毛病
➜ tmp git:(dev) git cat-file -p 528022a97b413fae3f4a8cc0e308e5506a598ea3
tree fcb545d5746547a597811b7441ed8eba307be1ff
author Lewis <fangniude@gmail.com> 1583290125 +0800
committer Lewis <fangniude@gmail.com> 1583290125 +0800

first file

# 第3次提交的tree对象中,除了第1次提交的hello.txt外,还有一个tree对象,指向testBranch
➜ tmp git:(dev) git cat-file -p fb54f679949f2593bb83d4179f8c7d058393235c
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.txt
040000 tree ba015591b2652a669fc5892ff58023af8482e8c3 testBranch

# testBranch这个对象,包含1个blob对象,即devAdd.txt这个文件
➜ tmp git:(dev) git cat-file -p ba015591b2652a669fc5892ff58023af8482e8c3
100644 blob 101ff992f2995edf65ee71ebee38b7ba412555cf devAdd.txt

# 这个文件中的内容,是刚才输入的文字
➜ tmp git:(dev) git cat-file -p 101ff992f2995edf65ee71ebee38b7ba412555cf
this is dev file

看看现在的图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
graph LR
T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

T2[/第2次提交tree/] -->|hello.txt| F1
T2 -->|hell.copy| F1
C2[第2次提交] -->T2
C2 -->C1

T3[/testBranch的tree/] -->F2(devAdd.txt)
T4[/第3次提交tree/] -->|hello.txt| F1
T4 --> T3
C3[第3次提交] --> T4

M((master)) -.-> C2
D((dev)) -.-> C3
H((HEAD)) -.-> D

结论七:

分支就是个引用(或 指针),可以随便指向哪个对象;

在引用改变的时候,对象不用动;

每一次提交,都会生成一个新的「提交tree」;

文件夹是通过「tree对象」管理版本的,tree对象可以指向tree对象 和 blob对象

3.3 分支合并

将dev分支的,合并到master分支。

3.3.1 merge合并

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
44
45
46
47
48
49
# 切回master分支
➜ git checkout master
Switched to branch 'master'

# 从dev分支merge过来
➜ git merge dev
Merge made by the 'recursive' strategy.
testBranch/devAdd.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 testBranch/devAdd.txt

# 来看现在是怎么样的,还是从HEAD开始
➜ cat .git/HEAD
ref: refs/heads/master
# 看看master里面,指向了一个新的merge commit,它有2个parent
➜ cat .git/refs/heads/master
bb6c11e29dcc8865f9c8eae2da89750fd29bf005
➜ git cat-file -p bb6c11e29dcc8865f9c8eae2da89750fd29bf005
tree 202b85d575214f94ed494ee026e8b2cdb4fcab4f
parent 92223238a21c13bec2663fe349e8ea1dbfab97bb
parent 161645439e35c5db8f4755641ca98815c69a9d63
author Lewis <fangniude@gmail.com> 1583313790 +0800
committer Lewis <fangniude@gmail.com> 1583313790 +0800

Merge branch 'dev'

# 第1个parent是 第2次提交
➜ git cat-file -p 92223238a21c13bec2663fe349e8ea1dbfab97bb
tree 598293c40ffff2b5d26ab7f9d78601e8d8d05d94
parent 528022a97b413fae3f4a8cc0e308e5506a598ea3
author Lewis <fangniude@gmail.com> 1583295073 +0800
committer Lewis <fangniude@gmail.com> 1583295073 +0800

add hello.copy

# 第2个parent是 第3次提交
➜ git cat-file -p 161645439e35c5db8f4755641ca98815c69a9d63
tree fb54f679949f2593bb83d4179f8c7d058393235c
parent 528022a97b413fae3f4a8cc0e308e5506a598ea3
author Lewis <fangniude@gmail.com> 1583306810 +0800
committer Lewis <fangniude@gmail.com> 1583306810 +0800

third commit

# 当然,它还指向了第4次提交的树,其中有原来master 和 dev所有的文件
➜ git cat-file -p 202b85d575214f94ed494ee026e8b2cdb4fcab4f
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.copy
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.txt
040000 tree ba015591b2652a669fc5892ff58023af8482e8c3 testBranch

现在的图,是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
graph LR
T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

T2[/第2次提交tree/] -->|hello.txt| F1
T2 -->|hell.copy| F1
C2[第2次提交] -->T2
C2 -->C1

T3[/testBranch的tree/] -->F2(devAdd.txt)
T4[/第3次提交tree/] -->|hello.txt| F1
T4 --> T3
C3[第3次提交] --> T4

T5[/第4次提交tree/] -->|hello.txt|F1
T5 -->|hello.copy|F1
T5 -->T3
C4[第4次提交]-->T5
C4 --> C2
C4 --> C3

M((master)) -.-> C4
D((dev)) -.-> C3
H((HEAD)) -.-> M

3.3.2 rebase合并

思路:dev基于master合并,开发中最常用的工作模式。

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
# 需要先回滚刚才的操作
➜ git reset --hard 92223238a21c13bec2663fe349e8ea1dbfab97bb
HEAD is now at 9222323 add hello.copy

# 切换到dev分支
➜ git checkout dev
Switched to branch 'dev'

# 执行rebase
➜ git rebase master
First, rewinding head to replay your work on top of it...
Applying: third commit

# 然后,一起来看效果吧,dev有了一个新的commit
➜ cat .git/refs/heads/dev
6b4df1e31ed4c652f790841f46f30c34abb65c8c
➜ tmp git:(dev) git cat-file -p 6b4df1e31ed4c652f790841f46f30c34abb65c8c
tree 202b85d575214f94ed494ee026e8b2cdb4fcab4f
parent 92223238a21c13bec2663fe349e8ea1dbfab97bb
author Lewis <fangniude@gmail.com> 1583306810 +0800
committer Lewis <fangniude@gmail.com> 1583315190 +0800

third commit

# 这个commit的parent,是master指向的那次提交,即第2次提交
➜ tmp git:(dev) cat .git/refs/heads/master
92223238a21c13bec2663fe349e8ea1dbfab97bb
➜ tmp git:(dev) git cat-file -p 92223238a21c13bec2663fe349e8ea1dbfab97bb
tree 598293c40ffff2b5d26ab7f9d78601e8d8d05d94
parent 528022a97b413fae3f4a8cc0e308e5506a598ea3
author Lewis <fangniude@gmail.com> 1583295073 +0800
committer Lewis <fangniude@gmail.com> 1583295073 +0800

add hello.copy

# 当然,它指向的树对象,居然是刚才merge时,第4次提交的树对象,因为内容一致,直接重用了
➜ git cat-file -p 202b85d575214f94ed494ee026e8b2cdb4fcab4f
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.copy
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 hello.txt
040000 tree ba015591b2652a669fc5892ff58023af8482e8c3 testBranch

下面是现在的图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
graph LR
T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

T2[/第2次提交tree/] -->|hello.txt| F1
T2 -->|hell.copy| F1
C2[第2次提交] -->T2
C2 -->C1

T3[/testBranch的tree/] -->F2(devAdd.txt)
T5[/第5次提交tree/] -->|hello.txt| F1
T5[/第5次提交tree/] -->|hello.copy| F1
T5 --> T3
C5[第5次提交] --> T5
C5 --> C2

M((master)) -.-> C2
D((dev)) -.-> C5
H((HEAD)) -.-> D

4. 标签tag

我们给第2次提交,打个2.0标签,第5次提交打个3.0标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 当前在dev分支,即5次提交
➜ git tag -a 3.0 -m 'this is 3.0'

# 切换到master分支,即第2次提交
➜ git checkout master
Switched to branch 'master'
# 这个偷个懒,不带什么参数
➜ git tag 2.0

# 可以看到,两个tag打好了
➜ git tag --list
2.0
3.0
(END)

# 再看看refs/tags吧
➜ ls -al .git/refs/tags
total 16
drwxr-xr-x 4 lewis staff 128 3 4 18:17 .
drwxr-xr-x 4 lewis staff 128 3 4 09:53 ..
-rw-r--r-- 1 lewis staff 41 3 4 18:16 2.0
-rw-r--r-- 1 lewis staff 41 3 4 18:17 3.0

4.1 轻量的tag

我们先来看2.0这个tag。

1
2
3
4
5
6
7
8
9
10
11
12
# 和分支一样,2.0这个tag里面,也是存了一个引用
➜ cat .git/refs/tags/2.0
92223238a21c13bec2663fe349e8ea1dbfab97bb

# 指向的是第2次提交,就像现在的master一样
➜ git cat-file -p 92223238a21c13bec2663fe349e8ea1dbfab97bb
tree 598293c40ffff2b5d26ab7f9d78601e8d8d05d94
parent 528022a97b413fae3f4a8cc0e308e5506a598ea3
author Lewis <fangniude@gmail.com> 1583295073 +0800
committer Lewis <fangniude@gmail.com> 1583295073 +0800

add hello.copy

4.2 重量的tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 这个也类似
➜ cat .git/refs/tags/3.0
87c71f8bfbbb86ffae62f61f322084654932f85a

# 但它指向了一个没有见过的对象,能看到刚才输入的信息,它指向了另一个object
➜ git cat-file -p 87c71f8bfbbb86ffae62f61f322084654932f85a
object 6b4df1e31ed4c652f790841f46f30c34abb65c8c
type commit
tag 3.0
tagger Lewis <fangniude@gmail.com> 1583317040 +0800

this is 3.0

# 指向了谁呢?当然是第5次提交了
➜ git cat-file -p 6b4df1e31ed4c652f790841f46f30c34abb65c8c
tree 202b85d575214f94ed494ee026e8b2cdb4fcab4f
parent 92223238a21c13bec2663fe349e8ea1dbfab97bb
author Lewis <fangniude@gmail.com> 1583306810 +0800
committer Lewis <fangniude@gmail.com> 1583315190 +0800

third commit

4.3 有tag的图

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
graph LR

%% 第1次提交
T1[/第1次提交tree/] -->|hello.txt| F1(空文件)
C1[第1次提交] -->T1

%% 第2次提交
T2[/第2次提交tree/] -->|hello.txt| F1
T2 -->|hell.copy| F1
C2[第2次提交] -->T2
C2 -->C1

%% 第5次提交
T3[/testBranch的tree/] -->F2(devAdd.txt)
T5[/第5次提交tree/] -->|hello.txt| F1
T5[/第5次提交tree/] -->|hello.copy| F1
T5 --> T3
C5[第5次提交] --> T5
C5 --> C2

%% 分支
M((master)) -.-> C2
D((dev)) -.-> C5
H((HEAD)) -.-> M

%% tag
2((2.0)) -.->C2
TAG>Tag对象]-->C5
3((3.0)) -.->TAG

5. 时光机

6. 远程remote

7. 总结