Git内部原理之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个子文件夹heads、tags和remotes,分别对应于HEAD引用、标签引用和远程引用,下面分别讲一讲每种引用的原理。

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

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
c728ac62f0a7b5ac201fd3ed1f69165df8be31

上面的命令我们将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 8be4d8e4e8e80711dd7bae304ccfa63b35a6eb8c
object 3ac728ac62f0a7b5ac201fd3ed1f69165df8be31
type commit
tag v1.1
tagger jingsam <[email protected]> 1529481368 +0800

test tag

可以看到,上面的Git对象存储了我们填写的附注信息。

总之,普通的标签引用和附注引用同样都是存储的是40位hash值,指向一个Git对象,所不同的是普通的标签引用是直接指向提交对象,而附注标签是指向一个附注对象,附注对象再指向具体的提交对象。

另外,本质上标签引用并不是只可以指向提交对象,实际上可以指向任何Git对象,即可以给任何Git对象打标签。

远程引用
远程引用,类似于.git/refs/heads中存储的本地仓库各分支的最后一次提交,在.git/refs/remotes是用来记录多个远程仓库各分支的最后一次提交。

我们可以使用git remote来管理远程分支:

$ git remote add origin [email protected]:jingsam/git-test.git
上面添加了一个origin远程分支,接下来我们把本地仓库的master推送到远程仓库上:

$ git push origin master
Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (9/9), 720 bytes | 360.00 KiB/s, done.
Total 9 (delta 0), reused 0 (delta 0)
To github.com:jingsam/git-test.git

  • [new branch] master -> master
    这时候在.git/refs/remotes中的远程引用就会更新:
$ cat .git/refs/remotes/origin/master
3ac728ac62f0a7b5ac201fd3ed1f69165df8be31

和本地仓库的master比较一下,发现是一模一样的,表示远程分支和本地分支是同步的:

$ cat .git/refs/heads/master
3ac728ac62f0a7b5ac201fd3ed1f69165df8be31

由于远程引用也是Git引用对象,所以理论上也可以使用git update-ref来手动维护。但是,我们需要先把代码与远程仓库进行同步,在远程仓库中找到对应分支的HEAD,然后使用git update-ref进行更新,过程比较麻烦。而我们在执行git pull或git push这样的高层命令的时候,远程引用会自动更新。

总结
到这里,三种Git引用都已分析完毕。总的来说,三种Git引用都统一存储到.git/refs目录下,Git引用中的内容都是40位的hash值,指向某个Git对象,这个对象可以是任意的Git对象,可以是数据对象、树对象、提交对象。三种Git引用都可以使用git update-ref来手动维护。

三种Git引用对象所不同的是,分别存储于.git/refs/heads、.git/refs/tags、.git/refs/remotes,存储的文件夹不同,赋予了引用对象不同的功能。HEAD引用用来记录本地分支的最后一次提交,标签引用用来给任意Git对象打标签,远程引用正式用来记录远程分支的最后一次提交。

原文地址:http://blog.51cto.com/13932491/2316077

时间: 2024-12-28 16:26:19

Git内部原理之Git引用的相关文章

Git详解之九 Git内部原理

来自:http://www.open-open.com/lib/view/open1328070620202.html Git 内部原理 不管你是从前面的章节直接跳到了本章,还是读完了其余各章一直到这,你都将在本章见识 Git 的内部工作原理和实现方式.我个人发现学习这些内容对于理解 Git 的用处和强大是非常重要的,不过也有人认为这些内容对于初学者来说可能难以理解且过于复杂.正因如此我把这部分内容放在最后一章,你在学习过程中可以先阅 读这部分,也可以晚点阅读这部分,这完全取决于你自己. 既然已

Git详解之九:Git内部原理

Git 内部原理 不管你是从前面的章节直接跳到了本章,还是读完了其余各章一直到这,你都将在本章见识 Git 的内部工作原理和实现方式.我个人发现学习这些内容对于理解 Git 的用处和强大是非常重要的,不过也有人认为这些内容对于初学者来说可能难以理解且过于复杂.正因如此我把这部分内容放在最后一章,你在学习过程中可以先阅 读这部分,也可以晚点阅读这部分,这完全取决于你自己.(伯乐在线注:如果你对Git还不了解,建议从本Git系列第一篇文章开始阅读) 既然已经读到这了,就让我们开始吧.首先要弄明白一点

Git内部原理探索

目录 前言 Git分区 .git版本库里的文件/目录是干什么的 Git是如何存储文件信息的 当我们执行git add.git commit时,Git背后做了什么 Git分支的本质是什么 HEAD引用 参考 @ 前言 洞悉技术的本质,可以让我们在层出不穷的框架面前仍能泰然处之.用了那么久的 Git,不懂点内部原理,那可不行!懂点原理可以让我们遇到问题的时候能够更好更快的理清解决问题的思路.博客原文 要真正读懂本文可能需要以下基础: 有 Git 使用经验 对 Git 的三个分区有所了解 熟悉常用的

Git详解之内部原理

前言 不管你是从前面的章节直接跳到了本章,还是读完了其余各章一直到这,你都将在本章见识 Git 的内部工作原理和实现方式.我个人发现学习这些内容对于理解 Git 的用处和强大是非常重要的,不过也有人认为这些内容对于初学者来说可能难以理解且过于复杂.正因如此我把这部分内容放在最后一章,你在学习过程中可以先阅 读这部分,也可以晚点阅读这部分,这完全取决于你自己. 既然已经读到这了,就让我们开始吧.首先要弄明白一点,从根本上来讲 Git 是一套内容寻址 (content-addressable) 文件

Git详解之一 Git起步

来自:http://www.open-open.com/lib/view/open1328069609436.html 起步 本章介绍开始使用 Git 前的相关知识.我们会先了解一些版本控制工具的历史背景,然后试着让 Git 在你的系统上跑起来,直到最后配置好,可以正常开始开发工作.读完本章,你就会明白为什么 Git 会如此流行,为什么你应该立即开始使用它. 1.1 关于版本控制 什么是版本控制?我真的需要吗?版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统.在本书所展示的

Git上手:认识Git

Git 自述Git 是由伟大的电脑程序员Linus Torvalds编写的一个开源的,分布式的版本控制系统软件. Git 核心原理Git 利用底层数据结构,通过指向索引对象的可变指针,保存文件快照. PS:简单理解就是git是对象数据库,把git仓库里面文件(源码,图片,声音等文件)转化成一个对象数据,并记录在git仓库里面所有的文件增删改操作. Git 工作原理 1.git仓库下,被跟踪的文件所有操作(增删改)之后,就会对这些文件快照,然后保存到暂存区,当提交更新的时候,就会生成一条操作记录,

Git工程开发实践(二)——Git内部实现机制

Git工程开发实践(二)--Git内部实现机制 一.Git仓库内部实现简介 Git本质上是一个内容寻址(content-addressable)的文件系统,根据文件内容的SHA-1哈希值来定位文件.Git核心部分是一个简单的键值对数据库(key-value data store).向Git数据库插入任意类型的内容,会返回一个键值,通过返回的键值可以在任意时刻再次检索(retrieve)插入的内容.通过底层命令hash-object可以将任意数据保存到.git目录并返回相应的键值.Git包含一套面

Git的原理简介和常用命令

Git和SVN是我们最常用的版本控制系(Version Control System, VCS),当然,除了这二者之外还有许多其他的VCS,例如早期的CVS等.顾名思义,版本控制系统主要就是控制.协调各个版本的文档内容的一致性,这些文档包括但不限于代码文件.图片文件等等.早期SVN占据了绝大部分市场,而后来随着Git的出现,越来越多的人选择将它作为版本控制工具,社区也越来越强大.相较于SVN,最核心的区别是Git是分布式的VCS,简而言之,每一个你pull下来的Git仓库都是主仓库的一个分布式版

【git学习一】git的原理

1.背景 git是比较流行的版本管理软件,博主才疏学浅,到目前为止只用过svn和git.虽然git也用了较长时间了,但是还是没有深入学习过,这周打算阅读Progit,对git有一个深入的总结,另外把git的一些主要命令总结下,方便日后学习工作中使用. 2.git简史 读了一遍Progit第一章节,印象比较深刻的有如下几点. 1.git的底层是数据库,这样我们就大体明白git的基本原理,把项目的快照按照编码存入数据库. 2.git的最早是由linux社区的开发者开发的,膜拜大神! 3.git的主要