GIT-说点不一样的东西
我们知道git是一个分布式版本控制系统,本地目录.git/objects/
扮演着数据库(键值对数据库)的角色,里面有blob
、tree
、commit
、tag
类型,每一次提交保存的都是完整数据的一次快照。而我一直好奇的有两个点:一是这个全量数据中会记录author
和commit time
等信息吗?二是像git cherry-pick
和git rebase
这些指令可以将某一次提交的改动作为操作单元是如何实现的,按照blob
的定义,每一次commit
也是保存的全量数据呀。
存储全量数据的好处显而易见,要复现某一次历史时刻的提交commit
,只需要按图索骥,根据commit
->tree
->tree/blob
对象找到对应数据文件即可,这样不用遍历所有历史提交记录,这样提升了效率。另外git blame
是我们平时用于查看代码作者和提交时间的,那blame
的执行是否需要往前追溯历史去查找代码作者和比较时间呢,其底层具体是怎么执行的呢?git cherry-pick
指令可以将某一次提交记录使用到另一个分支上,那么各位可否想过,如果每一次提交都是保存的全量快照数据,那么git是如何实现git cherry-pick
的呢?
带着好奇心,我们一步一步来解答这些问题。 首先是我们验证下git确实没有单独保存文件各行的author和commit时间(这样做不合理,存储量也大)
author
和commit time
只存储在commit
对象中
验证很简单,找到几种类型的对象,看下具体的存储内容即可,这里图方便,就从GIT官网摘了相关数据过来(https://git-scm.com/book/en/v2/Git-Internals-Git-Objects)
blob
对象,存储具体文件数据tree
对象,存储文件目录commit
对象,存储某一次提交信息 我们重点看下commit
对象的数据
|
|
这是我所找到的所有有关author
和commit time
唯一的数据了。那么git blame
也一定是通过commit
对象拿到的信息了。
git blame
是根据比对tree
对象和parent
的差异获取到author
和commit time
的。
有两个方法可以验证,我们先看一个简单的方法,根据我们所掌握的知识点,因为author
等信息都只存储在commit
对象中,那么git blame
要获取某一行代码的作者等信息只能对比连续两次commit
中文件中代码获得
假设当前tree
对象如下:
|
|
在提交commit22222
中文件file1
如下:
|
|
在提交commit11111
中文件file1
如下:
|
|
两者对比,可知version 2
是这次提交新增的,那么这一行就可以标记为zhangsan
,至于version 1
的作者就得再继续往前追溯了。
这里啰嗦了很多,主要是想把这里的逻辑说清楚。按照我们这个逻辑,假设我们当前仓库版本只有一个提交历史记录的情况下,执行git blame
应该就只会显示最近一次提交的author
了。
- 方法一
我们可以找到一个有很多历史提交记录的仓库,使用git clone --depth=1 ....
命令拉取代码,然后执行git blame
就能发现所有的文件的author
信息都是最近一次提交者。这就验证了我们的猜想。
- 方法二
我们可以玩一点复杂的,来验证我们的猜想,这里会啰嗦一些,大体思路是这样的:
1、我们连续创建两次commit
,修改同一个文件,在同一个文件中得到两行不同提交日期的记录
2、我们修改第一次提交所指向的blob
数据,让其存储的数据变为另外一个内容,这样也就改变了diff
的内容,那么文件中的两行数据的提交author
都是相同的了。
下面我们详细来操作下:
git init .
,同时在另个窗口执行watch -n 3 tree .git/objects/
查看objects
下的变动
echo "version 1" > file1.txt; git add file1.txt; git commit -m "version 1";
可以通过
git cat-file -p 83baae
等查看存储信息
echo "version 2" >> file2.txt; git add file1.txt; git commit -m "version 2";
git blame file1.txt
可以看到两行代码的提交时间不一致
我们再创建一个
blob
对象,以下命令会往objects
文件下写入一个新的blob
对象
|
|
我们再将第一次提交的
file1
的blob
文件修改为指向我们新创建的blob
对象
|
|
git blame file1.txt
会发现显示的author
信息已经变了
|
|
这也就验证了我们的想法。
同理git cherry-pick
方法也是通过对比两次相邻commit
得到的差异部分,再将差异部分应用到当前分支上面。