【02】入门:Git系统的内部原理及底层命令

总第93篇

接上篇,本篇主要详细梳理一下Git版本控制系统的内部实现原理。对待一件事物,我们在学习的过程中,一定要先知其然,再知其所以然

1.Git对象

对于Git来说,本质上它就是一个内容寻址的文件系统,并在此之上实现的一个版本控制系统。当在一个目录中执行git init命令时,Git会创建一个.git目录,这个目录包含了几乎所有Git存储和操作的内容。若想备份或复制一个版本库,只需拷贝这个目录即可。新初始化的.git目录的典型结构如下:

【02】入门:Git系统的内部原理及底层命令_第1张图片

随着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 

v2-a8d9660ba90f9a63b42601c3fbee40a6_b.png

此时,我们打开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_PATHSPECSGIT_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_COUNTERGIT_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的操作更顺手。


本文到此结束!

如果对你有帮助,请随手 点个赞点喜欢

=======================================================

欢迎【关注作者、私信作者】。我们一起交流一起进步。

=======================================================

你可能感兴趣的:(python,java,linux,git,数据库)