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