[英]Removing all commits from a git repository containing only deleted files
多年來,我們龐大的回購協議之一已經有機增長,包含兩個項目。 現在,項目之間的分歧很大,我們決定寧願將它們放在單獨的存儲庫中。
拆分它們是沒有問題的(復制存儲庫,在存儲庫1中刪除項目A並在存儲庫2中刪除項目B),將項目移動到存儲庫根目錄而不是repo / projectX( git filter-branch --subdirectory-filter
) 。
但是,我們有8000多個提交,其中絕大多數只涉及其中一個項目,而不是兩者。 理想情況下,我們希望從repo2中清除對項目A的提交,反之亦然。
是否有腳本或工具可以執行此類操作? 從邏輯上講,這似乎很簡單:
for each commit {
if all files startwith '/projectA' delete commit
}
更改 任何提交都是不可能的。
同時,每個提交都記錄其前任( 父 )提交的哈希ID(直接:原始哈希ID)。
當您將這兩個事實放在一起時,您會發現不可能從鏈的中間刪除提交。 這是一個簡單的玩具示例,整個存儲庫中只有三個提交:
A <--B <--C <--master
在這里,名稱master
擁有第三個提交C
的ID。 這是最后的提交:這是Git開始工作的地方。 Git從提交C
讀取第二個提交B
的哈希ID。 Git從提交B
讀取第一個提交A
的哈希ID。 提交A
沒有父項,因此操作停止,我們剛剛看到了歷史記錄。
現在,您決定要刪除提交B
提交C
不能更改 ,但有一些東西,你可以這樣做:你可以提取提交C
,做一些修改,並重新提交結果作出新的承諾,我們可以稱之為D
,但我們把它叫做C'
,而不是:
A--B--C
\
C'
我們想在做C'
之前進行的更改是消除兩件事:
提交B
對源樹的影響:無論B
什么變化,我們都會以某種方式退回。
新提交C'
的父級應該是A
,而不是B
完成此過程后,我們將擁有一個新的歷史記錄,其中從未提交B
但是,名稱master
現在必須指向新的不同提交C'
:
B--C [abandoned]
/
A--C' <-- master
如果我們有一個更大的存儲庫,其中包含更多提交,並且想跳過其中兩個提交,則過程將非常相似:
B--C--D--E--F--G--H [abandoned]
/
A--C'-F'-G'-H' <-- master
請注意,廢棄的提交會在存儲庫中保留一段時間,但最終會通過Git的“垃圾收集”過程( git gc
)丟棄, 前提是您確保您從未通過例如合並提交重新連接到舊提交H
提交H'
。
任何仍具有master
指向H
現有克隆都必須被視為放射性的,以免您不小心將H
合並到H'
並把所有這些提交帶回來。
Git附帶了一個工具,使您可以執行上述操作。 但是,此工具就像瑞士軍隊的電鋸一樣,沒有防護措施可防止割傷手,腳,頭等。 它並不是特別容易使用,特別是對於您在此處設置的任務。
它所做的只是簡單地枚舉存儲庫中的可達提交 (請參閱Think Like(a)Git,並閱讀Git對圖論的使用)。 然后運行一個循環:
--commit-filter
)允許您跳過提交結果 並一路保持“原始哈希ID⟶新哈希ID”的映射,以映射父哈希ID。 您將要了解在跳過這樣的提交時“重映射到祖先”是如何工作的。
重新復制存儲庫中的每個提交都很慢。 在某些情況下,例如使用--tree-filter
從字面上提取每個提交時,它的運行速度非常慢。 結果,filter-branch有許多過濾器選項可以嘗試加快處理速度。 請記住,這些選項本質上只是優化技巧:作為打算使用filter-branch作為工具的人,您應該首先明確定義問題並提出正確的解決方案(即,如何修改源樹快照和提交)。 ),然后查看是否可以使用優化路徑(例如--index-filter
)進行快照編輯。
如果您的存儲庫中有標簽,請記住,除非提供--tag-name-filter
否則filter-branch不會重寫標簽以指向新的提交哈希。
最后,請記住,如果您選擇走這條路(即使您使用自己編寫的工具而不是使用git filter-branch
您正在做的就是復制原始提交的某些子集。 新的存儲庫不再與舊存儲庫的任何現有克隆兼容!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.