[英]git merge --rebase: how to find “former/previous sha1”
想象我有分支develop
和分支feature
在feature
有一個提交shaX
有人用--rebase
合並了對feature
develop
,並進行了推送,因此該要素的提交現在已經shaY
,歷史已被重寫。
目前,我只有shaY
。 但是我想從shaX
重新創建原始feature
分支。
我怎樣才能得到shaX
時,我只知道shaY
?
我可以(如果需要)接受在進行錯誤合並的開發人員的計算機上完成的解決方案,但就我所知,並且更喜歡可以在任何計算機上(如果可能)完成的解決方案,因為該開發人員可以消失或他的機器被毀
正如Mort在評論中所說 ,您不一定必須在任何計算機上執行此操作。
確實,我們必須考慮的不是機器 ,而是存儲庫 。 問題變成了:哪一組存儲庫具有X ,或者有X ,我們如何找到它? 只要我們牢記一旦找到該集合,它實際上就是單個存儲庫的列表,那么我們稱之為“誰”,“什么”或“存儲庫”之類的東西並不重要。
由於Git是分布式的,因此可能有很多存儲庫副本。 每個副本都可能略有不同。 某些副本(盡管數量可以為零 ) 將具有原始提交,而某些副本將沒有 。 您說的第一個數字可能為零的原因:
...開發人員可能會離開或他的[副本]被銷毀
如果他是唯一擁有可行副本的人,並且現在已經消失了,那么您的原始副本將減少為零,而且很不走運。 但是,由於他在自己的Git存儲庫中執行了原始操作,因此我們知道他一定在某個時候必須使用哈希X進行原始提交。 因此問題擴展到:還有誰(其他存儲庫)可能還有X? 第二,對於那些可能擁有它的人,我們如何找到它?
對第一個問題的回答始於此: 任何撿起它的人,在某個時候都有它。 如果是這樣,他們可能仍然有。 如果沒有,他們顯然沒有。
通常,大多數人選擇提交的方式是通過運行git fetch
。 (請注意, git clone
是一個包裝器,一旦創建了新的存儲庫,它將運行git fetch
,因此即使git clone
也是git fetch
一種情況。)當您運行git fetch
,您指示Git連接到另一個Git。 您的Git詢問另一個Git: 您擁有哪些分支和標簽名稱? 他們的Git向您發送一個列表,您的Git挑選出自己喜歡的名稱,並要求他們的Git發送任何可訪問的提交以及其他與這些名稱一起的對象,而您的Git尚不具備這些名稱。
一旦您的Git具有了這些對象,您的Git就將使用這些名稱並(通常)以某種方式對其進行更改,然后將這些名稱放入您自己的存儲庫中。 如果他們有一個名為feature
的分支,則您的Git會使用其名稱feature
,並將其轉換為您自己的名稱origin/feature
。 然后,您的Git將您自己的參考 refs/remotes/origin/feature
設置為哈希ID,以對其分支feature
進行尖端提交。
(對於標記名,您自己的Git通常不會做任何更改,這就是為什么標記名比分支名“更全局”的原因。)
通常,大多數運行git fetch
只會帶來一個分支-例如,如果我運行git pull origin master
(運行git fetch origin master
),我最終會指示Git連接到您的Git,詢問您的Git什么分支和標簽等您擁有的東西,但隨后只帶您的master
,然后我將其稱為我的origin/master
-或帶走所有分支。 因此,任何運行git fetch
到另一個Git並在feature
名稱下具有“良好”提交的Git並帶來了feature
,都將帶來“良好提交X ”。
這為您提供了候選庫:最初誰會選擇X ? 任何運行git fetch
(包括由git clone
和git pull
運行的git fetch
-es,盡管后者傾向於限制獲取)的人都到具有X的存儲庫,而該存儲庫具有X。
還有另一套存儲庫需要考慮,因為還有另一種獲取提交的方式: git push
可以發送提交。 git push
操作類似於git fetch
,除了角色是相反的。 我沒有讓我的Git與您的Git對話,以便我的Git可以從您的Git中獲取提交,而是讓我的Git與您的Git對話,以便我的Git可以向您的Git 提供提交。 我提供了提交對象和任何其他對象,然后我發送了一個請求: 請為此哈希ID設置您的分支或標記名稱,例如refs/heads/feature
。 使用--force
我發送命令: 將您的名稱設置為此哈希ID! 強制標志會覆蓋在接收端執行的默認“是快進”檢查(但不會在接收端強制執行任何其他檢查:仍然可以拒絕該命令)。
因此,任何通過git push
獲得良好提交的人都可以擁有它。 即使隨后的強制推送重寫了會導致良好提交X的名稱,這仍然可能是正確的,但是默認情況下,由於下一節中給出的原因,事實並非如此。
現在我們已經排好所有候選存儲庫,我們需要檢查它們。 看的地方在reflogs中 。 引用日志是Git用於保留引用發生的歷史的內容。
上面,我們一直在使用“參考”,而沒有正確定義它。 引用只是以refs/
開頭的名稱。 Git通過這些引用可以找到幾乎所有內容。 例如,分支名稱是以refs/heads/
開頭的引用,標簽名稱是以refs/tags/
開頭的引用。 每個引用存儲一個(單個)哈希ID。
每當您有Git 更新參考時(將當前的哈希ID替換為新的ID),您都可以選擇讓Git在日志條目中保存 舊值。 這些日志條目是您的引用日志。
每個引用都有一個reflog,特殊名稱HEAD
也有一個reflog。 每個reflog的長度都可能是無限的(Git會在每次引用更新時添加它的長度),因此,為了避免reflog占用無限空間,Git還會為每個reflog條目分配一個時間戳 。 Reflog條目然后最終到期。
任何給定的reflog條目的到期時間默認為30或90天。 這部分比較棘手,我沒有時間在這里寫下,所以讓我們以30天的較短時間為准。 但是,當reflog條目處於活動狀態時,它會保留早期的哈希ID,並且通過保留它們,可以將這些對象“活動”在存儲庫數據庫中。
同時,還有其他兩個棘手的部分:
git push
請求的服務器將禁用 reflog。 如果他們推了一個不好的替代品,這使他們不太可能保留良好的提交X。 feature
重命名為origin/feature
。 因此,查看是否可以找到具有良好哈希值X的提交的地方是在已運行git fetch
每個客戶端的reflog中 ,可能是針對origin/feature
的reflog中 。
在任何給定的Git存儲庫上查看reflog的命令是git reflog reference
。 (這實際上運行git log -g
因此受用戶配置git log
選項的影響。)這是我在Git的Git存儲庫副本上的git reflog origin/master
和git reflog origin/pu
的代碼段:
3dc57ebfb refs/remotes/origin/master@{2}: fetch: fast-forward
ca0964be6 refs/remotes/origin/pu@{1}: fetch: forced-update
我展示了這些內容,以說明更常規的訪3dc57ebfb
新提交而不丟棄舊提交)之間的區別: origin/master@{2}
指向提交3dc57ebfb
,並且當該更新發生時,這是常規樣式更新。 但是origin/pu@{1}
指向ca0964be6
並且當該更新發生時,它是“強制更新”。
如果有人選擇了良好的提交X ,然后feature
被重新建立基礎並強制推入origin
,並且有人選擇了新的錯誤提交鏈,則他們的origin/feature
將經歷強制更新。 這意味着您將在reflog中看到強制更新,這又意味着較早的引用有機會指向良好提交X或歷史導致良好提交X的提交。
無論它實際上直指X或X的一些后裔是偶然的事情,你必須手動檢查這些提交給找出來。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.