提交ID

在介绍暂存区的时候,使用过git log --pretty=oneline命令,会得到以下输出

1
2
3
4
$ git log --pretty=oneline
5782fe225003c2bc481d554ddeeab8fecd0dd19d (HEAD -> master) third change
0789463b17be77ffe8da6fbc7e4b7777d065008c which version checked in?
ac01ff39a78fa388e1e628754fd55a76feb358d2 init hellogit file

上面三行输出前面的三个40位十六进制的魔幻数,这就是提交ID,这些个数其实就是SHA1哈希值

上面这条命令仅仅是简要输出日志,使用下面的命令可以详细查看git log --pretty=raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ git log --pretty=raw
commit 5782fe225003c2bc481d554ddeeab8fecd0dd19d
tree 9c0665f333400ef615ea572bd006dca8408c0579
parent 0789463b17be77ffe8da6fbc7e4b7777d065008c
author smxknife <2323937771@qq.com> 1522171984 +0800
committer smxknife <2323937771@qq.com> 1522171984 +0800

third change

commit 0789463b17be77ffe8da6fbc7e4b7777d065008c
tree 2ac6d09bdc7a46221bb25f1d2c6446924a0bd96a
parent ac01ff39a78fa388e1e628754fd55a76feb358d2
author smxknife <2323937771@qq.com> 1522171550 +0800
committer smxknife <2323937771@qq.com> 1522171550 +0800

which version checked in?

commit ac01ff39a78fa388e1e628754fd55a76feb358d2
tree ced33498fd91fb4cfc1c05d101a9571d0fda4f50
author smxknife <2323937771@qq.com> 1522165085 +0800
committer smxknife <2323937771@qq.com> 1522165085 +0800

init hellogit file

同样显示了三次提交日志,但是每次日志丰富了很多

  • commit 本次提交的ID
  • tree 本次提交对应的目录树
  • parent 上一次提交ID,可以看一下第一个parent与第二个commit是一样的

cat-file 研究提交ID的重量级武器

每次的提交id虽然很长,但是呢在使用的时候不需要40位全部使用,只要保证不重复,写个四五六七位都可以,现在就用cat-file来研究一下这些ID到底有什么用,代表什么意思

1
2
3
4
5
6
7
8
$ git cat-file -t 5782fe
commit

$ git cat-file -t 9c0665
tree

$ git cat-file -t 078946
commit

从输出结果上看,根据这些id输出都是日志前面的类型,所以,-t的用处是显示ID对应的类型,下面再用-p(用来查看ID的内容)参数来进行查看

1
2
3
4
5
6
7
$ git cat-file -p 5782fe
tree 9c0665f333400ef615ea572bd006dca8408c0579
parent 0789463b17be77ffe8da6fbc7e4b7777d065008c
author smxknife <2323937771@qq.com> 1522171984 +0800
committer smxknife <2323937771@qq.com> 1522171984 +0800

third change

commit的ID里面存储的是上面的内容,再看一下tree的ID里面存的是什么内容

1
2
$ git cat-file -p 9c0665
100644 blob 990080f5544293a6409c0d32f06b6d98b506b0bd hellogit

这里出现新的类型:blob,看一下后面的Id的类型是否是blob

1
2
$ git cat-file -t 9900
blob

再看一下blob的内容吧

1
2
3
4
$ git cat-file -p 9900
Hello Git.
new change
third change.

熟悉亲切,这正是我们亲手写入hellogit文件中的内容,所以git上真正的文件内容是存在blob对象里。

那这些对象到底是存在哪里呢?

.git/objects目录

见名知意,objects目录正是git存放对象的目录,进去看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cd .git/objects
$ ls -al
total 0
drwxr-xr-x 13 ShaoYun staff 416 3 28 01:33 .
drwxr-xr-x 13 ShaoYun staff 416 4 2 21:27 ..
drwxr-xr-x 3 ShaoYun staff 96 3 28 01:25 07
drwxr-xr-x 3 ShaoYun staff 96 3 28 01:25 2a
drwxr-xr-x 3 ShaoYun staff 96 3 28 01:33 57
drwxr-xr-x 3 ShaoYun staff 96 3 28 01:32 99
drwxr-xr-x 3 ShaoYun staff 96 3 28 01:33 9c
drwxr-xr-x 3 ShaoYun staff 96 3 27 23:38 ac
drwxr-xr-x 3 ShaoYun staff 96 3 28 00:41 c4
drwxr-xr-x 3 ShaoYun staff 96 3 27 23:38 ce
drwxr-xr-x 3 ShaoYun staff 96 3 27 23:35 d9
drwxr-xr-x 2 ShaoYun staff 64 3 27 23:28 info
drwxr-xr-x 2 ShaoYun staff 64 3 27 23:28 pack

???这都是些什么,看不懂,不要紧,往上翻,找到blob对应的ID是990080f5544293a6409c0d32f06b6d98b506b0bd,前两位是99,目录列表中正好有99目录,进入…

