日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

我在 Mastodon 上進行了調查:

你有覺得哪些 Git 術語很讓人困惑嗎?我計劃寫篇博客,來解讀 Git 中一些奇怪的術語,如:“分離的 HEAD 狀態”,“快速前移”,“索引/暫存區/已暫存”,“比 origin/mAIn 提前 1 個提交”等等。

我收到了許多有洞見的答案,我在這里試圖概述其中的一部分。下面是這些術語的列表:

  • HEAD 和 “heads”
  • “分離的 HEAD 狀態”
  • 在合并或變基時的 “ours” 和 “theirs”
  • “你的分支已經與 'origin/main' 同步”
  • HEAD^、HEAD~、HEAD^^、HEAD~~、HEAD^2、HEAD~2
  • .. 和 ...
  • “可以快速前移”
  • “引用”、“符號引用”
  • refspecs
  • “tree-ish”
  • “索引”、“暫存的”、“已緩存的”
  • “重置”、“還原”、“恢復”
  • “未跟蹤的文件”、“追蹤遠程分支”、“跟蹤遠程分支”
  • 檢出
  • reflog
  • 合并、變基和遴選
  • rebase –onto
  • 提交
  • 更多復雜的術語

我已經盡力講解了這些術語,但它們幾乎覆蓋了 Git 的每一個主要特性,這對一篇博客而言顯然過于繁重,所以在某些地方可能會有一些粗糙。

HEAD 和 “heads”

有些人表示他們對 HEAD 和 refs/heads/main 這些術語感到困惑,因為聽起來像是一些復雜的技術內部實現。

以下是一個快速概述:

  • “heads” 就是 “分支”。在 Git 內部,分支存儲在一個名為 .git/refs/heads 的目錄中。(從技術上講,官方 Git 術語表 中明確表示分支是所有的提交,而 head 只是最近的提交,但這只是同一事物的兩種不同思考方式)
  • HEAD 是當前的分支,它被存儲在 .git/HEAD 中。

我認為,“head 是一個分支,HEAD 是當前的分支” 或許是 Git 中最奇怪的術語選擇,但已經設定好了,想要更清晰的命名方案已經為時已晚,我們繼續。

“HEAD 是當前的分支” 有一些重要的例外情況,我們將在下面討論。

“分離的 HEAD 狀態”

你可能已經看到過這條信息:

$ git checkout v0.1
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
[...]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

(消息譯文:你處于 “分離 HEAD” 的狀態。你可以四處看看,進行試驗性的更改并提交,你可以通過切換回一個分支來丟棄這個狀態下做出的任何提交。)

這條信息的實質是:

  • 在 Git 中,通常你有一個已經檢出的 “當前分支”,例如 main。
  • 存放當前分支的地方被稱為 HEAD。
  • 你做出的任何新提交都會被添加到你的當前分支,如果你運行 git merge other_branch,這也會影響你的當前分支。
  • 但是,HEAD 不一定必須是一個分支!它也可以是一個提交 ID。
  • Git 會稱這種狀態(HEAD 是提交 ID 而不是分支)為 “分離的 HEAD 狀態”
  • 例如,你可以通過檢出一個標簽來進入分離的 HEAD 狀態,因為標簽不是分支
  • 如果你沒有當前分支,一系列事情就斷鏈了:
  • git pull 根本就無法工作(因為它的全部目的就是更新你的當前分支)
  • 除非以特殊方式使用 git push,否則它也無法工作
  • git commit、git merge、git rebase 和 git cherry-pick 仍然可以工作,但它們會留下“孤兒”提交,這些提交沒有連接到任何分支,因此找到這些提交會很困難
  • 你可以通過創建一個新的分支或切換到一個現有的分支來退出分離的 HEAD 狀態

在合并或變基中的 “ours” 和 “theirs”

遇到合并沖突時,你可以運行 git checkout --ours file.txt 來選擇 “ours” 版本中的 file.txt。但問題是,什么是 “ours”,什么是 “theirs” 呢?

我總感覺此類術語混淆不清,也因此從未用過 git checkout --ours,但我還是查找相關資料試圖理清。

在合并的過程中,這是如何運作的:當前分支是 “ours”,你要合并進來的分支是 “theirs”,這樣看來似乎很合理。

$ git checkout merge-into-ours # 當前分支是 “ours”
$ git merge from-theirs # 我們正要合并的分支是 “theirs”
  • 1.
  • 2.

而在變基的過程中就剛好相反 —— 當前分支是 “theirs”,我們正在變基到的目標分支是 “ours”,如下:

$ git checkout theirs # 當前分支是 “theirs”
$ git rebase ours # 我們正在變基到的目標分支是 “ours”
  • 1.
  • 2.

我以為之所以會如此,因為在操作過程中,git rebase main 其實是將當前分支合并到 main (它類似于 git checkout main; git merge current_branch),盡管如此我仍然覺得此類術語會造成混淆。

這個精巧的小網站 對 “ours” 和 “theirs” 的術語進行了解釋。

人們也提到,VSCode 將 “ours”/“theirs” 稱作 “當前的更改”/“收到的更改”,同樣會引起混淆。

“你的分支已經與 origin/main 同步”

此信息貌似很直白 —— 你的 main 分支已經與源端同步!

但它實際上有些誤導??赡軙屇阋詾檫@意味著你的 main 分支已經是最新的,其實不然。它真正的含義是 —— 如果你最后一次運行 git fetch 或 git pull 是五天前,那么你的 main 分支就是與五天前的所有更改同步。

因此,如果你沒有意識到這一點,它對你的安全感其實是一種誤導。

我認為 Git 理論上可以給出一個更有用的信息,像是“與五天前上一次獲取的源端 main 是同步的”,因為最新一次獲取的時間是在 reflog 中記錄的,但它沒有這么做。

HEAD^HEAD~、HEAD^^HEAD~~、HEAD^2HEAD~2

我早就清楚 HEAD^ 代表前一次提交,但我很長一段時間都困惑于 HEAD~ 和 HEAD^ 之間的區別。

我查詢資料,得到了如下的對應關系:

  • HEAD^ 和 HEAD~ 是同一件事情(指向前 1 個提交)
  • HEAD^^^、HEAD~~~ 和 HEAD~3 是同一件事情(指向前 3 個提交)
  • HEAD^3 指向提交的第三個父提交,它與 HEAD~3 是不同的

這看起來有些奇怪,為什么 HEAD~ 和 HEAD^ 是同一個概念?以及,“第三個父提交”是什么?難道就是父提交的父提交的父提交?(劇透:并非如此)讓我們一起深入探討一下!

大部分提交只有一個父提交。但是合并提交有多個父提交 - 因為它們合并了兩個或更多的提交。在 Git 中,HEAD^ 意味著 “HEAD 提交的父提交”。但是如果 HEAD 是一個合并提交,那 HEAD^ 又代表怎么回事呢?

答案是,HEAD^ 指向的是合并提交的第一個父提交,HEAD^2 是第二個父提交,HEAD^3 是第三個父提交,等等。

但我猜他們也需要一個方式來表示“前三個提交”,所以 HEAD^3 是當前提交的第三個父提交(如果當前提交是一個合并提交,可能會有很多父提交),而 HEAD~3 是父提交的父提交的父提交。

我想,從我們之前對合并提交 “ours”/“theirs” 的討論來看,HEAD^ 是 “ours”,HEAD^2 是 “theirs”。

.. 和 ...

這是兩個命令:

  • git log main..test
  • git log main...test

我從沒用過 .. 和 ... 這兩個命令,所以我得查一下 man git-range-diff。我的理解是比如這樣一個情況:

A - B main
  
    C - D test
  • 1.
  • 2.
  • 3.
  • main..test 對應的是提交 C 和 D
  • test..main 對應的是提交 B
  • main...test 對應的是提交 B,C,和 D

更有挑戰的是,git diff 顯然也支持 .. 和 ...,但它們在 git log 中的意思完全不同?我的理解如下:

  • git log test..main 顯示在 main 而不在 test 的更改,但是 git log test...main 則會顯示 兩邊
  • git diff test..main 顯示 test 變動 和 main 變動(它比較 B 和 D),而 git diff test...main 會比較 A 和 D(它只會給你顯示一邊的差異)。

有關這個的更多討論可以參考 這篇博客文章。

“可以快速前移”

在 git status 中,我們會經常遇到如下的信息:

$ git status
On branch main
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
  • 1.
  • 2.
  • 3.
  • 4.

(消息譯文:你現在處于 main 分支上。你的分支比 origin/main 分支落后了 2 個提交,可以進行快速前進。 (使用 git pull 命令可以更新你的本地分支))

但“快速前移” 到底是何意?本質上,它在告訴我們這兩個分支基本如下圖所示(最新的提交在右側):

main:        A - B - C
origin/main: A - B - C - D - E
  • 1.
  • 2.

或者,從另一個角度理解就是:

A - B - C - D - E (origin/main)
        |
        main
  • 1.
  • 2.
  • 3.

