总第93篇
接上篇,本篇主要详细梳理一下Git
版本控制系统的内部实现原理。对待一件事物,我们在学习的过程中,一定要先知其然,再知其所以然
。
1.Git
对象
对于Git
来说,本质上它就是一个内容寻址的文件系统,并在此之上实现的一个版本控制系统。当在一个目录中执行git init
命令时,Git
会创建一个.git
目录,这个目录包含了几乎所有Git
存储和操作的内容。若想备份或复制一个版本库,只需拷贝这个目录即可。新初始化的.git
目录的典型结构如下:
随着Git
版本的不同,此目录下可能还会包含其它内容, 但是刚初始化的默认形式如上图。对各文件及文件夹的含义说明如下:
description
:仅供GitWeb
程序使用,可以不用关心;config
:包含此项目的配置信息;info
:包含全局性的排除文件,用以放置那些不希望记录在.gitignore
文件中的忽略模式;hooks
:包含服务端或客户端的钩子脚本;HEAD
:指向目前被检出的分支;index
:文件保存暂存区的信息(后面会自动创建);objects
:目录存储所有的数据内容;refs
:存储指向分支、远程仓库、标签等数据的提交对象的指针;
Git
的核心部分是一个简单的键值对数据库, 你可以向Git
仓库中插入任意类型的内容,它会返回一个唯一的键值,通过此键值可以在未来任意时刻取出插入的内容。我们可以用底层命令git hash-object
来演示这一效果:
//初始化后,objects目录中创建了pack和info两个目录,但全部为空
//用命令创建一个新的数据对象并将它手动存入git数据库中
$ echo 'this is a test object insertion' | git hash-object -w --stdin
此时,我们打开objects
目录查看,可以发现多了一个文件夹,文件夹名字为SHA-1
校验和的前两个字符,余下的38
个字符则用作文件名。
//可以用下面的指令用存储的内容取出来,校验和输入前六位即可
$ git cat-file -p c500df
//-t 参数可以让git告诉我们其内部存储的对象类型
$ git cat-file -t c500df
2.Git
引用
若你想要查看仓库某次提交(例如c500df
)前的历史,那么你可以运行git log c500df
命令来显示历史,但是你必须记住c500df
这个历史点。而.git/refs
目录下正是保存这个SHA-1
值的文件,通过访问文件的名字,我们就不用去记这个具体的值了。
若你想更新某个引用,你可以使用git update-ref refs/heads/refName
命令即可。
Git
分支的本质在于:一个指向某一系列提交之首的指针或引用。当我们用命令git branch branchName
命令时,Git
实际上会运行update-ref
命令,取得当前所在分支最新提交对应的SHA-1
值,并将其加入你想要创建的新引用中。
3.HEAD
引用与标签引用
HEAD
文件通常是一个符号引用(指向其它引用的指针),指向目前所在的分支。我们也可以用命令来设置HEAD
文件的值:
//用此命令来查看HEAD引用对应的值
$ git symbolic-ref HEAD
//用此命令设置HEAD引用的值为"refs/heads/test"
$ git symbolic-ref HEAD refs/heads/test
Git
中有四种对象类型,包括数据对象、树对象、提交对象和标签对象。
标签对象非常类似于一个提交对象,它包含一个标签的创建者信息、日期、注释和一个指针。其主要区别在于,标签对象通常指向一个提交对象,而不是一个树对象,并且永远指向同一个提交对象不会移动。
4.Git
环境变量
Git
总是在一个bash shell
中运行,并借助一些shell
环境变量来决定它的运行方式,下面列出部分非常有用的环境变量。
全局变量:
GIT_EXEC_PATH
:决定git
到哪里去找它的子程序,可以用git --exec-path
来查看当前的设置;GIT_PAGER
:控制在命令行上显示多页输出的程序,若没有设置,就会用PAGER
;GIT_EDITOR
:当用户需要编辑一些文本时,git
会启用这个编辑器,若没有设置,会启用EDITOR
;
版本库位置:
git
用了几个变量来确定它如何与当前版本库交互。
GIT_DIR
:是.git
目录的位置,若没有这个设置,git
会按照目录逐层向上查找,直到根目录;GIT_CEILING_DIRECTORIES
:控制查找.git
目录的行为;GIT_WORK_TREE
:是非空版本库的工作目录的根路径,若指定了GIT_DIR
而未指定GIT_WORK_TREE
,那么当前工作目录就会视作工作树的顶级目录;GIT_INDEX_FILE
:是索引文件的路径;GIT_OBJECT_DIRECTORY
:用来指定.git/objects
目录的位置;
路径规则:
路径规则是指你在git
中如何指定路径,包括通配符的使用。
GIT_GLOB_PATHSPECS
和GIT_NOGLOB_PATHSPECS
:用来控制通配符在路径规则中的默认行为。前者设置为1
时,通配符表现为通配符,这也是默认设置;后者设置为1
时,通配符仅匹配字符字面意思;GIT_LITERAL_PATHSPECS
:禁用上面的两种行为;GIT_ICASE_PATHSPECS
:让所有的路径规范忽略大小写;
提交:
git
提交对象的创建通常最后是由git-commit-tree
来完成的, 它使用下面这些环境变量作为信息源:
GIT_AUTHOR_NAME
:是"author"字段的名字;GIT_AUTHOR_EMAIL
:是"author"字段的邮件;GIT_AUTHOR_DATE
:是"author"字段的时间戳;GIT_COMMITTER_NAME
:是"committer"字段的名字;GIT_COMMITTER_EMAIL
:是"committer"字段的邮件;GIT_COMMITTER_DATE
:是"committer"字段的时间戳;
网络:
git
使用curl
库通过HTTP
来完成网络操作。
GIT_CURL_VERBOSE
:告诉git
显示所有由那个库产生的消息;GIT_SSLNO_VERIFY
:告诉git
不用验证SSL
证书;GIT_HTTPUSER_AGENT
:设置git
在通过HTTP
通讯时用到的user-agent
;
比较与合并:
GIT_DIFF_OPTS
:用来控制git diff
命令中显示的内容行数;GIT_EXTERNAL_DIFF
: 用来覆盖diff.external
配置的值。若设置了这个值,当执行git diff
时,git
会调用此程序;GIT_DIFFPATH_COUNTER
和GIT_DIFFPATH_TOTAL
:前者表示在一系列文件中哪个是被比较的(从1
开始),后者表示每批文件的总数;GIT_MERGE_VERBOSITY
:控制递归合并策略的输出;
调试与跟踪:
你可以用变量来跟踪git
中具体的操作过程。这些变量的可用值有两种情况:1.值为"true"、"1"、"2",它将跟踪类别写到标准错误输出中;2. 值为"以/
开头的绝对路径",它将跟踪输出写到这个文件。
GIT_TRACE
:控制常规跟踪,不适用于特殊情况;GIT_TRACEPACK_ACCESS
:控制访问打包文件的跟踪信息,第一个字段是被访问的打包文件,第二个是文件的偏移量;GIT_TRACE_PACKET
:打开网络操作包级别的跟踪信息;GIT_TRACE_PERFORMANCE
:控制性能数据的日志打印;GIT_TRACE_SETUP
:显示git
发现的关于版本库和交互环境的信息。
通过以上内容,相信你大体了解了git
在背后都做了些什么以及git
的底层实现。通过对git
底层命令和内部原理的了解,可以帮助我们对git
的操作更顺手。
本文到此结束!
如果对你有帮助,请随手 点个赞 或 点喜欢!
=======================================================
欢迎【关注作者、私信作者】。我们一起交流一起进步。
=======================================================