Git学习笔记 004 Git内部原理 part2 Git的引用-part文件

Git学习笔记 004 Git内部原理 part2 Git的引用-part文件

引用的基本原理

Git使用SHA-1来追踪各个版本的文件和提交。但是SHA-1是一组无序字母数字,不变记忆,所以Git提供了引用,用简短的有意义的字母或者单词来代替SHA-1。那么字母或者单词与SHA-1需要一个映射关系。这个映射关系就是引用。它们被记录在.git/refs文件夹中。

下面使用一个全新的git工程来说明引用。

$ git init

Initialized empty Git repository in F:/97_example/git/ByHand/RefTestProject/.git/

确认一下此时refs文件夹的内容

$ find .git/refs

.git/refs

.git/refs/heads

.git/refs/tags

$ find .git/refs -type f

建立一个test.txt文件,改变一些内容,提交三次。

$ git log --pretty=oneline master

169c899e7e2d1a57e809d97998e541da01ba34e9 (HEAD -> master) 3rd commit

03787b6b681ede0bdf5f0f39d170a7dfcc22f6ec 2nd commit

5d2af1e8d157e2d54aa067f109fc2058607b64c2 1st commit

现在确认一下refs的内容,发现多了一个叫做master的文件。master就是引用名。

$ find .git/refs -type f

.git/refs/heads/master

用文本编辑器打开这个文件,会看到里面是一个SHA-1(169c899e7e2d1a57e809d97998e541da01ba34e9),它就是引用值。并且指向最新的提交。我们可以手动更改这个文件,来让它指向其他提交。比如手动改为第二个提交

$ git log --pretty=oneline master

03787b6b681ede0bdf5f0f39d170a7dfcc22f6ec (HEAD -> master) 2nd commit

5d2af1e8d157e2d54aa067f109fc2058607b64c2 1st commit

这和重置(reset)的效果是一样的。当然,并不推荐手动编辑引用文件。如果想使用底层命令完成相同的操作,可以使用如下命令

$ git update-ref refs/heads/master 169c899e7e2d1a57e809d97998e541da01ba34e9

确认当前状态

$ git log --pretty=oneline master

169c899e7e2d1a57e809d97998e541da01ba34e9 (HEAD -> master) 3rd commit

03787b6b681ede0bdf5f0f39d170a7dfcc22f6ec 2nd commit

5d2af1e8d157e2d54aa067f109fc2058607b64c2 1st commit

发现master又指向第三个提交了。

如果想实现创建分支的功能,可以使用如下命令

$ git update-ref refs/heads/test 03787b6b681ede0bdf5f0f39d170a7dfcc22f6ec

这个示例实现了,以第二个提交创建一个新的分支的功能。确认refs的内容会发现多了一个test文件。文件的内容就是第二个提交的SHA-1

$ find .git/refs -type f

.git/refs/heads/master

.git/refs/heads/test

确认它的状态发现,它只包含第一和第二两个提交。因为它是基于第二个提交生成的。

$ git log --pretty=oneline test

03787b6b681ede0bdf5f0f39d170a7dfcc22f6ec (HEAD -> test) 2nd commit

5d2af1e8d157e2d54aa067f109fc2058607b64c2 1st commit

HEAD指针

之前的章节中介绍过HEAD指针,它始终指向当前分支的最新提交。它也是一个引用,隐含在.git文件夹中。接上例,我们切换到了test分支,那么此时的HEAD的内容如下:

$ cat .git/HEAD

ref: refs/heads/test

它指向了test引用,test引用又指向了test分支的最新提交。那么HEAD指针当前指向的就是test分支的最新提交。

此时如果使用commit命令,会创建一个对象,并且将HEAD指向的提交作为父提交。HEAD文件是不可见的,但是可以使用一下命令来编辑它

$ git symbolic-ref HEAD refs/heads/master

$ cat .git/HEAD

ref: refs/heads/master

结果是当前分支切换回了master,而且HEAD指向的最新提交是master的第三次提交。

标签引用

refs文件夹中出了heads文件夹还有一个tags文件夹。标签文件的内容,是设定标签时的提交的SHA-1。比如很久以前的GitTestProject示例

$ find .git/refs -type f

.git/refs/heads/master

.git/refs/heads/testing

.git/refs/remotes/gtNew/master

.git/refs/remotes/origin/HEAD

.git/refs/remotes/origin/master

.git/refs/tags/v0.9

.git/refs/tags/v1.0

最下面的两行就是标签引用。

v1.0文件中的内容是

78f82e3198a308895c0c5b3d0bed395ac1c9cbd8

确认一下当前分支状态

$ git log --pretty=oneline master

42427244030cc65299d7eba8796ae6264ecb8ed5 (HEAD -> master) add lib folder

013c1ed1006d469d59cf72b80925cba15ee051ca git log

4416a2c9d6c5c010d42a2326170c4a9ddd54cbd3 (origin/master, origin/HEAD) fix conflict

7957b99d73b8e2782aed8c327359ade6aaf7f6eb (testing) changed by testing branch

c7a8ec8a7a0941ec3301859d715dae6e9f6ab8ba changed by master branch

812b2893979d5fa0f6c615e903ae9ebe88d0e5cc made a change

6fa2268d1f6f1db13d3f233982fdd274af5c1381 (tag: v1.0) combine commit second time

c4bea312bd0676dcdf00a40bb38fb2930bc82afe (tag: v0.9) First commit

6e2fa0997c2ce0bd01c289000c70795d232676ca (gtNew/master) Initial commit

发现分支中标记为v1.0的提交的SHA-1和标签引用中的并不一样。这是因为标签引用中的SHA-1指向一个记录了标签信息的隐含文件。看一下这个文件的内容:

$ git cat-file -p 78f82e3198a308895c0c5b3d0bed395ac1c9cbd8

object 6fa2268d1f6f1db13d3f233982fdd274af5c1381

type commit

tag v1.0

tagger kutilion <kutilion@gmail.com> 1554209566 +0800

my version 1.0

文件中记录的SHA-1才是v1.0标签标记的提交的SHA-1。

远程引用

如果你添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在 refs/remotes 目录下。

还是GitTestProject示例:

$ find .git/refs -type f

.git/refs/heads/master

.git/refs/heads/testing

.git/refs/remotes/gtNew/master

.git/refs/remotes/origin/HEAD

.git/refs/remotes/origin/master

.git/refs/tags/v0.9

.git/refs/tags/v1.0

中间有三行remotes的记录,就是远程引用所生成的文件。HEAD

远程引用和分支(位于 refs/heads 目录下的引用)之间最主要的区别在于,远程引用是只读的。 虽然可以git checkout 到某个远程引用,但是 Git 并不会将 HEAD 引用指向该远程引用。因此,你不能通过commit 命令来更新远程引用。

推荐阅读