1
2
3
4
5
6
$ cd 99
$ ll
total 8
drwxr-xr-x 3 ShaoYun staff 96 3 28 01:32 .
drwxr-xr-x 13 ShaoYun staff 416 3 28 01:33 ..
-r--r--r-- 1 ShaoYun staff 46 3 28 01:32 0080f5544293a6409c0d32f06b6d98b506b0bd

又出现了一串数字!!!仔细看,加上前面的99,是不是与blob对应的ID相等。觉得不靠谱,再查一个,第一个commit对应的ID,肯定一样

所以,git对象的真正存储方式我们已经找到了,每个提交ID其实就是git对象对应的存储目录和文件名,前2位是目录,后38位是文件名,对应的内容都是存在这个38位的文件里

HEAD和master有什么区别和联系

这里需要先了解一下分支命令,输入git branch

1
2
$ git branch
* master

显示分支,这里只有一个分支master,前面的*代表当前工作的分支,由此说明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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$ git log -l HEAD
commit 5782fe225003c2bc481d554ddeeab8fecd0dd19d (HEAD -> master)
Author: smxknife <2323937771@qq.com>
Date: Wed Mar 28 01:33:04 2018 +0800

third change

commit 0789463b17be77ffe8da6fbc7e4b7777d065008c
Author: smxknife <2323937771@qq.com>
Date: Wed Mar 28 01:25:50 2018 +0800

which version checked in?

commit ac01ff39a78fa388e1e628754fd55a76feb358d2
Author: smxknife <2323937771@qq.com>
Date: Tue Mar 27 23:38:05 2018 +0800

init hellogit file

$ git log -l master
commit 5782fe225003c2bc481d554ddeeab8fecd0dd19d (HEAD -> master)
Author: smxknife <2323937771@qq.com>
Date: Wed Mar 28 01:33:04 2018 +0800

third change

commit 0789463b17be77ffe8da6fbc7e4b7777d065008c
Author: smxknife <2323937771@qq.com>
Date: Wed Mar 28 01:25:50 2018 +0800

which version checked in?

commit ac01ff39a78fa388e1e628754fd55a76feb358d2
Author: smxknife <2323937771@qq.com>
Date: Tue Mar 27 23:38:05 2018 +0800

init hellogit file

$ git log -l .git/refs/heads/master
commit 5782fe225003c2bc481d554ddeeab8fecd0dd19d (HEAD -> master)
Author: smxknife <2323937771@qq.com>
Date: Wed Mar 28 01:33:04 2018 +0800

third change

commit 0789463b17be77ffe8da6fbc7e4b7777d065008c
Author: smxknife <2323937771@qq.com>
Date: Wed Mar 28 01:25:50 2018 +0800

which version checked in?

commit ac01ff39a78fa388e1e628754fd55a76feb358d2
Author: smxknife <2323937771@qq.com>
Date: Tue Mar 27 23:38:05 2018 +0800

init hellogit file

这三个命令的输出结果完全一致。
首先解释一下,git分支有以下两种表示方法

  • 正规的长格式表示法:refs/heads/master
  • 简单表示法(我自己起的名):master

那么上面三个命令的后两个一样就完全可以理解,一个东西,内容肯定是一样的

现在看HEAD和master

进入.git目录查看内容,目录下有个HEAD文件,查看一下文件内容

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

了然,HEAD其实就是指向了一个引用,而这个引用地址为master的地址,所以,三者输出完全一样,没毛病

再看一下master里面的内容

1
2
$ cat .git/refs/heads/master
5782fe225003c2bc481d554ddeeab8fecd0dd19d

熟悉的ID,与上面的日志对比一下看到,就是最新一次的commit ID,有了这个id,我们就可以追踪整个提交历史了

目录.git/refs是用来保存引用的命名空间,其中refs/heads目录下的为分支目录

为什么采用40位SHA1

40位的ID是很长,为什么不采用svn那种递增式增长序列呢?

这就涉及到git的设计思想了,git是一种分布式管理方式,这就需要一个全球唯一的ID才能做到不重复,而SHA1做到了基本上全球唯一,很难出现重复的情况,这样可以避免版本冲突的问题

rev-parse

git rev-parse master 这是git的一个底层命令,可以用来查看引用对应的提交ID,而不需要像我们之前一样要访问到分支文件下的文件查看

git rev-parse HEAD
使用HEAD代表版本库最近的一次提交

git rev-parse HEAD^加一个“\^”号,代表最新一次提交的父提交,后面可以加多个\^,如果想要找到10次提交之前的,总不能加10个\^吧,是的,这很不优雅,可以在\^后面跟数字,表示第几个父提交git rev-parse HEAD^2表示第二个父提交

还有另一种写法~<n>,如 5782~5,相当于5782^^^^^

提交所对应的树对象可以用以下语法访问

5782^{tree}