這里,origin/main 僅僅多出了 2 個 main 不存在的提交,因此我們可以輕松地讓 main 更新至最新 —— 我們所需要做的就是添加上那 2 個提交。事實上,這幾乎不可能出錯 —— 不存在合并沖突。快速前進式合并是個非常棒的事情!這是合并兩個分支最簡單的方式。

運行完 git pull 之后,你會得到如下狀態:

main:        A - B - C - D - E
origin/main: A - B - C - D - E
  • 1.
  • 2.

下面這個例子展示了一種不能快速前進的狀態。

A - B - C - X  (main)
        |
        - - D - E  (origin/main)
  • 1.
  • 2.
  • 3.

此時,main 分支上有一個 origin/main 分支上無的提交(X),所以無法執行快速前移。在此種情況,git status 就會如此顯示:

$ git status
Your branch and 'origin/main' have diverged,
and have 1 and 2 different commits each, respectively.
  • 1.
  • 2.
  • 3.

(你的分支和 origin/main 分支已經產生了分歧,其中各有 1 個和 2 個不同的提交。)

“引用”、“符號引用”

在使用 Git 時,“引用” 一詞可能會使人混淆。實際上,Git 中被稱為 “引用” 的實例至少有三種:

  • 分支和標簽,例如 main 和 v0.2
  • HEAD,代表當前活躍的分支
  • 諸如 HEAD^^^ 這樣的表達式,Git 會將其解析成一個提交 ID。確切說,這可能并非 “引用”,我想 Git 將其稱作 “版本參數”,但我個人并未使用過這個術語。

個人而言,“符號引用” 這個術語頗為奇特,因為我覺得我只使用過 HEAD(即當前分支)作為符號引用。而 HEAD 在 Git 中占據核心位置,多數 Git 核心命令的行為都基于 HEAD 的值,因此我不太確定將其泛化成一個概念的實際意義。

refspecs

在 .git/config 配置 Git 遠程倉庫時,你可能會看到這樣的代碼 +refs/heads/main:refs/remotes/origin/main 。

[remote "origin"]
    url = [email protected]:jvns/pandas-cookbook
    fetch = +refs/heads/main:refs/remotes/origin/main
  • 1.
  • 2.
  • 3.

我對這段代碼的含義并不十分清楚,我通常只是在使用 git clone 或 git remote add 配置遠程倉庫時采用默認配置,并沒有動機去深究或改變。

“tree-ish”

在 git checkout 的手冊頁中,我們可以看到:

git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
  • 1.

那么這里的 tree-ish 是什么意思呢?其實當你執行 git checkout THING . 時,THING 可以是以下的任一種:

  • 一個提交 ID(如 182cd3f
  • 對一個提交 ID 的引用(如 main 或 HEAD^^ 或 v0.3.2
  • 一個位于提交內的子目錄(如 main:./docs
  • 可能就這些?

對我個人來說,“提交內的目錄”這個功能我從未使用過,從我的視角看,tree-ish 可以解讀為“提交或對提交的引用”。

“索引”、“暫存”、“緩存”

這些術語都指向的是同一樣東西(文件 .git/index,當你執行 git add 時,你的變動會在這里被暫存):

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

盡管它們都是指向同一個文件,但在實際使用中,這些術語的應用方式有所不同:

  • 很顯然,--index 和 --cached 并不總是表示同一種意思。我自己從未使用 --index,所以具體細節我就不展開討論了,但是你可以在 Junio Hamano(Git 的主管維護者)的博客文章 中找到詳細解釋。
  • “索引” 會包含未跟蹤的文件(我猜可能是對性能的考慮),但你通常不會把未跟蹤的文件考慮在“暫存區”內。

“重置”、“還原”、“恢復”

許多人提到,“重置reset”、“還原revert” 和 “恢復restore” 這三個詞非常相似,易使人混淆。

我認為這部分的困惑來自以下原因:

  • git reset --hard 和 git restore . 單獨使用時,基本上達到的效果是一樣的。然而,git reset --hard COMMIT 和 git restore --source COMMIT . 相互之間是完全不同的。
  • 相應的手冊頁沒有給出特別有幫助的描述:
  • git reset: “重置當前 HEAD 到指定的狀態”
  • git revert: “還原某些現有的提交”
  • git restore: “恢復工作樹文件”

雖然這些簡短的描述為你詳細說明了哪個名詞受到了影響(“當前 HEAD”,“某些提交”,“工作樹文件”),但它們都預設了你已經知道在這種語境中,“重置”、“還原”和“恢復”的準確含義。

以下是對它們各自功能的簡要說明:

  • 重置 —— git revert COMMIT: 在你當前的分支上,創建一個新的提交,該提交是 COMMIT 的“反向”操作(如果 COMMIT 添加了 3 行,那么新的提交就會刪除這 3 行)。
  • 還原 —— git reset --hard COMMIT: 強行將當前分支回退到 COMMIT 所在的狀態,抹去自 COMMIT 以來的所有更改。這是一個高風險的操作。
  • 恢復 —— git restore --source=COMMIT PATH: 將 PATH 中的所有文件回退到 COMMIT 當時的狀態,而不擾亂其他文件或提交歷史。

“未跟蹤的文件”、“遠程跟蹤分支”、“跟蹤遠程分支”

在 Git 中,“跟蹤” 這個詞以三種相關但不同的方式使用:

  • “未跟蹤的文件Untracked files”:在 git status 命令的輸出中可以看到。這里,“未跟蹤” 意味著這些文件不受 Git 管理,不會被計入提交。
  • “遠程跟蹤分支remote tracking branch” 例如 origin/main。此處的“遠程跟蹤分支”是一個本地引用,旨在記住上次執行 git pull 或 git fetch 時,遠程 origin 上 main 分支的狀態。
  • 我們經常看到類似 “分支 foo 被設置為跟蹤 origin 上的遠程分支 bar ”這樣的提示。

即使“未跟蹤的文件”和“遠程跟蹤分支”都用到了“跟蹤”這個詞,但是它們所在的上下文完全不同,所以沒有太多混淆。但是,對于以下兩種方式的“跟蹤”使用,我覺得可能會產生些許困擾:

  • main 是一個跟蹤遠程的分支
  • origin/main 是一個遠程跟蹤分支

然而,在 Git 中,“跟蹤遠程的分支” 和 “遠程跟蹤分支” 是不同的事物,理解它們之間的區別非常關鍵!下面是對這兩者區別的一個簡單概述:

  • main 是一個分支。你可以在它上面做提交,進行合并等操作。在 .git/config 中,它通常被配置為 “追蹤” 遠程的 main 分支,這樣你就可以用 git pull 和 git push 來同步和上傳更改。
  • origin/main 則并不是一個分支,而是一個“遠程跟蹤分支”,這并不是一種真正的分支(這有些抱歉)。你不能在此基礎上做提交。只有通過運行 git pull 或 git fetch 獲取遠程 main 的最新狀態,才能更新它。

我以前沒有深入思考過這種模糊的地方,但我認為很容易看出為什么它會讓人感到困惑。

簽出

簽出做了兩個完全無關的事情:

  • git checkout BRANCH 用于切換分支
  • git checkout file.txt 用于撤銷對 file.txt 的未暫存修改

這是眾所周知的混淆點,因此 Git 實際上已經將這兩個功能分離到了 git switch 和 git restore(盡管你還是可以使用 checkout,就像我一樣,在不愿丟棄 15 年對 git checkout 肌肉記憶的情況下)。

再者,即使用了 15 年,我仍然記不住 git checkout main file.txt 用于從 main 分支恢復 file.txt 版本的命令參數。

我覺得有時你可能需要在 checkout 命令后面加上--,幫助區分哪個參數是分支名,哪個是路徑,但我并未這么使用過,也不確定何時需要這樣做。

參考日志(reflog)

有很多人把 reflog 讀作 re-flog,而不是 ref-log。由于本文已經足夠長,我這里不會深入討論參考日志,但值得注意的是:

  • 在 Git 中,“參考” 是一個泛指分支、標簽和 HEAD 的術語
  • 參考日志(“reflog”)則為你提供了一個參考歷次記錄的歷史追蹤
  • 它是從一些極端困境中拯救出來的利器,比如說你不小心刪除了重要的分支
  • 我覺得參考日志是 Git 用戶界面中最難懂的部分,我總是試圖避免使用它。

合并 vs 變基 vs 遴選

有許多人提及他們常常對于合并和變基的區別感到迷惑,并且不理解變基中的“基base”指的是什么。

我會在這里盡量簡要的進行描述,但是這些一句話的解釋最終可能并不那么明了,因為每個人使用合并和變基創建工作流程時的方式差別挺大,要真正理解合并和變基,你必須理解工作流程。此外,有圖示會更好理解。不過這個話題可能需要一篇獨立的博客文章來完整討論,所以我不打算深入這個問題。

  • 合并會創建一個新的提交,用來融合兩個分支
  • 變基則會逐個地把當前分支上的提交復制到目標分支
  • 遴選跟變基類似,但是語法完全不同(一個顯著的差異是變基是從當前分支復制提交,而遴選則會把提交復制到當前分支)

rebase --onto

在 git rebase 中,存在一個被稱為 --onto 的選項。這一直讓我感到困惑,因為 git rebase main 的核心功能就是將當前分支變基 main 運行上。那么,額外的 --onto 參數又是怎么回事呢?

我進行了一番查找,--onto 顯然解決了一個我幾乎沒有或者說從未遇到過的問題,但我還是會記錄下我對它的理解。

A - B - C (main)
      
      D - E - F - G (mybranch)
          |
          otherbranch
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

設想一下,出于某種原因,我只想把提交 F 和 G 變基到 main 上。我相信這應該是某些 Git 工作流中會經常遇到的場景。

顯然,你可以運行 git rebase --onto main otherbranch mybranch 來完成這個操作。對我來說,在這個語法中記住 3 個不同的分支名順序似乎是不可能的(三個分支名,對我來說實在太多了),但由于我從很多人那里聽說過,我想它一定有它的用途。

提交

有人提到他們對 Git 中的提交作為一詞雙義(既作為動詞也作為名詞)的用法感到困惑。

例如:

  • 動詞:“別忘了經常提交”
  • 名詞:“main 分支上最新的提交”

我覺得大多數人應該能很快適應這個雙關的用法,但是在 SQL 數據庫中的“提交”用法與 Git 是有所不同,我認為在 SQL 數據庫中,“提交”只是作為一個動詞(你使用 COMMIT 來結束一個事務),并不作為名詞。

此外,在 Git 中,你可以從以下三個不同的角度去考慮一個 Git 提交:

  1. 表示當前每個文件狀態的快照
  2. 與父提交的差異
  3. 記錄所有先前提交的歷史

這些理解都是不錯的:不同的命令在所有的這些情況下都會使用提交。例如,git show 將提交視為一個差異,git log 把提交看作是歷史,git restore 則將提交理解為一個快照。

然而,Git 的術語并無太多助于你理解一個給定的命令正在如何使用提交。

更多令人困惑的術語

以下是更多讓人覺得混淆的術語。我對許多這些術語的意思并不十分清楚。

我自己也不是很理解的東西:

  • git pickaxe (也許這是 git log -S 和 git log -G,它們用于搜索以前提交的差異?)
  • 子模塊(我知道的全部就是它們并不以我想要的方向工作)
  • Git 稀疏檢出中的 “cone mode” (沒有任何關于這個的概念,但有人提到過)

人們提及覺得混淆,但我在這篇已經 3000 字的文章中略過的東西:

  • blob、tree
  • “合并” 的方向
  • “origin”、“upstream”,“downstream”
  • push 和 pull 并不是對立面
  • fetch 和 pull 的關系(pull = fetch + merge)
  • git porcelain
  • 子樹
  • 工作樹
  • 暫存
  • “master” 或者 “main” (聽起來它在 Git 內部有特殊含義,但其實并沒有)
  • 何時需要使用 origin main(如 git push origin main)vs origin/main

人們提及感到困惑的 Github 術語:

  • “拉取請求pull request” (與 Gitlab 中的 “合并請求merge request” 相比,人們似乎認為后者更清晰)
  • “壓扁并合并” 和 “變基并合并” 的作用 (在昨天我從未聽說過 git merge --squash,我一直以為 “壓扁并合并” 是 Github 的特殊功能)

確實是 “每個 Git 術語”

我驚訝地發現,幾乎 Git 的每個其他核心特性都被至少一人提及為某種方式中的困惑。我對聽到更多我錯過的混淆的 Git 術語的例子也有興趣。

關于這個,有另一篇很棒的 2012 年的文章叫做《最困惑的 Git 術語》。它更多的討論的是 Git 術語與 CVS 和 Subversion 術語的關聯。

如果我要選出我覺得最令人困惑的 3 個 Git 術語,我現在會選:

  • head 是一個分支,HEAD 是當前分支
  • “遠程跟蹤分支” 和 “跟蹤遠程的分支” 是不同的事物
  • “索引”、“暫存的”、“已緩存的” 全部指的同一件事

就這樣了!

在寫這些的過程中,我學到了不少東西。我了解到了一些新的關于Git的事實,但更重要的是,現在我對于別人說Git的所有功能和特性都引起困惑有了更深的理解。

許多問題我之前根本沒考慮過,比如我從來沒有意識到,在討論分支時,“跟蹤”這個詞的用法是多么地特別。

另外,盡管我已經盡力做到準確無誤,但由于我涉獵到了一些我從未深入探討過的Git的角落,所以可能還是出現了一些錯誤。

分享到:
標簽:Git
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定