..

Git Internal(初探git的内部实现)

最近YouTube上看到Gitlab频道的一个视频: Git Internals - How Git Works - Fear Not The SHA!
感觉打开了新世界的大门🤩😍🤣. 用这篇博客记录一下感悟和思考, 希望你看完之后, 对下边这张图会有更深的理解.

第一部分: 基础概念和定义(重要!)

Basic git workflow

(p.s. git pull = git fetch + git merge)

万物皆对象

git中各种概念(e.g. branch/commit..), 其实都是对象(文件). 每个对象拥有唯一标示: SHA1.
SHA1一共有40位, 前两位作为文件夹, 一般使用前八位作为shortcut (下图红色方框中).

Git Model:

不同的branch其实指向对应的commit
然后每个commit都会指向它之前的commit

一个具体的例子(现在可能一头雾水, 但完成本文第二部分的Workshop后, 肯定会豁然开朗):

第二部分: Workshop

提示: 只有自己实践一遍才能真正领悟git的奥妙.
小工具: 监测当前目录下的所有文件: watch -n 1 -d find .
效果见下图:

第一个commit:

git add

操作: 新建文件foo.txt, 并执行git add foo.txt → 自动生成了一个文件(.git/objects/9d/aeaf…)

上图的文件(9daeaf)是什么呢?
git cat-file -p 9daeaf → 文件foo.txt的内容 blob怎么知道是哪个path?
文件名和blob的对应关系保存在 .git/index中.

git commit

提交!
执行 git commit -m “foo commit #1” → 自动新建了两个文件:

两个文件分别对应两个对象:

  1. commit(fb4495):
    commit文件中包含这几个内容: 指向的tree(b54231)/author/committer/message.

  2. tree(b54231)
    指向的blob(9daeaf)

完整关系(point references):
commit(红色) → tree(黄色) → blob(绿色)

对应这张图:

第二个commit:

操作: 新建一个文件bar.txt, 修改文件foo.txt的内容. add并commit之后, 具体细节见第一个commit, 就不细说了, 但注意:

  1. 当前commit多了一个指向上一个commit的字段: parent.
  2. 当前commit指向的tree指向了两个blobs, 分别为新建的bar.txt, 和修改后的foo.txt

第三个commit:

  1. 新建一个文件baz.txt, 输入"AHA!!!"
  2. git add → 新增一个blob对象(ce9db9).
  3. commit之后:
    新建了一个commit对象(8f1fd55) → tree(d905ab) → 三个blob(两个已经存在的blob对象 + 刚刚add新增的blob对象(ce9db9))
    这个地方就是git的巧妙之处!!! 复用之前的文件, 所以永远不会保存重复的文件
    “That’s glorious.” 视频中的原句 XD

    再举一个例子: 如果创建了一百个相同内容的文件, git add .之后, 只会创建一个blob对象.
    再对照着看下图(对blob的引用), 是不是有种豁然开朗的感觉呢?

Branch

git checkout -b foobranch → 创建了一个文件: ./.git/refs/heads/foobranch

cat .git/refs/heads/foobranch → 8f1fd5… → git cat-file -p 8f1fd5 → 第三个commit 所以branch就是一个指向commit的对象 AHA!

TODO: merge/rebase

视频最后给出的扩展阅读资料:

Reference