簡體中文 ▾ 主題 ▾ 最新版本 ▾ git-filter-branch 最後更新於 2.44.0

名稱

git-filter-branch - 重寫分支

概要

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,請仔細閱讀 安全(和 效能)以瞭解 filter-branch 的地雷,然後竭盡所能地避免其中列出的危險。

描述

允許您透過重寫 <rev-list-options> 中提到的分支來重寫 Git 提交歷史,並對每個提交應用自定義過濾器。這些過濾器可以修改每個樹(例如,刪除檔案或對所有檔案執行 Perl 重寫)或關於每個提交的資訊。否則,所有資訊(包括原始提交時間或合併資訊)都將被保留。

該命令只會重寫命令列中提到的*正面*引用(例如,如果您傳遞 a..b,則只會重寫 b)。如果您未指定任何過濾器,提交將不經任何更改地重新提交,這通常不會產生任何影響。儘管如此,這在未來可能有助於彌補某些 Git 錯誤等,因此允許這種用法。

注意:此命令遵守 .git/info/grafts 檔案和 refs/replace/ 名稱空間中的引用。如果您定義了任何 graft 或替換引用,執行此命令將使它們永久生效。

警告!重寫的歷史將為所有物件提供不同的物件名稱,並且不會與原始分支收斂。您將無法輕鬆地將重寫的分支推送到原始分支之上並進行分發。如果您不瞭解全部含義,請不要使用此命令,並且如果一個簡單的提交就足以解決您的問題,請避免使用它。(有關重寫已釋出歷史的更多資訊,請參閱 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>

這是用於重寫提交父項列表的過濾器。它將在 stdin 上接收父項字串,並在 stdout 上輸出新的父項字串。父項字串的格式如 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>)…​]”,提交訊息從 stdin 讀取。提交 ID 預計會從 stdout 讀取。

作為特殊擴充套件,提交過濾器可以發出多個提交 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] 引數(例如,路徑限制器),您可以限制將被重寫的修訂集。但是,命令列上的正面引用有所區別:我們不讓它們被此類限制器排除。為此,它們將被重寫為指向最近未被排除的祖先。

退出狀態

成功時,退出狀態為 0。如果過濾器找不到任何要重寫的提交,退出狀態為 2。在任何其他錯誤情況下,退出狀態可能是任何其他非零值。

示例

假設您想從所有提交中刪除一個檔案(包含機密資訊或侵犯版權的內容)

git filter-branch --tree-filter 'rm filename' HEAD

但是,如果檔案在某個提交的樹中不存在,簡單的 rm filename 會在該樹和提交中失敗。因此,您可能希望使用 rm -f filename 作為指令碼。

使用 git rm--index-filter 產生了顯著更快的版本。與使用 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 過期所有 reflog。

  • 使用 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(在重新命名標籤時)不刪除舊標籤,只新增具有新名稱的新標籤

    • 教育資訊很少,無法告知使用者重寫的後果以及如何避免混淆新舊歷史。例如,本手冊討論了使用者需要了解他們需要將他們所有分支的更改重新基礎化到新歷史之上(或刪除並重新克隆),但這只是需要考慮的多個問題之一。有關更多詳細資訊,請參閱 git filter-repo 手冊頁的“討論”部分。

  • 帶有註解的標籤可能會意外地轉換為輕量級標籤,原因可能是以下任一問題:

    • 有人可能會進行歷史重寫,意識到他們搞砸了,從 refs/original/ 中的備份恢復,然後重新執行他們的 git-filter-branch 命令。(refs/original/ 中的備份不是真正的備份;它首先會解引用標籤。)

    • 在您的 <rev-list-options> 中執行 git-filter-branch 並使用 --tags 或 --all。為了保留帶註解的標籤作為帶註解的,您必須使用 --tag-name-filter(並且不能在先前出錯的重寫中從 refs/original/ 恢復)。

  • 任何指定了編碼的提交訊息在重寫時都會損壞;git-filter-branch 會忽略編碼,獲取原始位元組,並將其饋送給 commit-tree,而不會告知其正確的編碼。(無論是否使用 --msg-filter,都會發生這種情況。)

  • 提交訊息(即使它們都是 UTF-8)預設情況下會因未更新而損壞 — 提交訊息中對其他提交雜湊的任何引用現在都會指向不再存在的提交。

  • 沒有提供幫助使用者查詢應刪除的無用內容的功能,這意味著他們更有可能進行不完整或部分的清理,有時會導致混淆,並且人們會浪費時間試圖理解。(例如,人們傾向於只查詢要刪除的大檔案而不是大目錄或擴充套件,然後,過了一段時間,使用新儲存庫的人在檢視歷史時會注意到一個構建工件目錄,其中包含一些檔案但不是其他檔案,或者一個依賴項快取(node_modules 或類似)由於缺少某些檔案而從未起作用。)

  • 如果未指定 --prune-empty,則過濾過程可能會建立大量令人困惑的空提交。

  • 如果指定了 --prune-empty,則過濾規則以外的、有意放置的空提交也會被修剪,而不僅僅是因過濾規則而變為空的提交。

  • 如果指定了 --prune-empty,有時空提交仍會被遺漏並保留(這是一個相對罕見的錯誤,但確實會發生……)

  • 一個小問題是,目標是更新儲存庫中所有名稱和電子郵件的使用者可能會被引導使用 --env-filter,它只會更新作者和提交者,而忽略標籤者。

  • 如果使用者提供的 --tag-name-filter 將多個標籤對映到同一個名稱,則不會發出警告或錯誤;git-filter-branch 只是以某種未公開的預定義順序覆蓋每個標籤,最終只有一個標籤。(一個 git-filter-branch 迴歸測試需要這種令人驚訝的行為。)

此外,git-filter-branch 的效能不佳經常導致安全問題

  • 除非您只是進行微小的修改,例如刪除幾個檔案,否則想出用於執行所需過濾的正確 shell 片段有時會很困難。不幸的是,人們通常透過嘗試來學習片段是否正確或錯誤,但正確性或錯誤性可能因特殊情況(檔名中的空格、非 ASCII 檔名、奇怪的作者姓名或電子郵件、無效的時區、graft 或替換物件的存在等)而異,這意味著他們可能不得不長時間等待、遇到錯誤,然後重新開始。git-filter-branch 的效能非常差,以至於這個週期很痛苦,減少了仔細重新檢查的時間(更不用說它對執行重寫的人的耐心造成的打擊,即使他們技術上有更多的時間可用)。這個問題因以下幾點而加劇:有問題的過濾器的錯誤可能不會很快顯示出來,並且/或者會在大量輸出中丟失。更糟糕的是,有問題的過濾器通常只會導致靜默的錯誤重寫。

  • 最糟糕的是,即使使用者最終找到了有效的命令,他們自然也想共享它們。但他們可能不知道他們的儲存庫沒有一些特殊情況,而別人的有。因此,當另一個具有不同儲存庫的人執行相同的命令時,他們會遇到上述問題。或者,使用者只是運行了實際上經過特殊情況驗證的命令,但他們在不同的作業系統上執行它,而它在那裡不起作用,如上所述。

GIT

Git[1] 套件的一部分