[英]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
假設我有E
或F
的 hash 。 如何恢復H
?
存在與此類似的問題。 最大的區別在於 OP 不知道E
、 F
、 G
或H
中的任何一個。 在這種情況下,唯一的答案是使用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
指所有引用,而不是所有提交; “丟棄的”提交,例如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
。 這將對G
和H
重復,然后 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
如果pattern
與name
匹配,則gc. pattern .reflogExpire
覆蓋存儲在gc.reflogExpire
中的模式,當 ref name
的 reflogs 到期時。 文檔是......在這里構成pattern
的內容很少。 它也未能正確描述expireUnreachable
和expire
超時之間的區別:
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_HEAD
、 CHERRY_PICK_HEAD
、 MERGE_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.