从.git文件夹探析git实现原理
git是一款分布式代码版本管理工具,通过git能够更加高效地协同编程。了解git的工作原理将有助于我们使用git工具更好地管理项目。通过了解.git文件夹中的文件组成,我们可以从一个角度去窥探git的实现原理。我们知道,在开始开发一个项目或加入一个项目时,需要创建一个新的仓库git init [options],或从远端克隆一个已经存在的仓库git clone [uri],除使用git init --bare创建一个“裸”仓库以外,所有创建的本地仓库都包含有一个.git文件夹,需要了解的是,“裸”仓库的内容就是.git文件夹中的内容。讨论“裸”仓库与实际仓库作用的异同不是我们现在讨论的重点,如需了解可翻阅相关文档。由此,git系统当中的的所有数据都存在于.git文件夹之中。
.git文件夹中的内容及作用
打开.git文件夹后,通常有五个文件夹:
- hooks文件夹,用于存储shell脚本,当执行某些git指令后,会触发存储在该文件夹下指定的shell脚本
- info文件夹,用于存储该项目仓库的相关信息
- logs文件夹,用于记录分支提交记录
- objects文件夹,“key-value数据库”
- refs文件夹,用于记录每个分支的最新提交结点以及tags
我们需要着重关注logs文件夹、objests文件夹以及refs文件夹,通过这三个文件夹所存储的内容来分析git。在.git文件夹中,同样存在有一些文件,譬如HEAD、config、index等文件,其中HEAD文件用以记录当前仓库指向的项目提交结点,config文件中记录着仓库的配置信息,这些文件内容不是我们要讨论的重点。
objects文件夹,git中的“key-value数据库”
在讨论objects文件夹的内容之前,我们需要明确存在于git系统中的三个实体,即“提交结点”、“节点内容”、“文件内容”。
objects可以认为是一种“key-value数据库”,之所以将数据库打引号,是因为这个“git的数据库”不具备数据库的基本功能,而仅仅具备可以通过key值能够找到与之对应的value。
提交结点实体,是整个git中的核心实体,提交节点中描述了提交节点之间的继承关系,即本次提交的内容是基于哪个或哪几个之前的提交的内容,提交结点实体之间的关系形成了一个DAG图,通过这个DAG图可以清晰地理顺整个项目的发展脉络,提交节点的内容如下:
tree <SHA1-signature>
[
parent <SHA1-signature>
...
]
author <author name> <\<author email\>> <timestamp> <time zone>
committer <committer name> <\<committer email\>> <timestamp> <time zone>
<commit message>
tree用于指向与该提交结点实体关联的节点内容实体。parent用于指向该提交节点实体所基于的之前的提交结点实体,可以看到,parent可以是多个。author用于记录本次提交的作者姓名、作者邮箱、作者所添加的内容时间以及时区。committer用于记录本次提交的提交者姓名、邮箱等内容。commit message用于记录当前提交的消息日志。
节点内容实体,用于记录本次提交时,提交中所包含的所有文件名,以及文件名所对应的key值,值得注意的是,可能由于查询性能的缘故,并非是仅记录本次提交时修改的文件,而是记录本次提交时所有的文件。另有一点值得注意的是,即便项目仓库中的文件不变,仅改变某个或某几个文件内容的前后两次提交,生成的前后两次提交节点中的tree值是不同的,换句话说,节点内容与提交节点是逻辑上的一对一关系。随着之后的讨论我们会很自然地得出这样的结论,这种一对一关系也同样是必须的,尽管在实际情况中允许两个不同的提交节点实体指向相同的节点内容实体。
文件内容实体,用于记录具体的文件内容。也就是说,在一个git仓库中,并非只有程序员们所能看到的当前项目文件夹下的代码版本,包括所有的历史代码都会在.git文件夹中有一个备份。
在objects文件夹中,三种数据实体无差别的以key-value的形式进行存储。因此一次提交操作,在objects文件夹中至少生成两个文件。存储时采用deflate算法对原始文件内容进行压缩,而key值是根据原始文件内容、文件大小等数据生成的消息摘要,在当前版本的git中,消息摘要生成算法采用SHA1算法,生成过程是将文件格式与文件长度组成头部,将文件内容作为尾部,由头部和尾部拼接后作为原文,经过SHA1算法计算之后得到该文件的160位长的SHA1签名。为防止一个文件夹内的文件数量过多,将签名每四位用字符表示十六进制数,于是得到一个长度为40的字符串,将字符串的前两个字符作为文件夹,后38个字符作为文件名进行存储。
观察仔细的同学可以发现,在三个实体的内容里,没有任何一个字段提供分支概念的信息。
logs文件夹,用于记录分支提交记录
该文件夹下的内容是一条分支下的所有提交节点实体序列。在该文件夹下,文件内容格式是单一的,即形如这样:
0000000000000000000000000000000000000000 6a0fa53d78f03abea3439b9213123d1f260f5beb author <mail> 1511776312 +0800 commit (initial): master 1
6a0fa53d78f03abea3439b9213123d1f260f5beb 75642040a2da5b324befde7ca8531b3426b32ba7 author <mail> 1511776323 +0800 commit: master 2
...
在一个分支创建时,无论这个分支是master还是基于某个提交结点创建的子分支,在logs文件中关于分支的时间线总是以全0的值为开始的。我们需要关注这样几个问题:
- master在初始化时是否会创建一个起始地提交结点?
- 分支创建时的是否会创建一个新的提交节点?
通过 git init创建一个初始化的git仓库时,master是默认创建的,在初始化的git仓库中,.git文件夹中是不存在logs文件夹的,且在objects文件夹中不包含任何key-value键值对,甚至不存在一个实际存在的master主分支,因此所谓的master初始化并非是在git仓库初始化时进行的,而是在首次提交时进行的。在测试项目中,我在以75为开头的提交结点时创建了一个子分支,查看子分支的logs文件内容:
0000000000000000000000000000000000000000 75642040a2da5b324befde7ca8531b3426b32ba7 author <mail> 1511776334 +0800 branch: Created from HEAD
...
可以看到,子分支创建时并非将主分支上分叉节点复制一下,而是从这个节点起即为一个子分支。
当合并分支时,logs的日志是如何表现,事先需要明确的是,合并分支等价于一次提交(合并分支会生成一个提交结点实体)。我们需要关注这样几个问题:
- 在子分支上已经提交过若干次,在父分支上不提交代码,当在父分支上合并子分支时,父分支的logs记录序列是怎样的
- 在子分支上提交若干次,在父分支上同样提交若干次,当在父分支上合并子分支时,父分支的logs记录序列是怎样的
关于这两个问题,我们需要观察相关的文件。第一个实验是,首先创建了一个仓库,并在master分支上提交了一次代码,之后在master分支的最新提交结点上创建了一个子分支branch,再在branch分支上连续提交了两次代码,而后切换到mster分支后,合并branch分支,logs文档的记录如下:
该文件是master分支的logs文件:
0000000000000000000000000000000000000000 79f586c23a8a169f1651411c879657406757ef92 author <mail> 1511853763 +0800 commit (initial): master 1
79f586c23a8a169f1651411c879657406757ef92 ea4fbfc8600b90555a8a4eb410a176cfbdfa48d7 author <mail> 1511853908 +0800 merge branch: Fast-forward
该文件是branch分支的logs文件
0000000000000000000000000000000000000000 79f586c23a8a169f1651411c879657406757ef92 author <mail> 1511853776 +0800 branch: Created from master
79f586c23a8a169f1651411c879657406757ef92 2dbe03d87733bbcf5b760ba3beacd61ab3f54b58 author <mail> 1511853808 +0800 commit: branch 1
2dbe03d87733bbcf5b760ba3beacd61ab3f54b58 ea4fbfc8600b90555a8a4eb410a176cfbdfa48d7 author <mail> 1511853870 +0800 commit: branch 2
可以看到,在父分支没有做改动且子分支做改动的情况下,由父分支进行合并时,是直接将父分支的最新分支节点定义为子分支上的最新分支节点,ea开头的提交节点实体的内容如下:
tree 1247c7d74e9c28fb83e8e394910346dee104fcae
parent 2dbe03d87733bbcf5b760ba3beacd61ab3f54b58
author author <mail> 1511853870 +0800
committer author <mail> 1511853870 +0800
...
第二个实验是在父分支上提交若干次切在子分支上提交若干次,在父分支上合并子分支时,logs的数据内容。首先创建一个仓库,且在master分支上提交一次代码。在该提交的代码基础上创建一个分支branch。分别在branch分支和在master分支上各自提交两次代码(无冲突),再在master分支上合并branch分支,观察两个分支下的文件内容:
该文件是master分支的logs文件:
0000000000000000000000000000000000000000 8bf95277d6c020f0ade434896355c448fd0cac00 author <mail> 1511854786 +0800 commit (initial): 1
8bf95277d6c020f0ade434896355c448fd0cac00 368ddb7d615e5eedfe5813ff2f88547dcb66b02b author <mail> 1511854819 +0800 commit: change
368ddb7d615e5eedfe5813ff2f88547dcb66b02b 3ba031189d37d816421a019a6240e9d79a683fdd author <mail> 1511854837 +0800 commit: master add 2
3ba031189d37d816421a019a6240e9d79a683fdd 12c6263013cf467f348549337813db01044046b9 author <mail> 1511854896 +0800 merge branch: Merge made by the 'recursive' strategy.
该文件是branch分支的logs文件
0000000000000000000000000000000000000000 8bf95277d6c020f0ade434896355c448fd0cac00 author <mail> 1511854795 +0800 branch: Created from master
8bf95277d6c020f0ade434896355c448fd0cac00 ba65e4cc73953f522f14a47088acf814e26ebc29 author <mail> 1511854859 +0800 commit: add 3
ba65e4cc73953f522f14a47088acf814e26ebc29 eccc7d8176edb5064cde7649ad651bb2e4fce0e3 author <mail> 1511854873 +0800 commit: add 4
可以看出,在执行merge之后,master分支的最后一次提交结点实体是两个logs文件中都从未出现过的,并且,这个以12为开头的最新提交结点实体的前一个结点实体,是父节点的次新提交结点实体。
额外关注一下以12为开头的最新提交结点实体的内容:
tree 659231fc28ddcd98c8d557e07a3fb5de4efc55b6
parent 3ba031189d37d816421a019a6240e9d79a683fdd
parent eccc7d8176edb5064cde7649ad651bb2e4fce0e3
author author <mail> 1511854896 +0800
committer author <mail> 1511854896 +0800
从.git文件夹探析git实现原理的更多相关文章
- 修改apache2配置,禁止目录访问+禁止访问.git文件夹
通过url访问服务器,无论是本地服务器还是远程服务器 如果你的文件根目录里有 index.html,index.php,浏览器就会显示 index.html的内容,如果没有 index.html,浏览 ...
- .git文件夹太大问题及解决方法
最近我们做了自动化构建, 发现文件.git文件夹越来越大, 求后端小伙伴帮忙, 小伙伴指点了一下说周末弄了一下, 忘记命令的.大致的意思就是找到git 提交了哪些大文件. 然后重构git, 先分享给小 ...
- git文件夹下项目更改ip地址小结
在我们开发的过程中,经常切换项目IP地址是很正常的,之前弄过一次,没有记住,现在简单的总结下: 找到要切换IP地址的项目,点击鼠标右键,弹出下图: 打开该项目的路径后,双击打开该项目,具体参考自己项目 ...
- 将本地文件夹添加到git仓库
1.git init; 2.git add . 3.git commit -m "初始化" 4.git remote add origin https://github.com/g ...
- Windows 文件夹修改为exe的原理和解决办法
有关文件夹后缀改为exe的病毒 该病毒之前出现过,不过没多长时间便消失了,最新的这个应该是变种,下面解决一下该病毒在移动存储设备中的问题: 该病毒并不具备能够将文件夹改为文件的能力,只是将原有文件夹全 ...
- 整理自Git文件夹下资料及man手册(不包括书籍)
$ git commit -awhich will automatically notice any modified (but not new) files, add them to the ind ...
- git 无法添加文件夹下文件
最近做项目时,发现无法提交某个子文件夹下的文件. google后发现可能是该子文件夹下有.git文件夹导致无法上传. 删除子文件夹下.git后,依然无法提交子文件夹下的文件. 继续google, 尝试 ...
- git删除文件夹
git rm 要删除的文件夹 -r -f git commit -m 'del config' git push 使用场景,删除test文件夹,本来在码云上,正常的文件夹右击会出现 ...
- Git 忽略特定文件或文件夹
在代码编译过程中,可能会生成一些目标文件或其他我们不希望提交到服务器的文件或文件夹, 但是因为是生成出来的文件/文件夹,在每次使用git status 查看状态的时候git系统总会提示这些 文件或文件 ...
随机推荐
- win10 UWP MessageDialog 和 ContentDialog
我之前开发一个软件 winMarkdown,这个软件在关闭需要提示用户还没有保存东西,需要保存,如果用户选择退出,那么把数据存放. 在Metro程序中,没有传统的窗口,当我们要用需要交互的消息提示时, ...
- UVa816,Ordering Tasks,WA
#include <iostream> #include <cstdio> #include <string> #include <cstring> # ...
- php 守护进程类
最近个人项目中需要后台运行任务,之前一直是用nouhp & + 重定向输出 来后台跑任务,后来觉得不好维护原始数据,同时可能也没有直接操作进程那么稳吧(没验证).废话少说,来看分析. 首先,我 ...
- Node.js之异步流控制
前言 在没有深度使用函数回调的经验的时候,去看这些内容还是有一点吃力的.由于Node.js独特的异步特性,才出现了"回调地狱"的问题,这篇文章中,我比较详细的记录了如何解决异步流问 ...
- 【Win 10 应用开发】在代码中加载文本资源
记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...
- url编码&&PHP大法
URL编码 Url编码通常也被称为百分号编码(Url Encoding,also known as percent-encoding),是因为它的编码方式非常简单,使用%百分号加上两位的字符--012 ...
- javascript 之作用域链-07
复习作用域 上一节我们说到作用域:是指变量可以访问的范围,他规定了如何查找变量,以及确定当前执行代码对变量的访问权限:也说到静态作用域即词法作用域,是在编译阶段决定变量的引用(由程序定义的位置决定,和 ...
- Vue源码后记-vFor列表渲染(1)
钩子函数比较简单,没有什么意思,这一节搞点大事情 => 源码中v-for的渲染过程. vue的内置指令包含了v-html.v-if.v-once.v-bind.v-on.v-show等,先从一个 ...
- IDEA 护眼色设置 背景行颜色取消等设置
首先做一些简答的记录,护眼色 等等的设置很久以前机器上已经设置过了,今天偶尔要在其他机器上重新做一些设置反而忘记了很多步骤, 设置后的HTML页面如何所示: 默认情况下,当只是设置General通用的 ...
- [板子]segTree
segTree 参考:http://www.cnblogs.com/TenosDoIt/p/3453089.html#c 初学者建议先参考上面“一步一步理解线段树”学习理论. 在这里Code分别为区间 ...