設定和配置
獲取和建立專案
基本快照
分支與合併
共享和更新專案
檢查和比較
打補丁
除錯
電子郵件
外部系統
伺服器管理
指南
管理
底層命令
- 2.44.1 → 2.50.1 無更改
-
2.44.0
2024-02-23
- 2.29.3 → 2.43.7 無更改
-
2.29.2
2020-10-29
- 2.25.2 → 2.29.1 無更改
-
2.25.1
2020-02-17
-
2.25.0
2020-01-13
- 2.24.1 → 2.24.4 無更改
-
2.24.0
2019-11-04
- 2.22.1 → 2.23.4 無更改
-
2.22.0
2019-06-07
- 2.18.1 → 2.21.4 無變更
-
2.18.0
2018-06-21
- 2.16.6 → 2.17.6 無更改
-
2.15.4
2019-12-06
-
2.14.6
2019-12-06
-
2.13.7
2018-05-22
-
2.12.5
2017-09-22
- 2.10.5 → 2.11.4 無更改
-
2.9.5
2017-07-30
-
2.8.6
2017-07-30
- 2.3.10 → 2.7.6 無更改
-
2.2.3
2015-09-04
- 2.1.4 無更改
-
2.0.5
2014-12-17
概要
git filter-branch [--setup <command>] [--subdirectory-filter <directory>] [--env-filter <command>] [--tree-filter <command>] [--index-filter <command>] [--parent-filter <command>] [--msg-filter <command>] [--commit-filter <command>] [--tag-name-filter <command>] [--prune-empty] [--original <namespace>] [-d <directory>] [-f | --force] [--state-branch <branch>] [--] [<rev-list-options>…]
警告
git filter-branch 存在大量陷阱,可能導致預期的歷史重寫結果不明顯地遭到破壞(並且由於其糟糕的效能,留給您調查此類問題的時間也很少)。這些安全和效能問題無法向後相容地修復,因此,不建議使用它。請使用其他歷史過濾工具,例如 git filter-repo。如果您仍然需要使用 git filter-branch,請仔細閱讀 SAFETY(和 PERFORMANCE),瞭解 filter-branch 的陷阱,然後儘可能地避免其中列出的危害。
描述
允許您透過重寫 <rev-list-options> 中提到的分支來重寫 Git 修訂歷史,對每個修訂應用自定義過濾器。這些過濾器可以修改每個樹(例如,刪除檔案或對所有檔案執行 perl 重寫)或每個提交的資訊。否則,所有資訊(包括原始提交時間或合併資訊)都將保留。
該命令只會重寫命令列中提到的正向引用(例如,如果您傳遞 a..b,則只重寫 b)。如果您未指定任何過濾器,提交將不做任何更改地重新提交,這通常不會產生任何效果。儘管如此,這在將來可能有助於彌補一些 Git 錯誤等,因此允許這種用法。
注意:此命令遵守 .git/info/grafts
檔案和 refs/replace/
名稱空間中的引用。如果您定義了任何嫁接或替換引用,執行此命令將使它們永久化。
警告!重寫後的歷史將為所有物件提供不同的物件名稱,並且不會與原始分支收斂。您將無法輕鬆地在原始分支之上推送和分發重寫後的分支。如果您不瞭解其全部影響,請不要使用此命令,如果一個簡單的單次提交足以解決您的問題,請無論如何都避免使用它。(有關重寫已釋出歷史的更多資訊,請參閱 git-rebase[1] 中的“從上游 rebase 恢復”部分。)
務必驗證重寫後的版本是否正確:如果與重寫後的引用不同,原始引用將儲存在 refs/original/ 名稱空間中。
請注意,由於此操作的 I/O 成本很高,因此最好使用 -d
選項將臨時目錄重定向到磁碟之外,例如在 tmpfs 上。據報道,速度提升非常明顯。
過濾器
過濾器按照如下所示的順序列出。<command> 引數始終在 shell 上下文中透過 eval 命令進行評估(由於技術原因,提交過濾器是唯一的例外)。在此之前,$GIT_COMMIT
環境變數將被設定為包含正在重寫的提交 ID。此外,GIT_AUTHOR_NAME、GIT_AUTHOR_EMAIL、GIT_AUTHOR_DATE、GIT_COMMITTER_NAME、GIT_COMMITTER_EMAIL 和 GIT_COMMITTER_DATE 會從當前提交中獲取並匯出到環境中,以影響過濾器執行後由 git-commit-tree[1] 建立的替換提交的作者和提交者身份。
如果 <command> 的任何評估返回非零退出狀態,則整個操作將中止。
提供了一個 map 函式,它接受一個“原始 sha1 ID”引數,如果提交已被重寫,則輸出一個“重寫後的 sha1 ID”,否則輸出“原始 sha1 ID”;如果您的提交過濾器發出了多個提交,map 函式可以在單獨的行上返回多個 ID。
選項
- --setup <command>
-
這不是針對每個提交執行的實際過濾器,而是在迴圈之前進行的一次性設定。因此,尚未定義任何提交特定的變數。此處定義的功能或變數可以在後續的過濾器步驟中使用或修改,但提交過濾器除外,這是出於技術原因。
- --subdirectory-filter <directory>
-
僅檢視涉及給定子目錄的歷史。結果將只包含該目錄作為其專案根目錄。暗示 重對映到祖先。
- --env-filter <command>
-
如果您只需要修改執行提交的環境,則可以使用此過濾器。具體來說,您可能希望重寫作者/提交者姓名/電子郵件/時間環境變數(有關詳細資訊,請參閱 git-commit-tree[1])。
- --tree-filter <command>
-
這是用於重寫樹及其內容的過濾器。引數在 shell 中評估,工作目錄設定為已檢出樹的根目錄。然後新樹將按原樣使用(新檔案自動新增,消失的檔案自動刪除——.gitignore 檔案和任何其他忽略規則都無效!)。
- --index-filter <command>
-
這是用於重寫索引的過濾器。它類似於樹過濾器,但不會檢出樹,這使其速度更快。常與
git
rm
--cached
--ignore-unmatch
... 一起使用,詳見下面的示例。對於複雜情況,請參閱 git-update-index[1]。 - --parent-filter <command>
-
這是用於重寫提交父列表的過濾器。它將從標準輸入接收父字串,並應將新的父字串輸出到標準輸出。父字串的格式在 git-commit-tree[1] 中描述:對於初始提交為空,對於普通提交為 "-p parent",對於合併提交為 "-p parent1 -p parent2 -p parent3 …"。
- --msg-filter <command>
-
這是用於重寫提交訊息的過濾器。引數在 shell 中評估,原始提交訊息在標準輸入上;其標準輸出用作新的提交訊息。
- --commit-filter <command>
-
這是執行提交的過濾器。如果指定了此過濾器,它將代替 git commit-tree 命令被呼叫,引數形式為 "<TREE_ID> [(-p <PARENT_COMMIT_ID>)…]",並且日誌訊息在標準輸入上。提交 ID 期望在標準輸出上。
作為一項特殊擴充套件,提交過濾器可以發出多個提交 ID;在這種情況下,原始提交的重寫子項將全部將其作為父項。
您可以在此過濾器中使用 map 方便函式,以及其他方便函式。例如,呼叫 skip_commit "$@" 將跳過當前提交(但不會跳過其更改!如果您想這樣做,請改用 git rebase)。
如果您不希望保留只有一個父級且未對樹進行任何更改的提交,也可以使用
git_commit_non_empty_tree
"$@"
代替git
commit-tree
"$@"
。 - --tag-name-filter <command>
-
這是用於重寫標籤名稱的過濾器。當傳入時,它將對每個指向重寫物件(或指向重寫物件的標籤物件)的標籤引用呼叫。原始標籤名稱透過標準輸入傳遞,新標籤名稱期望在標準輸出上。
原始標籤不會被刪除,但可以被覆蓋;使用 "--tag-name-filter cat" 只是更新標籤。在這種情況下,要非常小心,並確保您已備份舊標籤,以防轉換出現問題。
支援幾乎正確的標籤物件重寫。如果標籤附有訊息,將建立一個具有相同訊息、作者和時間戳的新標籤物件。如果標籤附有簽名,簽名將被剝離。根據定義,無法保留簽名。之所以說“幾乎”正確,是因為理想情況下,如果標籤沒有更改(指向同一物件,具有相同名稱等),它應該保留任何簽名。事實並非如此,簽名總是會被刪除,買家自負。也不支援更改作者或時間戳(或標籤訊息)。指向其他標籤的標籤將被重寫以指向底層提交。
- --prune-empty
-
一些過濾器將生成未觸及樹的空提交。此選項指示 git-filter-branch 刪除此類提交,如果它們只有一個或零個未修剪的父級;因此,合併提交將保持不變。此選項不能與
--commit-filter
一起使用,儘管可以使用提交過濾器中提供的git_commit_non_empty_tree
函式實現相同的效果。 - --original <namespace>
-
使用此選項設定原始提交將儲存的名稱空間。預設值為 refs/original。
- -d <directory>
-
使用此選項設定用於重寫的臨時目錄的路徑。當應用樹過濾器時,命令需要將樹臨時檢出到某個目錄,這在大型專案中可能會佔用大量空間。預設情況下,它在
.git-rewrite/
目錄中執行此操作,但您可以透過此引數覆蓋該選擇。 - -f
- --force
-
除非強制執行,否則 git filter-branch 拒絕在存在臨時目錄或已有以 refs/original/ 開頭的引用時啟動。
- --state-branch <branch>
-
此選項將導致舊物件到新物件的對映在啟動時從指定分支載入,並在退出時作為新提交儲存到該分支,從而實現大型樹的增量。如果 <branch> 不存在,它將被建立。
- <rev-list options>…
-
git rev-list 的引數。這些選項包含的所有正向引用都將被重寫。您還可以指定
--all
等選項,但必須使用--
將它們與 git filter-branch 選項分開。暗示 重對映到祖先。
重對映到祖先
透過使用 git-rev-list[1] 引數,例如路徑限制器,您可以限制被重寫的修訂集。但是,命令列上的正向引用是特殊的:我們不允許它們被此類限制器排除。為此,它們會改為指向未被排除的最近祖先。
示例
假設您想從所有提交中刪除一個檔案(包含機密資訊或侵犯版權)
git filter-branch --tree-filter 'rm filename' HEAD
但是,如果檔案在某些提交的樹中不存在,則簡單的 rm
filename
將對該樹和提交失敗。因此,您可能希望改用 rm
-f
filename
作為指令碼。
將 --index-filter
與 git rm 結合使用會產生一個顯著更快的版本。與使用 rm
filename
一樣,如果檔案在提交的樹中不存在,git
rm
--cached
filename
將失敗。如果您想“完全忘記”一個檔案,它何時進入歷史無關緊要,因此我們還新增 --ignore-unmatch
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
現在,您將獲得儲存在 HEAD 中的重寫歷史。
將倉庫重寫為 foodir/
作為其專案根目錄,並丟棄所有其他歷史記錄
git filter-branch --subdirectory-filter foodir -- --all
因此,您可以將一個庫子目錄變成它自己的倉庫。注意 --
將 filter-branch 選項與修訂選項分開,以及 --all
以重寫所有分支和標籤。
將一個提交(通常位於另一個歷史的尖端)設定為當前初始提交的父級,以便將另一個歷史貼上到當前歷史之後
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD
(如果父字串為空——這發生在處理初始提交時——則將 graftcommit 新增為父級)。請注意,這假定歷史只有一個根(即,沒有發生沒有共同祖先的合併)。如果不是這種情況,請使用
git filter-branch --parent-filter \ 'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
或者更簡單地
git replace --graft $commit-id $graft-id git filter-branch $graft-id..HEAD
從歷史中刪除“Darl McBribe”所做的提交
git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ]; then skip_commit "$@"; else git commit-tree "$@"; fi' HEAD
函式 skip_commit 定義如下
skip_commit() { shift; while [ -n "$1" ]; do shift; map "$1"; shift; done; }
這個移位技巧首先丟棄了樹 ID,然後是 -p 引數。請注意,這正確地處理了合併!如果 Darl 提交了 P1 和 P2 之間的合併,它將正確傳播,並且合併的所有子提交將成為以 P1,P2 為其父級的合併提交,而不是原始合併提交。
注意:由這些提交引入的、且未被後續提交恢復的更改,仍將存在於重寫後的分支中。如果您想將更改連同提交一起丟棄,則應使用 git rebase 的互動模式。
您可以使用 --msg-filter
重寫提交日誌訊息。例如,透過 git svn 建立的倉庫中的 git svn-id 字串可以透過這種方式刪除
git filter-branch --msg-filter ' sed -e "/^git-svn-id:/d" '
如果您需要向例如最後 10 個提交(其中沒有一個是合併)新增 Acked-by 行,請使用此命令
git filter-branch --msg-filter ' cat && echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>" ' HEAD~10..HEAD
--env-filter
選項可用於修改提交者和/或作者身份。例如,如果您發現由於 user.email 配置錯誤導致您的提交身份錯誤,則可以在釋出專案之前進行更正,如下所示
git filter-branch --env-filter ' if test "$GIT_AUTHOR_EMAIL" = "root@localhost" then GIT_AUTHOR_EMAIL=john@example.com fi if test "$GIT_COMMITTER_EMAIL" = "root@localhost" then GIT_COMMITTER_EMAIL=john@example.com fi ' -- --all
要將重寫限制為歷史的某個部分,除了新的分支名稱之外,還要指定一個修訂範圍。新的分支名稱將指向 git rev-list 列印的此範圍的最高修訂。
考慮這段歷史
D--E--F--G--H / / A--B-----C
要僅重寫提交 D、E、F、G、H,而保留 A、B 和 C 不變,請使用
git filter-branch ... C..H
要重寫提交 E、F、G、H,請使用以下之一
git filter-branch ... C..H --not D git filter-branch ... D..H --not C
將整個樹移動到子目錄中,或從其中刪除
git filter-branch --index-filter \ 'git ls-files -s | sed "s-\t\"*-&newsubdir/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
縮減倉庫的清單
git-filter-branch 可以用於去除檔案的子集,通常結合使用 --index-filter
和 --subdirectory-filter
。人們期望最終的倉庫會比原始倉庫小,但您需要多做幾步才能真正使其變小,因為 Git 會努力不丟失您的物件,直到您告訴它這樣做。首先確保:
-
如果一個 blob 在其生命週期中被移動過,您確實刪除了所有檔名變體。
git
log
--name-only
--follow
--all
--
filename
可以幫助您找到重新命名。 -
您確實過濾了所有引用:呼叫 git-filter-branch 時使用
--tag-name-filter
cat
--
--all
。
然後有兩種方法可以獲得更小的倉庫。一種更安全的方法是克隆,這會使您的原始倉庫保持完整。
-
使用
git
clone
file:///path/to/repo
克隆它。克隆將不包含已刪除的物件。請參閱 git-clone[1]。(請注意,使用純路徑克隆只會硬連結所有內容!)
如果您出於任何原因真的不想克隆它,請檢查以下幾點(按此順序)。這是一種非常具有破壞性的方法,所以請務必備份或回到克隆方式。您已收到警告。
-
刪除 git-filter-branch 備份的原始引用:執行
git
for-each-ref
--format="%
(refname
)"
refs/original/
|xargs
-n
1
git
update-ref
-d
。 -
使用
git
reflog
expire
--expire=now
--all
使所有引用日誌過期。 -
使用
git
gc
--prune=now
垃圾回收所有未引用的物件(如果您的 git-gc 不夠新,不支援--prune
的引數,請改用 git repack -ad; git prune)。
效能
git-filter-branch 的效能緩慢得像冰川;它的設計使得向後相容的實現不可能快速
-
在編輯檔案時,git-filter-branch 的設計是檢出原始倉庫中存在的每一個提交。如果您的倉庫有
10^5
個檔案和10^5
個提交,但每個提交只修改五個檔案,那麼 git-filter-branch 將強制您執行10^10
次修改,儘管最多隻有5*10^5
個唯一 blob。 -
如果您試圖作弊,並讓 git-filter-branch 只處理提交中修改的檔案,那麼會發生兩件事
-
當用戶只是嘗試重新命名檔案時,您會遇到刪除問題(因為嘗試刪除不存在的檔案看起來是空操作;需要一些詭計才能在檔案重新命名透過任意使用者提供的 shell 發生時重新對映刪除)
-
即使您成功地進行了重新命名刪除的詭計,您仍然在技術上違反了向後相容性,因為使用者被允許以依賴提交拓撲而不是僅僅基於檔案內容或名稱的方式過濾檔案(儘管這在實踐中尚未觀察到)。
-
-
即使您不需要編輯檔案,而只是想重新命名或刪除一些檔案,從而可以避免檢出每個檔案(即,您可以使用 --index-filter),您仍然在為您的過濾器傳遞 shell 片段。這意味著對於每個提交,您都必須有一個準備好的 git 倉庫,以便可以在其中執行這些過濾器。這是一個重要的設定。
-
此外,git-filter-branch 會為每個提交建立或更新幾個額外的檔案。其中一些是為了支援 git-filter-branch 提供的便捷函式(如 map()),而另一些則是為了跟蹤內部狀態(但也可能被使用者過濾器訪問;git-filter-branch 的一個迴歸測試就是這樣做的)。這實質上相當於使用檔案系統作為 git-filter-branch 和使用者提供的過濾器之間的 IPC 機制。磁碟通常是一種緩慢的 IPC 機制,並且寫入這些檔案實際上也代表了我們每次提交時都會遇到的獨立程序之間的強制同步點。
-
使用者提供的 shell 命令可能涉及命令管道,導致每個提交建立許多程序。建立和執行另一個程序在不同作業系統之間需要不同量的時間,但在任何平臺上,相對於呼叫函式來說,這都非常慢。
-
git-filter-branch 本身是用 shell 編寫的,這有點慢。這是唯一一個可以向後相容修復的效能問題,但與 git-filter-branch 設計固有的上述問題相比,工具本身的語言是一個相對次要的問題。
-
旁註:不幸的是,人們傾向於專注於“用 shell 編寫”這一方面,並定期詢問 git-filter-branch 是否可以用其他語言重寫以解決效能問題。這不僅忽略了設計中固有的更大問題,而且幫助也比您預期的要少:如果 git-filter-branch 本身不是 shell,那麼便捷函式(map(),skip_commit() 等)和
--setup
引數將無法在程式開始時只執行一次,而是需要新增到每個使用者過濾器前面(因此在每次提交時重新執行)。
-
git filter-repo 工具是 git-filter-branch 的替代品,它沒有這些效能問題或安全問題(下文提及)。對於那些現有工具依賴於 git-filter-branch 的使用者,git filter-repo 也提供了 filter-lamely,一個即插即用的 git-filter-branch 替代品(有一些注意事項)。儘管 filter-lamely 具有與 git-filter-branch 相同的所有安全問題,但它至少在一定程度上緩解了效能問題。
安全
git-filter-branch 充滿了陷阱,很容易導致倉庫損壞或結果比開始時更糟糕
-
某人可能有一套“工作且經過測試的過濾器”,他們將其記錄或提供給同事,然後同事在不同的作業系統上執行它們,而相同的命令在那裡未工作/未測試(git-filter-branch 手冊頁中的一些示例也受此影響)。BSD 與 GNU 使用者空間差異確實會造成麻煩。如果幸運,會噴出錯誤訊息。但同樣有可能的是,命令要麼沒有執行請求的過濾,要麼透過進行一些不需要的更改而悄悄地損壞。不需要的更改可能隻影響少數提交,因此也不一定明顯。(問題不一定明顯的事實意味著它們很可能在重寫後的歷史被使用了相當長一段時間後才被注意到,此時很難再為另一次重寫辯解了。)
-
帶有空格的檔名常常被 shell 片段錯誤處理,因為它們會給 shell 管道帶來問題。不是每個人都熟悉 find -print0, xargs -0, git-ls-files -z 等。即使熟悉這些的人也可能認為這些標誌不相關,因為在做過濾的人加入專案之前,其他人已經在他們的倉庫中重新命名了任何此類檔案。而且通常,即使那些熟悉處理帶空格引數的人也不會這樣做,只是因為他們沒有考慮所有可能出錯的事情的心態。
-
非 ASCII 檔名可能會在不知不覺中被刪除,儘管它們位於所需的目錄中。保留所需的路徑通常使用管道來完成,例如
git
ls-files
|grep
-v
^WANTED_DIR/
|xargs
git
rm
。ls-files 只會在需要時引用檔名,所以人們可能不會注意到其中一個檔案與正則表示式不匹配(至少直到太晚)。是的,瞭解 core.quotePath 的人可以避免這種情況(除非他們有其他特殊字元,如 \t、\n 或 "),以及使用 ls-files -z 和 grep 以外的東西的人可以避免這種情況,但這並不意味著他們會。 -
同樣,在移動檔案時,可能會發現帶有非 ASCII 或特殊字元的檔名最終出現在不同的目錄中,其中包含一個雙引號字元。(這在技術上與上述引用問題相同,但也許是一種有趣的、不同的方式,它已經並確實表現為一個問題。)
-
意外地混淆新舊歷史太容易了。使用任何工具仍然可能發生,但 git-filter-branch 幾乎是在鼓勵這樣做。如果幸運,唯一的缺點是使用者感到沮喪,因為他們不知道如何縮小倉庫並刪除舊內容。如果不走運,他們將舊歷史與新歷史合併,最終得到每個提交的多個“副本”,其中一些包含不需要或敏感的檔案,而另一些則沒有。這以多種不同的方式發生
-
預設只進行部分歷史重寫(--all 不是預設選項,很少有示例顯示它)
-
沒有自動的執行後清理功能
-
--tag-name-filter(用於重新命名標籤時)不會刪除舊標籤,而只是新增帶有新名稱的新標籤
-
沒有提供足夠的教育資訊來告知使用者重寫的後果以及如何避免混淆新舊歷史。例如,本手冊頁討論了使用者需要理解他們需要將所有分支的更改 rebase 到新歷史之上(或刪除並重新克隆),但這只是需要考慮的多個問題之一。有關更多詳細資訊,請參閱 git filter-repo 手冊頁的“DISCUSSION”部分。
-
-
由於以下兩個問題之一,帶註釋的標籤可能會意外地轉換為輕量級標籤
-
有人可能進行了歷史重寫,意識到搞砸了,從 refs/original/ 中的備份恢復,然後重新執行他們的 git-filter-branch 命令。(refs/original/ 中的備份不是真正的備份;它首先解引用標籤。)
-
在您的 <rev-list-options> 中使用 --tags 或 --all 執行 git-filter-branch。為了將帶註釋的標籤保留為帶註釋的,您必須使用 --tag-name-filter(並且不得在先前搞砸的重寫中從 refs/original/ 恢復)。
-
-
任何指定了編碼的提交訊息在重寫後都會損壞;git-filter-branch 會忽略編碼,獲取原始位元組,然後將其提供給 commit-tree,而不告知其正確的編碼。(無論是否使用 --msg-filter,都會發生這種情況。)
-
提交訊息(即使都是 UTF-8)預設情況下也會因未更新而損壞——提交訊息中對其他提交雜湊的任何引用現在都將指向不再存在的提交。
-
沒有為幫助使用者找到他們應該刪除的不需要的垃圾提供設施,這意味著他們更有可能進行不完整或部分清理,這有時會導致混亂和人們浪費時間試圖理解。(例如,人們傾向於只尋找大檔案刪除,而不是大目錄或副檔名,一旦他們這樣做,那麼過了一段時間,使用新倉庫並回顧歷史的人會注意到一個構建產物目錄,其中有一些檔案但不是全部,或者一個依賴快取(node_modules 或類似),它永遠無法正常工作,因為它缺少一些檔案。)
-
如果未指定 --prune-empty,則過濾過程可能會建立大量令人困惑的空提交
-
如果指定了 --prune-empty,那麼在過濾操作之前有意放置的空提交也會被修剪,而不僅僅是修剪因過濾規則而變空的提交。
-
如果指定了 --prune-empty,有時空提交會被遺漏並仍然存在(一個相對罕見的 bug,但它確實發生…)
-
一個小問題是,使用者如果目標是更新倉庫中的所有姓名和電子郵件,可能會被引向 --env-filter,這隻會更新作者和提交者,而忽略標籤建立者。
-
如果使用者提供的 --tag-name-filter 將多個標籤對映到同一名稱,則不會提供警告或錯誤;git-filter-branch 只會以某種未記錄的預定義順序覆蓋每個標籤,導致最終只剩下一個標籤。(git-filter-branch 的一個迴歸測試要求這種令人驚訝的行為。)
此外,git-filter-branch 的糟糕效能常常導致安全問題
-
想出正確的 shell 片段來執行您想要的過濾有時很困難,除非您只是進行微不足道的修改,例如刪除幾個檔案。不幸的是,人們通常透過嘗試來判斷片段是否正確,但正確與否可能因特殊情況而異(檔名中的空格、非 ASCII 檔名、有趣的作者姓名或電子郵件、無效時區、存在嫁接或替換物件等),這意味著他們可能需要等待很長時間,遇到錯誤,然後重新開始。git-filter-branch 的效能如此糟糕,使得這個迴圈非常痛苦,減少了仔細重新檢查的可用時間(更不用說它對執行重寫的人的耐心造成了什麼影響,即使他們確實在技術上擁有更多時間)。這個問題更加複雜,因為損壞的過濾器導致的錯誤可能長時間不顯示和/或在大量輸出中丟失。更糟糕的是,損壞的過濾器通常只會導致靜默的錯誤重寫。
-
最重要的是,即使使用者最終找到了可用的命令,他們也自然希望分享它們。但他們可能不知道自己的倉庫沒有一些別人倉庫才有的特殊情況。因此,當其他人使用不同的倉庫執行相同的命令時,他們就會遇到上述問題。或者,使用者只是運行了確實經過特殊情況驗證的命令,但他們將其執行在不同的作業系統上,在那裡它不起作用,如上所述。