一些被忽略的 Git 知識
我一直在慢慢地撰寫關(guān)于 Git 工作原理的文章。
現(xiàn)在回想起來,這些事情都不算太令人吃驚,但我以前并沒有清楚地思考過它們。
事實是:
- “索引”、“暫存區(qū)” 和
-cached
是一回事 - 隱匿文件就是一堆提交
- 并非所有引用都是分支或標簽
- 合并提交不是空的
下面我們來詳細了解這些內(nèi)容。
“索引”、“暫存區(qū)” 和 -cached
是一回事
當你運行 git add file.txt
,然后運行 git status
,你會看到類似以下的輸出:
$ git add content/post/2023-10-20-some-miscellaneous-git-facts.markdown
$ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: content/post/2023-10-20-some-miscellaneous-git-facts.markdown
人們通常稱這個過程為“暫存文件”或“將文件添加到暫存區(qū)”。
當你使用 git add
命令來暫存文件時,Git 在后臺將文件添加到其對象數(shù)據(jù)庫(在 .git/objects
目錄下),并更新一個名為 .git/index
的文件以引用新添加的文件。
Git 中的這個“暫存區(qū)”事實上有 3 種不同的名稱,但它們都指的是同一個東西(即 .git/index
文件):
git diff --cached
git diff --staged
.git/index
文件
我覺得我早該早點認識到這一點,但我之前并沒有,所以在這里提醒一下。
隱匿文件就是一堆提交
當我運行 git stash
命令來保存更改時,我一直對這些更改究竟去了哪里感到有些困惑。事實上,當你運行 git stash
命令時,Git 會根據(jù)你的更改創(chuàng)建一些提交,并用一個名為 stash
的引用來標記它們(在 .git/refs/stash
目錄下)。
讓我們將此博客文章隱匿起來,然后查看 stash
引用的日志:
$ git log stash --oneline
6cb983fe (refs/stash) WIP on main: c6ee55ed wip
2ff2c273 index on main: c6ee55ed wip
... some more stuff
現(xiàn)在我們可以查看提交 2ff2c273
以查看其包含的內(nèi)容:
$ git show 2ff2c273 --stat
commit 2ff2c273357c94a0087104f776a8dd28ee467769
Author: Julia Evans <julia@jvns.ca>
Date: Fri Oct 20 14:49:20 2023 -0400
index on main: c6ee55ed wip
content/post/2023-10-20-some-miscellaneous-git-facts.markdown | 40 ++++++++++++++++++++++++++++++++++++++++
毫不意外,它包含了這篇博客文章。這很合理!
實際上,git stash
會創(chuàng)建兩個獨立的提交:一個是索引提交,另一個是你尚未暫存的改動提交。這讓我感到很振奮,因為我一直在開發(fā)一款工具,用于快照和恢復 Git 倉庫的狀態(tài)(也許永遠不會發(fā)布),而我提出的設(shè)計與 Git 的隱匿實現(xiàn)非常相似,所以我對自己的選擇感到滿意。
顯然 stash
中的舊提交存儲在 reflog 中。
并非所有引用都是分支或標簽
Git 文檔中經(jīng)常泛泛地提到 “引用”,這使得我有時覺得很困惑。就個人而言,我在 Git 中處理 “引用” 的 99% 時間是指分支或 HEAD,而剩下的 1% 時間是指標簽。事實上,我以前完全不知道任何不是分支、標簽或 HEAD
的引用示例。
但現(xiàn)在我知道了一個例子—— stash
是一種引用,而它既不是分支也不是標簽!所以這太酷啦!
以下是我博客的 Git 倉庫中的所有引用(除了 HEAD
):
$ find .git/refs -type f
.git/refs/heads/main
.git/refs/remotes/origin/HEAD
.git/refs/remotes/origin/main
.git/refs/stash
人們在本帖回復中提到的其他一些參考資料:
refs/notes/*
,來自 git notesrefs/pull/123/head
和refs/pull/123/head`` 用于 GitHub 拉取請求(可通過
git fetch origin refs/pull/123/merge` 獲?。?/li>refs/bisect/*
,來自git bisect
合并提交不是空的
這是一個示例 Git 倉庫,其中我創(chuàng)建了兩個分支 x
和 y
,每個分支都有一個文件(x.txt
和 y.txt
),然后將它們合并。讓我們看看合并提交。
$ git log --oneline
96a8afb (HEAD -> y) Merge branch 'x' into y
0931e45 y
1d8bd2d (x) x
如果我運行 git show 96a8afb
,合并提交看起來是“空的”:沒有差異!
git show 96a8afb
commit 96a8afbf776c2cebccf8ec0dba7c6c765ea5d987 (HEAD -> y)
Merge: 0931e45 1d8bd2d
Author: Julia Evans <julia@jvns.ca>
Date: Fri Oct 20 14:07:00 2023 -0400
Merge branch 'x' into y
但是,如果我單獨比較合并提交與其兩個父提交之間的差異,你會發(fā)現(xiàn)當然有差異:
$ git diff 0931e45 96a8afb --stat
x.txt | 1 +
1 file changed, 1 insertion(+)
$ git diff 1d8bd2d 96a8afb --stat
y.txt | 1 +
1 file changed, 1 insertion(+)
現(xiàn)在回想起來,合并提交并不是實際上“空的”(它們是倉庫當前狀態(tài)的快照,就像任何其他提交一樣),這一點似乎很明顯,只是我以前從未思考為什么它們看起來為空。
顯然,這些合并差異為空的原因是合并差異只顯示沖突 —— 如果我創(chuàng)建一個帶有合并沖突的倉庫(一個分支在同一文件中添加了 x
,而另一個分支添加了 y
),然后查看我解決沖突的合并提交,它看起來會像這樣:
$ git show HEAD
commit 3bfe8311afa4da867426c0bf6343420217486594
Merge: 782b3d5 ac7046d
Author: Julia Evans <julia@jvns.ca>
Date: Fri Oct 20 15:29:06 2023 -0400
Merge branch 'x' into y
diff --cc file.txt
index 975fbec,587be6b..b680253
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,1 @@@
- y
-x
++z
這似乎是在告訴我,一個分支添加了 x
,另一個分支添加了 y
,合并提交通過將 z
替代沖突解決了它。但在前面的示例中,沒有沖突,所以 Git 并未顯示任何差異。
(感謝 Jordi 告訴我合并差異的工作原理)
先這樣吧
先寫到這里吧,也許我將在學到更多 Git 知識時撰寫另一篇關(guān)于 Git 的知識的博客文章。