设为首页 加入收藏

TOP

Git内部原理之Git引用(一)
2018-10-28 10:11:12 】 浏览:86
Tags:Git 内部 原理 引用

这篇文章本应该在6月份就完成,拖了4个月之后,终于鼓起勇气捡起来,实在惭愧。坚持写文章就像长跑,途中跑起来基本是靠惯性,如果停下来再起跑就很累很困难。

闲话不多说,本篇继续承接前文讲一讲Git内部原理,本篇的主题是Git引用的原理。

首先来搞清楚什么是Git引用,前文讲了Git提交对象的哈希、存储原理,理论上我们只要知道该对象的hash值,就能往前推出整个提交历史,例如:

$ git log --pretty=oneline 3ac728ac62f0a7b5ac201fd3ed1f69165df8be31
3ac728ac62f0a7b5ac201fd3ed1f69165df8be31 third commit
d4d2c6cffb408d978cb6f1eb6cfc70e977378a5c second commit
db1d6f137952f2b24e3c85724ebd7528587a067a first commit

现在问题来了,提交对象的这40位hash值不好记忆,Git引用相当于给40位hash值取一个别名,便于识别和读取。Git引用对象都存储在.git/refs目录下,该目录下有3个子文件夹headstagsremotes,分别对应于HEAD引用、标签引用和远程引用,下面分别讲一讲每种引用的原理。

HEAD引用

HEAD引用是用来指向每个分支的最后一次提交对象,这样切换到一个分支之后,才能知道分支的“尾巴”在哪里。HEAD引用存储在.git/refs/heads目录下,有多少个分支,就有相应的同名HEAD引用对象。例如代码库里面有mastertest两个分支,那么.git/refs/heads目录下就存在mastertest两个文件,分别记录了分支的最后一次提交。

HEAD引用的内容就是提交对象的hash值,理论上我们可以手动地构造一个HEAD引用:

$ echo "3ac728ac62f0a7b5ac201fd3ed1f69165df8be31" > .git/refs/heads/master

Git提供了一个专有命令update-ref,用来查看和修改Git引用对象,当然也包括HEAD引用:

$ git update-ref refs/heads/master 3ac728ac62f0a7b5ac201fd3ed1f69165df8be31
$ git update-ref refs/heads/master
3ac728ac62f0a7b5ac201fd3ed1f69165df8be31

上面的命令我们将master分支的HEAD指向了3ac728ac62f0a7b5ac201fd3ed1f69165df8be31,现在用git log查看下master的提交历史,可以发现最后一次提交就是所更新的hash值:

$ git log --pretty=oneline master
3ac728ac62f0a7b5ac201fd3ed1f69165df8be31 (HEAD -> master) third commit
d4d2c6cffb408d978cb6f1eb6cfc70e977378a5c second commit
db1d6f137952f2b24e3c85724ebd7528587a067a first commit

同理,可以使用同样的方法更新test分支的HEAD:

$ git update-ref refs/heads/test d4d2c6cffb408d978cb6f1eb6cfc70e977378a5c
$ git log --pretty=oneline test
d4d2c6cffb408d978cb6f1eb6cfc70e977378a5c (test) second commit
db1d6f137952f2b24e3c85724ebd7528587a067a first commit

.git/refs/heads目录下存储了每个分支的HEAD,那怎么知道代码库当前处于哪个分支呢?这就需要一个代码库级别的HEAD引用。.git/HEAD这个文件就是整个代码库级别的HEAD引用。我们先查看一下.git/HEAD文件的内容:

$ cat .git/HEAD
ref: refs/heads/master

我们发现.git/HEAD文件的内容不是40位hash值,而像是指向.git/refs/heads/master。尝试切换到test

$ git checkout test
$ cat .git/HEAD
ref: refs/heads/test

切换分支后,.git/HEAD文件的内容也跟着指向.git/refs/heads/test.git/HEAD也是HEAD引用对象,与一般引用不同的是,它是“符号引用”。符号引用类似于文件的快捷方式,链接到要引用的对象上。

Git提供专门的命令git symbolic-ref,用来查看和更新符号引用:

$ git symbolic-ref HEAD refs/heads/master
$ git symbolic-ref HEAD refs/heads/test

至此,我们分析了两种HEAD引用,一种是分支级别的HEAD引用,用来记录各分支的最后一次提交,存储在.git/refs/heads目录下,使用git update-ref来维护;一种是代码库级别的HEAD引用,用来记录代码库所处的分支,存储在.git/HEAD文件,使用git symbolic-ref来维护。

标签引用

标签引用,顾名思义就是给Git对象打标签,便于记忆。例如,我们可以将某个提交对象打v1.0标签,表示是1.0版本。标签引用都存储在.git/refs/tags里面。

标签引用和HEAD引用本质是Git引用对象,同样使用git update-ref来查看和修改:

$ git update-ref refs/tags/v1.0 d4d2c6cffb408d978cb6f1eb6cfc70e977378a5c
$ cat .git/refs/tags/v1.0
d4d2c6cffb408d978cb6f1eb6cfc70e977378a5c

还有一种标签引用称为“附注引用”,可以为标签添加说明信息。上面的标签引用打了一个v1.0的标签表示发布1.0版本,有时候发布软件的时候除了版本号信息,还要写更新说明。附注引用就是用来实现打标签的同时,也可以附带说明信息。

附注引用是怎么实现的呢?与常规标签引用不同的是,它不直接指向提交对象,而是新建一个Git对象存储到.git/objects中,用来记录附注信息,然后附注标签指向这个Git对象。

使用git tag建立一个附注标签:

$ git tag -a v1.1 3ac728ac62f0a7b5ac201fd3ed1f69165df8be31 -m "test tag"
$ cat .git/refs/tags/v1.1
8be4d8e4e8e80711dd7bae304ccfa63b35a6eb8c

使用git cat-file来查看附注标签所指向的Git对象:

$ git cat-file -p 8be4d8e4e8e807
编程开发网
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇说说 MQ 之 Kafka(三) 下一篇说说 MQ 之 Kafka(二)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }