簡體   English   中英

在該分支上查找 git 沒有分支名稱/標簽且提交 hash 的“分支”

[英]Find git “branch” with no branch name/label having a commit hash on that branch

有沒有辦法讓 Git 列出特定提交的子提交? 也就是說,如果我有 Git 分支:

A---B---C---D---E

我知道 C 的提交C ,有沒有辦法從C獲得D

這里更大的問題是我失去了一個分支,因為我移動了指向它的唯一分支 label。 所以我有這樣的事情:

A---B---C---D (master, moved-branch-label)
     \
      \---E---F---G---H

假設我有EF的 hash 。 如何恢復H

存在與此類似的問題 最大的區別在於 OP 不知道EFGH中的任何一個。 在這種情況下,唯一的答案是使用reflog來追溯您的步驟並手動找到H的 hash 。

但是在這里,我知道我要找的分支在哪里。 我只需要跟隨我知道的提交中的孩子。 我不敢相信在 Git 中沒有辦法做到這一點? 對於 Git 來說,這不是一個簡單的操作嗎? 給定E ,它不必知道F嗎? 看來我應該可以使用這樣的操作找到EFGH分支的結尾。

順便說一句,我很震驚地得知,如果您不提供git log以及其中一個的 Z0800FC577294C34E0B28AD2839Z39 日志,則無法獲取上述節點 E、F、G 或 H 的 Git 日志條目。 分支上缺少 label 意味着 Git 忽略該分支。 所以git log --all不會顯示這些提交。 我一直認為git log --all會從字面上顯示對存儲庫執行的所有提交。 但似乎情況並非如此。 如果有人可以反駁這一點,或者告訴我如何強制git log向我顯示那些孤立的提交,那將非常有幫助。

僅在本地搜索:

git reflog --parents | grep {HASH_OF_COMMIT_F}

僅搜索您的遠程存儲庫:

git reflog --parents --remotes | grep {HASH_OF_COMMIT_F}

在本地和遠程存儲庫上搜索:

git reflog --parents --all | grep {HASH_OF_COMMIT_F}

這些將以{COMMIT_HASH} {HASH_OF_PARENT_1} {HASH_OF_PARENT_2}. . .格式向您顯示以COMMIT_F作為父項的提交列表。 {COMMIT_HASH} {HASH_OF_PARENT_1} {HASH_OF_PARENT_2}. . . . 這將使您獲得COMMIT_F的所有直接子代,這將有助於您進行搜索。

請注意,使用了縮短的提交哈希(即前 7 個字符)

這有點附帶說明(因此應該是評論,但我需要格式化,還有更多的空間——好吧,評論中的空間多得多):

順便說一句,我很震驚地得知,如果您不提供git log以及其中一個的 Z0800FC577294C34E0B28AD2839Z39 日志,則無法獲取上述節點 E、F、G 或 H 的 Git 日志條目。 分支上缺少 label 意味着 Git 忽略該分支。 所以git log --all不會顯示這些提交。 我一直認為git log --all會從字面上顯示對存儲庫執行的所有提交。

這在其他一些版本控制系統中是有意義的,但在 Git 中則不然:

  • --all所有引用,而不是所有提交;
  • Git 通過從給定的 hash ID 開始查找提交 - 可能來自參考,或者可能只是原始 hash ID 您在命令行中列出的提交;
  • 每個提交都在零個或多個分支上。 在大多數存儲庫中,(單一的)根提交在每個分支上。

“丟棄的”提交,例如EFGH ,自然發生在 Git 中:它們是git rebase的結果,例如,在將EFGH鏈復制到一組新的和改進的提交之后。 例如,也許您希望E副本的父級是D而不是B ,並將舊的F + G擠壓在一起,以獲得:

           E'-FG-H'   <-- somebranch
          /
A--B--C--D   <-- master
    \
     E--F--G--H   ??? [was somebranch, earlier]

git reflog找到這些的原因和方式是每個ref都有一個它曾經保存的值的日志 所以在上面的例子中, somebranch的 reflog 將顯示在某一時刻,它命名為 commit E 在另一個——可能就在之后——它命名為 commit F 這將對GH重復,然后 rebase 操作將同時將名稱somebranch提交H' E'-FG-H'鏈是由git rebase使用分離的 HEAD模式構建的,因此唯一包含這些 hash ID 的 reflog 是HEAD本身的 ID,它也是一個參考。 1

請注意,“壓縮提交” FG本身是通過首先制作提交F的副本F'來構建的,然后將該副本推到一邊以構建FG ,因此我們可以很好地將上面的內容繪制為:

             F'   ???
            /
           E'-FG-H'   <-- somebranch
          /
A--B--C--D   <-- master
    \
     E--F--G--H   ??? [was somebranch, earlier]

事實上,Git 中的分支的整個概念充其量是可疑的,最壞的情況是胡說八道。 請注意在上圖中,提交A如何位於“所有分支”上,包括從現在丟棄的提交H向后工作形成的隱含分支。 我們可以隨時創建、銷毀和/或移動分支,而無需更改任何現有提交。 這些名稱只是充當標簽,指向圖表。 當名稱是分支名稱時,人們將導致並包括該名稱指向提交稱為“分支”。 如果我們添加兩個名稱,一個指向F' ,一個指向H ,提交A現在位於四個分支上。 沒有這些名字, A就在兩個分支上。 但是,如果我們對提交C進行 detached-HEAD 簽出怎么辦? 那是一個分支嗎? 如果是這樣, A就在上面。

