?
This document uses PHP Chinese website manual Release
git-filter-branch - 重寫分支
git filter-branch [--setup <command>] [--env-filter <command>] [--tree-filter <command>] [--index-filter <command>] [--parent-filter <command>] [--msg-filter <command>] [--commit-filter <command>] [--tag-name-filter <command>] [--subdirectory-filter <directory>] [--prune-empty] [--original <namespace>] [-d <directory>] [-f | --force] [--] [<rev-list options>…]
讓您通過重寫<rev-list選項>中提到的分支來重寫Git修訂歷史記錄,并在每個修訂版上應(yīng)用自定義過濾器。這些過濾器可以修改每個樹(例如,刪除文件或?qū)λ形募\行perl重寫)或每個提交的信息。否則,將保留所有信息(包括原始提交時間或合并信息)。
該命令將只重寫positive
命令行中提到的ref(例如,如果通過a..b
,只會b
被重寫)。如果您沒有指定過濾器,那么提交將被重新發(fā)送而不做任何更改,這通常沒有任何影響。盡管如此,這對于補償一些Git bug或?qū)砜赡軙杏?,因此這種用法是允許的。
注意:該命令.git/info/grafts
在refs/replace/
命名空間中承認(rèn)文件和引用。如果您有任何定義的移植或替換參考,運行此命令將使它們永久。
警告!重寫的歷史將為所有對象具有不同的對象名稱,并且不會與原始分支會聚。您將無法輕松地將重寫的分支推送并分發(fā)到原始分支的頂部。如果您不知道全部含義,請不要使用此命令,并且如果簡單的單一提交就足以解決您的問題,請避免使用它。(有關(guān)重寫已發(fā)布?xì)v史記錄的更多信息,請參閱git-rebase [1]中的“從上游重新引導(dǎo)恢復(fù)”一節(jié)。)
始終驗證重寫的版本是否正確:原始參考文獻(如果與重寫版本不同)將存儲在命名空間中refs/original/
。
請注意,由于此操作非常昂貴,因此使用該-d
選項將臨時目錄從磁盤重定向到磁盤可能是一個好主意,例如在tmpfs上。據(jù)報道,加速非常明顯。
這些過濾器按以下列出的順序應(yīng)用。<command>參數(shù)總是使用eval
命令在shell上下文中進行評估(出于技術(shù)原因,提交過濾器值得注意的例外)。在此之前,$GIT_COMMIT
環(huán)境變量將被設(shè)置為包含被重寫的提交的ID。此外,GIT_AUTHOR_NAME,GIT_AUTHOR_EMAIL,GIT_AUTHOR_DATE,GIT_COMMITTER_NAME,GIT_COMMITTER_EMAIL和GIT_COMMITTER_DATE取自當(dāng)前提交并導(dǎo)出到環(huán)境中,以影響由git-commit-tree [1]創(chuàng)建的替換提交的作者身份和提交者身份過濾器已運行。
如果任何對<command>的評估返回非零退出狀態(tài),則整個操作將被中止。
一個map
函數(shù)可以使用“original sha1 id”參數(shù),如果提交已被重寫,則輸出“重寫的sha1 id”,否則輸出“original sha1 id”。map
如果您的提交過濾器發(fā)出多個提交,該函數(shù)可以在單獨的行上返回多個ids。
--setup <command>
這不是為每個提交執(zhí)行的實際過濾器,而是在循環(huán)之前的一次設(shè)置。因此,還沒有定義提交特定的變量。由于技術(shù)原因,此處定義的函數(shù)或變量可以在除提交過濾器之外的以下過濾步驟中使用或修改。
--env-filter <command>
如果您只需要修改提交將執(zhí)行的環(huán)境,則可以使用此過濾器。具體來說,您可能需要重寫作者/提交者名稱/電子郵件/時間環(huán)境變量(有關(guān)詳細(xì)信息,請參閱git-commit-tree [1])。
--tree-filter <command>
這是重寫樹及其內(nèi)容的過濾器。該參數(shù)在shell中用工作目錄設(shè)置為檢出樹的根來評估。然后使用新的樹(新文件自動添加,消失的文件自動刪除 - 既不.gitignore文件也沒有任何其他忽略規(guī)則有任何影響?。?。
--index-filter <command>
這是重寫索引的過濾器。它類似于樹型過濾器,但不檢出樹,這使得它更快。經(jīng)常使用git rm --cached --ignore-unmatch ...
,請參閱下面的示例。對于毛病,請參閱git-update-index [1]。
--parent-filter <command>
這是重寫提交的父列表的過濾器。它將接收stdin上的父字符串,并應(yīng)在stdout上輸出新的父字符串。父字符串采用git-commit-tree [1]中描述的格式:初始提交時為空,正常提交時為“-p parent”,合并為“-p parent1 -p parent2 -p parent3 ...”承諾。
--msg-filter <command>
這是重寫提交消息的過濾器。參數(shù)在shell中使用標(biāo)準(zhǔn)輸入的原始提交消息進行評估; 其標(biāo)準(zhǔn)輸出被用作新的提交消息。
--commit-filter <command>
這是執(zhí)行提交的過濾器。如果指定了此過濾器,它將被調(diào)用,而不是git commit-tree
命令,參數(shù)形式為“<TREE_ID>(-p <PARENT_COMMIT_ID>)...”和stdin上的日志消息。提交ID預(yù)計在標(biāo)準(zhǔn)輸出上。
作為一個特殊的擴展,提交過濾器可能會發(fā)出多個提交id; 在那種情況下,原來承諾的改寫孩子將把他們?nèi)慨?dāng)作父母。
您可以map
在此過濾器中使用便利功能,以及其他便利功能。例如,調(diào)用skip_commit "$@"
將忽略當(dāng)前的提交(但不會更改它!如果需要,則git rebase
改為使用)。
如果您不希望保留對單個父代的提交并且不對樹進行更改git_commit_non_empty_tree "$@"
,git commit-tree "$@"
那么也可以使用它。
--tag-name-filter <command>
這是重寫標(biāo)簽名稱的過濾器。傳遞時,將調(diào)用指向重寫對象(或指向重寫對象的標(biāo)記對象)的每個標(biāo)記ref。原始標(biāo)簽名稱通過標(biāo)準(zhǔn)輸入傳遞,新標(biāo)簽名稱預(yù)計在標(biāo)準(zhǔn)輸出上。
原始標(biāo)簽不會被刪除,但可以被覆蓋; 使用“--tag-name-filter cat”來簡單地更新標(biāo)簽。在這種情況下,要非常小心,并確保在轉(zhuǎn)換發(fā)生沖突的情況下備份舊標(biāo)簽。
幾乎可以正確重寫標(biāo)簽對象。如果標(biāo)簽附有消息,則會使用相同的消息,作者和時間戳創(chuàng)建新的標(biāo)簽對象。如果標(biāo)簽附有簽名,簽名將被剝離。根據(jù)定義,不可能保留簽名。這是“幾乎”適當(dāng)?shù)脑?,因為理想情況下,如果標(biāo)簽沒有改變(指向相同的對象,具有相同的名稱等),它應(yīng)該保留任何簽名。情況并非如此,簽名將永遠被刪除,買家要小心。也不支持更改作者或時間戳(或針對該問題的標(biāo)記消息)。指向其他標(biāo)簽的標(biāo)簽將被重寫為指向底層提交。
--subdirectory-filter <directory>
只能看看觸及給定子目錄的歷史記錄。結(jié)果將包含該目錄(并且僅包含該目錄)作為其項目根目錄。意味著重新映射到祖先。
--prune-empty
有些過濾器會生成空的提交,使樹保持不變。這個選項指示git-filter-branch刪除這樣的提交,如果它們只有一個或零個未修剪的父母; 因此合并提交將保持不變。這個選項不能與一起使用--commit-filter
,盡管通過git_commit_non_empty_tree
在提交過濾器中使用提供的功能可以實現(xiàn)相同的效果。
--original <namespace>
使用此選項設(shè)置原始提交將存儲在其中的名稱空間。默認(rèn)值是refs/original
。
-d <directory>
使用此選項可將路徑設(shè)置為用于重寫的臨時目錄。當(dāng)應(yīng)用樹型過濾器時,該命令需要暫時將該樹檢出到某個目錄,這在大型項目的情況下可能消耗相當(dāng)大的空間。默認(rèn)情況下,它在.git-rewrite/
目錄中執(zhí)行此操作,但您可以通過此參數(shù)覆蓋該選項。
-f --force
git filter-branch
拒絕從現(xiàn)有的臨時目錄開始,或者當(dāng)已經(jīng)有ref時refs/original/
,除非強制。
<rev-list options>…
參數(shù)git rev-list
。這些選項包含的所有正面參考都被重寫。您也可以指定諸如此類的選項--all
,但您必須使用--
它們將它們與git filter-branch
選項分開。意味著重新映射到祖先。
通過使用git-rev-list [1]參數(shù),例如路徑限制器,您可以限制被重寫的修訂集。然而,在命令行上的正面參考是有區(qū)別的:我們不會讓這些限制器排除它們。為此,他們改寫為指向最近的未被排除的祖先。
假設(shè)您想從所有提交中刪除文件(包含機密信息或版權(quán)侵犯):
git filter-branch --tree-filter 'rm filename' HEAD
但是,如果該文件在某個提交的樹中不存在,則該樹的簡單操作rm filename
將失敗并提交。因此,您可以改為使用rm -f filename
腳本。
使用它--index-filter
可以git rm
產(chǎn)生更快的版本。與使用一樣,如果文件不在提交樹中rm filename
,git rm --cached filename
將會失敗。如果你想“完全忘記”一個文件,它輸入歷史記錄時無關(guān)緊要,所以我們還添加--ignore-unmatch
:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
現(xiàn)在,您將獲得保存在HEAD中的重寫歷史記錄。
重寫存儲庫以使其看起來像是foodir/
其項目根目錄,并放棄所有其他歷史記錄:
git filter-branch --subdirectory-filter foodir -- --all
因此,您可以將庫子目錄轉(zhuǎn)換為自己的存儲庫。請注意,--
該filter-branch
選項將從修訂選項中分離選項,并--all
重寫所有分支和標(biāo)簽。
要將提交(通常位于其他歷史記錄的頂端)設(shè)置為當(dāng)前初始提交的父級,以便將其他歷史記錄粘貼到當(dāng)前歷史記錄的后面:
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD
(如果父字符串為空 - 當(dāng)我們處理初始提交時發(fā)生 - 將graftcommit作為父項添加)。請注意,這假設(shè)歷史記錄具有單個根(即沒有共同祖先發(fā)生時不合并)。如果不是這種情況,請使用:
git filter-branch --parent-filter \ 'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
甚至更簡單:
echo "$commit-id $graft-id" >> .git/info/grafts 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參數(shù)。請注意,這將正確處理合并!如果Darl在P1和P2之間進行合并,它將被正確傳播,并且合并的所有子代將成為合并提交,P1和P2作為它們的父代提交,而不是合并提交。
注意提交引入的更改以及未被后續(xù)提交恢復(fù)的更改仍將位于重寫的分支中。如果你想changes
與提交一起扔掉,你應(yīng)該使用交互模式git rebase
。
您可以使用重寫提交日志消息--msg-filter
。例如,可以通過以下方式刪除git svn-id
由創(chuàng)建的存儲庫中的字符串git svn
:
git filter-branch --msg-filter ' sed -e "/^git-svn-id:/d"'
如果你需要為Acked-by
最后10個提交(其中沒有一個是合并)添加行,請使用以下命令:
git filter-branch --msg-filter ' cat && echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>"' HEAD~10..HEAD
該--env-filter
選項可用于修改提交者和/或作者身份。例如,如果您發(fā)現(xiàn)由于配置錯誤的user.email而導(dǎo)致您的提交有錯誤身份,則可以在發(fā)布項目之前進行更正,如下所示:
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(xiàn),G,H,但只保留A,B和C,請使用:
git filter-branch ... C..H
要重寫提交E,F(xiàn),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
可以幫助您找到重命名。
你真的過濾了所有的refs:--tag-name-filter cat -- --all
在調(diào)用git-filter-branch時使用。
然后有兩種方法可以獲得較小的存儲庫。更安全的方法是克隆,這可以保持原來的原樣。
克隆它git clone file:///path/to/repo
。克隆將不會有被刪除的對象。參見git-clone [1]。(請注意,使用純路徑進行克隆只是將所有內(nèi)容硬鏈接起來!)如果您確實不想克隆它,無論出于何種原因,請檢查以下幾點(按此順序)。這是一種非常具有破壞性的方法,因此請進行備份或恢復(fù)克隆。你被警告了。
刪除由git-filter-branch備份的原始參考:說git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
。
使用所有reflogs git reflog expire --expire=now --all
。
垃圾收集所有未被引用的對象git gc --prune=now
(或者如果你的git-gc不夠新以支持參數(shù)--prune
,則git repack -ad; git prune
改為使用)。
git-filter-branch允許你對Git歷史進行復(fù)雜的shell腳本重寫,但如果你只是removing unwanted data
像大文件或密碼那樣,你可能不需要這種靈活性。對于這些操作,您可能需要考慮BFG Repo-Cleaner,一種基于JVM的git-filter-branch替代方案,對于這些用例而言,其典型速度至少快10-50倍,并且具有不同的特征:
任何特定版本的文件都會被精確清理once
。與git-filter-branch不同的是,BFG不會讓你有機會根據(jù)歷史記錄中何時或何時提交文件來處理文件。這個約束條件給了BFG的核心性能優(yōu)勢,并且非常適合清理不良數(shù)據(jù)的任務(wù) - 您不關(guān)心where
壞數(shù)據(jù),您只需要它gone
。
默認(rèn)情況下,BFG充分利用多核機器,并行清理提交文件樹。git-filter-branch清除按順序提交(即以單線程方式)提交,盡管is
可以在針對每個提交執(zhí)行的腳本中編寫包含它們自己的并行性的過濾器。
該命令選項都遠遠超過git的過濾分支更嚴(yán)格,并致力于只是為了消除不必要的數(shù)據(jù)-例如任務(wù):--strip-blobs-bigger-than 1M
。