filter-repo 使用

從 filter-branch 轉(zhuǎn)換

本文檔面向熟悉 filter-branch 并希望學習如何轉(zhuǎn)換到使用 filter-repo 的人。

目錄

基本差異

使用 git filter-branch 時,你有一個 git 倉庫,其中每個提交(在你指定的分支或修訂版本內(nèi))都會被檢出,然后你運行一個或多個 shell 命令來將工作副本轉(zhuǎn)換為你想要的最終狀態(tài)。

使用 git filter-repo 時,你實際上獲得了一個編輯工具,可以操作倉庫的 fast-export 序列化。這意味著有一個包含倉庫所有內(nèi)容的輸入流,你通常不是以要運行的命令形式指定過濾器,而是使用許多常見的預定義過濾器,這些過濾器提供各種方式來基于倉庫的組件(如路徑名、文件內(nèi)容、用戶名或電子郵件等)對倉庫進行切片、切塊或修改。這使得常見操作更容易,即使它不如 shell 回調(diào)那么靈活。對于需要更復雜或特殊處理的情況,filter-repo 提供了 Python 回調(diào),可以對從 fast-export 流中填充的數(shù)據(jù)結構進行操作,幾乎可以做任何你想做的事。

filter-branch 默認在倉庫的一個子集上工作,并要求你指定一個或多個分支,這意味著你需要指定 -- --all 來修改所有提交。相比之下,filter-repo 默認重寫所有內(nèi)容,如果你想限制到某個特定的分支集或提交范圍,你需要指定 --refs <rev-list-args>。(但是,以連字符開頭的任何 <rev-list-args> 都不被 filter-repo 接受,因為它們看起來像是不同選項的開始。)

filter-repo 還自動處理額外的問題,比如重寫舊commit ID 的提交消息,使其引用重寫后的commit ID,刪除由于指定的過濾器而變?yōu)榭盏奶峤唬约霸谶^濾操作結束時自動縮小和 gc 倉庫。

filter-branch 示例的轉(zhuǎn)換

刪除文件

filter-branch 手冊提供了三個刪除單個文件的不同示例,基于不同級別的易用性與謹慎性和性能:

git filter-branch --tree-filter 'rm filename' HEAD
  • 這個命令會檢出每個提交,運行 rm filename 命令,然后重新提交。
  • 如果文件不存在,rm 命令會報錯,但過濾器會繼續(xù)執(zhí)行。
  • 這種方法最慢,因為它需要在每個提交上實際檢出文件。
git filter-branch --tree-filter 'rm -f filename' HEAD
  • 這個命令與第一個類似,但使用了 rm -f,這意味著"強制刪除"。
  • 即使文件不存在,也不會報錯。
  • 仍然會檢出每個提交,但比第一個命令稍微健壯一些。
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
  • 這個命令使用 --index-filter,它只操作 Git 索引,不會檢出文件。
  • git rm --cached 從 Git 索引中刪除文件,但不觸及工作目錄。
  • --ignore-unmatch 選項使得即使文件不在某些提交中也不會報錯。
    這是最快和最高效的方法,特別是對于大型倉庫。

所有這些在git filter-repo都變成了

git filter-repo --invert-paths --path filename

提取子目錄

通過以下方式提取子目錄:

git filter-branch --subdirectory-filter foodir -- --all

這是最容易轉(zhuǎn)換的命令之一;它只是變成了

git filter-repo --subdirectory-filter foodir

將整個樹移動到子目錄

保留所有文件但將它們放在新的子目錄中:

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-repo --to-subdirectory-filter newsubdir

刪除某個作者的提交

警告:這對于 filter-branch 和 filter-repo 都是一個糟糕的例子。它并不從倉庫中刪除用戶所做的更改,它只是刪除有問題的提交,同時將其更改壓縮到任何后續(xù)提交中,就好像后續(xù)作者也對這些更改負責一樣。如果你在看這個例子,git rebase 可能更適合你真正想要的。(另見這個解釋 rebase 和 filter-repo 之間差異的說明

這個 filter-branch 例子

git filter-branch --commit-filter '
    if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ];
    then
        skip_commit "$@";
    else
        git commit-tree "$@";
    fi' HEAD

變成了

git filter-repo --commit-callback '
    if commit.author_name == b"Darl McBribe":
        commit.skip()
    '

重寫提交消息 -- 刪除文本

通過以下方式從提交消息中刪除 git-svn-id: 行:

git filter-branch --msg-filter '
    sed -e "/^git-svn-id:/d"
    '

變成了

git filter-repo --message-callback '
    return re.sub(b"^git-svn-id:.*\n", b"", message, flags=re.MULTILINE)
    '

重寫提交消息 -- 添加文本

通過以下方式向最后十個提交添加 Acked-by 行:

git filter-branch --msg-filter '
        cat &&
        echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>"
    ' master~10..master

變成了

git filter-repo --message-callback '
        return message + b"Acked-by: Bugs Bunny <bunny@bugzilla.org>\n"
    ' --refs master~10..master

更改作者/提交者(/標簽者?)信息

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

變成了

# 確保 '<john@example.com> <root@localhost>' 是 .mailmap 中的一行,然后:
git filter-repo --use-mailmap

git filter-repo --email-callback '
  return email if email != b"root@localhost" else b"john@example.com"
  '

(作為額外的好處,這兩種 filter-repo 替代方案也會修復標簽者的電子郵件,而 filter-branch 示例不會)

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容