同時,隨時隨地創建臨時對象(包括臨時提交)的想法遍及 Git; 顯示所有對象對於完成任何事情都是至關重要的,因為有這么多。 Git 的垃圾收集器git gc會在一段時間后刪除它們,如果它們真的沒有使用的話。

git gc還會刪除舊的 reflog 條目。 一個 reflog 條目有一個創建時間戳,並且在一段時間后(默認情況下為 30 天或 90 天,盡管您可以調整這兩個),reflog 條目被認為足夠陳舊以至於無趣,並被刪除。 一旦所有提及的一些內部 Git object 都被刪除,並且滿足其他幾個條件, git gc將刪除 ZA8Z96FDE6331CBD59EB662AC66 這就是為什么 Git 在各種 Git 操作之后在后台剝離git gc --auto的原因:清理剩余的垃圾。

這就是被丟棄的提交的 30 天寬限期的來源。 30 天的時間限制是某些特定 reflog 的reflogExpireUnreachable設置的結果。 90 天期限是reflogExpire設置的結果。 請注意,這兩個設置至少可能每個 reflog 有兩個值:存儲在gc. pattern .reflogExpire 如果patternname匹配,則gc. pattern .reflogExpire覆蓋存儲在gc.reflogExpire中的模式,當 ref name的 reflogs 到期時。 文檔是......在這里構成pattern的內容很少。 它也未能正確描述expireUnreachableexpire超時之間的區別:

gc.reflogExpire
gc.<pattern>.reflogExpire
git reflog expire刪除比這個時間更舊的 reflog 條目; 默認為 90 天。 值“now”立即使所有條目過期,而“never”完全禁止過期。 在中間使用“<pattern>”(例如“refs/stash”)時,該設置僅適用於與 <pattern> 匹配的 refs。

gc.reflogExpireUnreachable
gc.<pattern>.reflogExpireUnreachable
git reflog expire刪除比這個時間更早的並且從當前提示無法訪問的 reflog 條目; 默認為 30 天。 值“now”立即使所有條目過期,而“never”完全禁止過期。 中間有“<pattern>”(例如“refs/stash”),該設置僅適用於匹配<pattern> 的引用。

從當前提示短語中無法訪問意味着 Git 檢查當前存儲在ref中的實際值。 如果這標識了一個提交,該提交導致其 hash ID 存儲在reflog條目中,則 Git 選擇expire時間。 如果它在 reflog 條目中識別出一個不會導致該提交的提交,則 Git 會選擇expireUnreachable時間。 就像措辭一樣,聽起來git gc同時查看此類條目,但實際上git gc只是假設“無法訪問”的寬限期將小於或等於可訪問提交的寬限期。

正如所有這些所暗示的,可達性是 Git 的核心概念。 太多的 Git 介紹中沒有正確教授它。 有關好的解釋器,請參閱Think Like (a) Git

(我不確定<pattern>自己是如何工作的。如果沒有在 Git 源代碼中四處尋找或進行實驗,我的猜測是 Git 在這里使用 glob 樣式匹配,但即使是這樣,我們也應該想知道:是否有任何在一端或兩端隱含*** glob?也就是說, refs/stash真的是**/refs/stash/** ,還是錨定在refs和/或stash端?我從未嘗試過調整我的git gc -invoked reflog expires:默認值很好。)


1由於ref被定義為 *something 以refs/開頭,因此HEAD不能完全是 ref。 但它仍然有一個 reflog,這意味着它是一個 ref。 我們可以將其與ORIG_HEADCHERRY_PICK_HEADMERGE_HEAD等偽引用進行比較,這些引用不會獲得引用日志。 Git 文檔 HEAD中有點軟 ,呃,這里模糊了HEAD是否算作參考。

但事實上, HEAD像這樣用大寫字母寫成——是特別的。 有一種象征性的方式來引用它,使用字符@ ,這可能有助於強調它的特殊性。 不過,使用@表示HEAD最早出現在 Git 1.8.5 中,隨着時間的推移,各種故障都得到了修復。 特殊性體現在其他方面:例如, HEAD從不打包,如果保存它的文件消失,Git 不再認為存儲庫存儲庫:文件的存在是內部“是這個”中的三個標准之一Git 存儲庫”測試。 此外, HEAD現在是每個工作樹的引用,但這也適用於例如 bisect 引用。 由於添加了git worktree ,每個工作樹引用的整個概念在 Git 2.5 中是新的。 有些事情在 Git 2.7 中得到了一些糾正,並且在 Git 2.154 和 2.154 之前,影響git gc的一些討厭的每個工作樹項目都沒有修復。 出於這個原因,如果您的 Git 不是至少 2.15,我建議您關注git worktree add

請注意,分支、標簽、遠程跟蹤名稱等都是通用形式的子集。 名稱以refs/heads/開頭的 ref 是分支名稱。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM