![](/img/trans.png)
[英]Using git filter-branch to remove commits by their commit message
[英]Using git filter-branch for specific commits
我正在嘗試使用git filter-branch
功能來刪除最近更新並提交的文件。 我嘗試運行以下命令:
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- 6f7fda9..HEAD
但是,這只會從master分支中刪除文件,而我希望將其從所有分支中刪除。
從將6f7fda9
提交到HEAD
開始,我希望刪除文件。 我執行的命令是否錯誤?
git filter-branch -- --all
all在所有分支上運行過濾器。 所以:
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- --all
我想從所有分支中刪除[文件]
重要的是要意識到分支幾乎(但不是完全)無關。 重要的是提交 。
您實際上無法更改任何現有的提交,並且Git不會嘗試。 git filter-branch
所做的是復制提交。 也就是說,對於每個要過濾的提交,Git都會將其提取到一個臨時工作區中,應用您的過濾器,然后從結果中進行一個新的提交。
如果新提交與原始提交逐位相同,則它將重新使用存儲庫數據庫中的實際基礎對象。 如果不是(目的是導致結果為“ not”),則將保留原始提交,而新副本將獲得新的,不同的哈希ID。 如果我們使用大寫字母代替提交哈希ID,並記住每個提交都存儲其父提交的哈希ID,則可以通過以下方式繪制原始文檔:
... <-F <-G <-H <-I <-- master
像master
這樣的分支名稱會記住上一次提交的哈希ID。 該提交會記住其父級的哈希ID,並記住另一個父級的另一個哈希ID,依此類推: master
讓Git 找到提交I
,然后找到提交H
,然后找到提交G
,依此類推。
使用git filter-branch
我們告訴Git: 提取commit F
並對其進行一些更改,然后重新提交。 如果F
沒有任何變化,我們將保留實際的哈希ID。 然后,我們將Git提取為commit G
並進行一些更改。 這次,也許我們刪除了一個敏感文件。 因此,我們進行了一個類似於G
的新提交,但有所不同:它獲得了一個新的,不同的哈希ID,我們可以將其稱為G'
。 提交G'
仍將提交F
作為其父級:
...--F--G--H--I <-- master
\
G'
然后,我們提取H
並應用過濾器。 即使沒有其他改變,我們也需要新的提交指向G'
,所以filter-branch確保了這種情況的發生,因此我們得到了指向G'
的提交H'
G'
。 我們為I
重復I
,結果是:
...--F--G--H--I <-- master
\
G'-H'-I'
最后一步是git filter-branch
重寫每個分支名稱 。 該名master
現在必須指向犯I'
,其新的和不同的哈希,不破舊惡心I
。
git filter-branch
在處理結束時重寫的名稱是您在命令行上肯定標識的所有名稱。 這部分有些棘手: git filter-branch
將適合git rev-list
字符串用作其某些參數。 這些可以是正引用(例如master
,也可以是負引用(例如^develop
或^6f7fda9
。
否定引用告訴Git: 不要為這些提交打擾 。 如果使用^6f7fda9
跳過提交6f7fda9
以及提交之前(按圖形顯示)的任何內容,則git filter-branch
不必花費任何計算機時間來執行該提交。
表達式6f7fda9..HEAD
是^6f7fda9 HEAD
簡寫,並且HEAD
表示當前分支名稱 。 因此,這是對一個分支名稱(例如master
)的正向引用 ,而對哈希ID的一個負向引用。
您可以使用--branches
來命名所有分支名稱。 您可以使用--all
來命名所有引用 (包括不是分支名稱的內容)。 Filter-branch將只重寫肯定引用,但會重寫所有引用。 請注意這一點,因為這可能會重寫refs/stash
。
當你重寫任何分支,標簽,或其他指的是一些名稱提交, 它包含你不希望有這個文件,你會得到的東西,如:
tip2 [abandoned]
/
...--good--bad--...--tip [abandoned]
\
copied--...--tip' <-- branch1
\
tip2' <-- branch2
如果您不重寫某些名稱,該名稱從bad
到下(向右)指向任何提交,則這些名稱仍將指向具有您要刪除的文件的“壞”提交。 (請記住,在我在StackOverflow上執行的這些特定圖形繪圖中,較早的/父提交位於左側,稍后的/子提交位於右側。)
所述的要求是矛盾的。 特別
我希望將其從所有分支中刪除。
和
從將6f7fda9提交到HEAD開始,我希望刪除文件。
需要和解。 我懷疑這歸結為對提交范圍的不正確理解-只是git中的一種。
考慮以下提交圖:
x -- 6f7fda9 -- A -- B -- C -- F <--(master)
\ ^(HEAD)
D -- E <--(branch)
所以HEAD
在F
master
那里; 並且有一個分支(顯然)是從A
創建A
(在6f7fda9
之后但在HEAD
之前)。
現在的問題是,給定該圖, 6f7fda9..HEAD
是什么意思? 不幸的是,答案並不是很多人憑直覺想到的。
6f7fda9..HEAD
是短期的HEAD ^6f7fda9
-意思是“一切從到達HEAD
但從不可達6f7fda9
”。 “可達到”是指“提交本身,以及通過遵循父指針找到的所有提交”。 因此,在這種情況下,它表示A
, B
, C
和F
; 但不是x
或6f7fda9
(因為它們可以從6f7fda9
到達),也不能是D
或E
(因為它們不能從HEAD
到達)。
有幾種方法可以使filter-branch
處理所有分支。 例如你可以
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- --all
但這將包括所有引用(不僅是所有分支); 如果有問題
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- --branches
另一項警告-如果您特別不希望在重寫6f7fda9
之前提交,那么您需要包括一個或多個否定提交引用。 但是,如果您確實打算包括6f7fda9
本身, 6f7fda9
排除其父項(而不是其自身)。
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch myfile' --prune-empty --tag-name-filter cat -- ^6f7fda9^ --branches
如果6f7fda9
是合並,則必須為其每個父項列出否定提交引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.