提交对象
一般我们平时有了需要提交的文件,都是2步走:add,然后commit
add操作
第一步:添加文件
//添加文件到暂存区
git add test.txt
这一步Git做了2件事:
- 将文件的内容用之前数据对象一节中提到的方法创建数据对象并保存到Git数据库中(计算SHA-1值、生成文件目录、写入压缩后的内容)
- 更新 Index文件,也就是我们平时说的 暂存区,增加或是更新指向
text.txt
文件的索引,等待后续的第二步操作
commit:创建提交记录
//提交到Git本地仓库
git commit -m "XXX"
这个步骤是创建了一个提交对象,提交对象里面就记录了提交的时间、作者、以及提交的原因等信息。
上述 git commit
命令做了以下几件事:
首先所有具体文件的数据,已经在
add
操作时用数据对象记录在Git数据库中,并且所有文件的索引都保存在暂存区中,所以commit
操作就不用再创建数据对象了如果暂存区中存在目录关系,就会先创建树对象来记录文件目录关系,这样文件数据和目录关系都有了记录
然后会再创建一个树对象,代表当前项目快照,这个树对象里面包含的就是上述信息,也就是所有要保存记录的数据
然后用这个树对象,配置中的user.name和email,以及当前的时间戳和
-m
参数后面的内容生成提交对象
我们可以用 git log
命令,查看提交的历史记录,就能拿到 commit
的 SHA-1值(也就是我们平时说的commit id)
然后用 git cat-file
命令就能查看这个提交里面的信息
git cat-file -p xxxxxxx0ab25ce7b1c6019c411e760a17205d7b0
//输出
tree 9bfb857f532d280ecd7704beb40a2ea4ba332f5a
author xxx <xxx@qq.com> 1561964726 +0800
committer xxx <xxx@qq.com> 1561964726 +0800
first commit
可以看到,commit
里面确实是有一个树的索引,这个树对象就是前面创建的顶层树对象。
此时,有个疑问:已经用暂存区的内容创建了提交对象,那暂存区的内容还在吗?
答案是:仍然存在
Git在执行commit命令时会根据暂存区创建树对象,暂存区没变,创建的树对象就是同一个,也就是不会重复创建。
最后我们看看commit对象的一个示意图
commit历史记录
上面我们用 git commit
命令创建了一个commit对象,实际上底层调用的是 commit-tree
命令。
commit-tree
命令里面需要确定2个参数:一个是需要指定一个树对象,第二个需要指定上一个提交对象(作为父提交对象)
// -p 后面的就是上一个提交对象
echo 'second commit' | git commit-tree 0155eb -p fdf4fc3
下面我们来模拟连续提交3次
echo 'version 1' > test.txt
git add .
git write-tree
//输出树对象SHA-1值
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
//创建第一个提交对象
echo 'first commit' | git commit-tree d8329fc
//输出提交对象的SHA-1值
xxxxxxxc670d30e9817fd1af481aca92f8d700c2
//然后添加新文件,创建新的树对象
echo 'version 2' > test.txt
echo 'new file' > new.txt
git add .
git write-tree
//输出新的树对象的SHA-1值
0155eb4229851634a0f03eb265b69f5a2d56f341
//接着创建新的提交对象
echo 'second commit' | git commit-tree 0155eb -p 9f17fcc
//输出新提交对象的SHA-1值
xxxxxxx9f57c7a0632084e2c9eb38e3584180e23
//先把之前的第一版的test.txt读入暂存区作为子树
git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
git write-tree
//输出新树的SHA-1值
3c4e9cd789d88d8d89c1073707c3585e41b0e614
//接着创建新的提交对象
echo 'third commit' | git commit-tree 3c4e9c -p 8565637
//输出第3次提交的SHA-1值
xxxxxxx747d9e83f3c05d10cbc68f876a8abf0d1
以上我们用 git的底层命令模拟了实际的三次 git commit操作,我们来看一下成果:
//--stat参数可以让git显示每一次提交改动的文件信息
git log --stat f32da7d
通过以上的步骤我们就从底层知道了我们每次 git add
和 git commit
的过程,同时我们知道,每一次我们 commit
的时候,都会记录上一个 commit
的 SHA-1值,这样一个个的 commit
就串起了我们的提交记录。
那么问题来了,Git是怎么知道新 commit
对象的上一个 commit
对象的呢?实际上就是上面commit-tree
命令中的第二个参数---父提交对象,每个提交对象都保存了上一个父提交对象的引用,这样就串起来一个提交历史记录了。