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”
→ 自动新建了两个文件:
两个文件分别对应两个对象:
commit(fb4495):
commit文件中包含这几个内容: 指向的tree(b54231)/author/committer/message.tree(b54231)
指向的blob(9daeaf)
完整关系(point references):
commit(红色) → tree(黄色) → blob(绿色)
对应这张图:
第二个commit:
操作: 新建一个文件bar.txt, 修改文件foo.txt的内容. add并commit之后, 具体细节见第一个commit, 就不细说了, 但注意:
- 当前commit多了一个指向上一个commit的字段: parent.
- 当前commit指向的tree指向了两个blobs, 分别为新建的bar.txt, 和修改后的foo.txt
第三个commit:
- 新建一个文件baz.txt, 输入"AHA!!!"
git add
→ 新增一个blob对象(ce9db9).- 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!