最近 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!