git原理简介

· · 个人记录

原文链接:https://mqcreaple.github.io/blog/2022/03/04/git.html,欢迎大家关注

相信大家都对git耳熟能详了。Git是一个简单易用的版本管理工具,用户可以在git中修改文件、提交commit(更新到本地仓库)、同步远程仓库等。本篇文章将主要讨论git更新文件和提交commit背后的原理。

前置知识:git基本操作,详见runoob和w3schools。

一些名词

所有的历史文件、文件目录、commmit记录等全部保存为二进制对象,统一保存在/.git/objects目录下,文件名为该文件的SHA-1哈希值且没有后缀。

└── objects
    ├── 41
    │   └── a1d4060cf09286c1cd8fe8bdab89ce26b71086
    ├── ce
    │   └── 013625030ba8dba906f756967f9e9ca394464a
    ├── dc
    │   └── a98923d43cd634f4359f8a1f897bf585100cfe
    ├── info
    └── pack

上图的文件目录中,三个object的SHA-1值分别为:41a1d4...ce0136...,和dca989...

可以使用指令:

git hash-object 文件名

计算一个文件的SHA-1哈希值。

HEAD指针和commit

HEAD是一个指针,默认指向当前分支的最新一个commit(存储了commit文件的哈希值)。每一次提交新的commit时,HEAD也会相应前移。HEAD存储在/.git/HEAD文件中。

每一个分支也有各自的head,指向当前分支的最后一个commit,各分支的head存储在/.git/refs/heads/分支名文件中。每一次执行git checkout切换分支的时候,实际上就是让全局的HEAD赋值成了另一个分支的head。

总结一下,现在讲过的/.git目录结构都有:

.git
├── HEAD          全局HEAD指针
├── objects
│   └── 二进制对象都在这里
└── refs
    └── heads
        └── 各个分支的head

除了第一个commit,以后的每个commit都会记录上一个commit的哈希值,这样就形成了一个树形结构。

当整个项目只有一个分支时,所有的commit形成一条链:

有多个分支时,则是这样的:

文件和目录

之前说过,blob对象和tree对象都存在.git/objects/目录下。Tree对象存储了其他一系列文件的哈希值,可以理解成是一个多叉树的结点,而一般文件是根节点。

暂存区/索引对应着/.git/index文件,它记录了当前暂存的所有文件和目录的哈希值。每一次执行git add指令,程序就会在/.git/objects目录中生成一个对应着该文件的blob对象,同时将这个对象的地址加到/.git/index中。

每一个commit结点指向了一个tree节点,表示某一次commit的根目录。两次commit中不变文件不会被创建新对象。

这时,如果我们添加了一个.gitignore文件并且执行git add .gitignore命令,就会创建一个新的blob对象并添加进索引。

再接下来提交commit,程序就会新建一个表示根目录的tree对象并且指向所有索引(index)中的文件和文件夹。最后再处理commit结点,将其指向上一次的commit,即完成了git提交,如图:

总结

Git的所有对象,包括文件、目录、和commit,全部存储在/.git/object/文件夹下。

Commit结点指向其上一次的commit,形成一个树形结构,每个叶节点对应一个分支的head。

全局的HEAD指针指向任意一个commit结点,通常是一个特定分支的head。

blob对应一般的文件,tree对应文件夹,同一个文件夹下所有文件和文件夹组成一个树形结构,但同一个文件有可能被不同版本的文件夹同时指向。

每次add文件时,git会生成一个新的blob对象并添加到index中。

每次进行commit时,git会生成tree对象并令其指向所有的子文件,最后让commit结点指向根目录文件夹,同时设置commit结点的上一个结点,最后完成提交。

Git的版本管理逻辑和信息竞赛里的“可持久化算法”思想很像,都是尽量避免记录过多重复的内容从而减少空间占用。

Git在文件和目录这个树形结构之上,还有一个“commit”的分支结构,这意味着它可以应对更复杂的需求,但也意味着使用者需要记忆更多的命令。

本文仅是一个简单介绍,没有涵盖诸如分支合并和tag等更复杂的功能,如果以后有时间可以更新。

参考资料

[1] Chacon, Scott and Straub, Ben. “Pro Git v2”. Git, https://git-scm.com/book/en/v2.

[2] Wiegley, John. “Git from the Bottom to Up”. GitHub, https://jwiegley.github.io/git-from-the-bottom-up/.