Linux中國

一些被忽略的 Git 知識

我一直在慢慢地撰寫關於 Git 工作原理的文章。儘管我曾認為自己對 Git 非常了解,但像往常一樣,當我嘗試解釋某事的時候,我又學到一些新東西。

現在回想起來,這些事情都不算太令人吃驚,但我以前並沒有清楚地思考過它們。

事實是:

  • 「索引」、「暫存區」 和 -cached 是一回事
  • 隱匿文件就是一堆提交
  • 並非所有引用都是分支或標籤
  • 合併提交不是空的

下面我們來詳細了解這些內容。

「索引」、「暫存區」 和 -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

人們通常稱這個過程為「暫存文件」或「將文件添加到暫存區」。

當你使用 git add 命令來暫存文件時,Git 在後台將文件添加到其對象資料庫(在 .git/objects 目錄下),並更新一個名為 .git/index 的文件以引用新添加的文件。

Git 中的這個「暫存區」事實上有 3 種不同的名稱,但它們都指的是同一個東西(即 .git/index 文件):

  • git diff --cached
  • git diff --staged
  • .git/index 文件

我覺得我早該早點認識到這一點,但我之前並沒有,所以在這裡提醒一下。

隱匿文件就是一堆提交

當我運行 git stash 命令來保存更改時,我一直對這些更改究竟去了哪裡感到有些困惑。事實上,當你運行 git stash 命令時,Git 會根據你的更改創建一些提交,並用一個名為 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

現在我們可以查看提交 2ff2c273 以查看其包含的內容:

$ 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 會創建兩個獨立的提交:一個是索引提交,另一個是你尚未暫存的改動提交。這讓我感到很振奮,因為我一直在開發一款工具,用於快照和恢復 Git 倉庫的狀態(也許永遠不會發布),而我提出的設計與 Git 的隱匿實現非常相似,所以我對自己的選擇感到滿意。

顯然 stash 中的舊提交存儲在 reflog 中。

並非所有引用都是分支或標籤

Git 文檔中經常泛泛地提到 「引用」,這使得我有時覺得很困惑。就個人而言,我在 Git 中處理 「引用」 的 99% 時間是指分支或 HEAD,而剩下的 1% 時間是指標籤。事實上,我以前完全不知道任何不是分支、標籤或 HEAD 的引用示例。

但現在我知道了一個例子—— 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 notes
  • refs/pull/123/headrefs/pull/123/head 用於 GitHub 拉取請求(可通過 git fetch origin refs/pull/123/merge` 獲取)
  • refs/bisect/*,來自 git bisect

合併提交不是空的

這是一個示例 Git 倉庫,其中我創建了兩個分支 xy,每個分支都有一個文件(x.txty.txt),然後將它們合併。讓我們看看合併提交。

$ git log --oneline
96a8afb (HEAD -> y) Merge branch &apos;x&apos; 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 &apos;x&apos; into y

但是,如果我單獨比較合併提交與其兩個父提交之間的差異,你會發現當然差異:

$ 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(+)

現在回想起來,合併提交並不是實際上「空的」(它們是倉庫當前狀態的快照,就像任何其他提交一樣),這一點似乎很明顯,只是我以前從未思考為什麼它們看起來為空。

顯然,這些合併差異為空的原因是合併差異只顯示衝突 —— 如果我創建一個帶有合併衝突的倉庫(一個分支在同一文件中添加了 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 &apos;x&apos; 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 知識時撰寫另一篇關於 Git 的知識的博客文章。

(題圖:MJ/03bfecc3-944e-47a0-a4fd-575293d2ba92)

via: https://jvns.ca/blog/2023/10/20/some-miscellaneous-git-facts/

作者:Julia Evans 選題:lujun9972 譯者:KaguyaQiang 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國