如何重写分支,放弃对子文件夹的更改

How to rewrite a branch, discarding changes to a subfolder

我需要重写一个分支,丢弃对特定子文件夹(例如,其路径为 path/to/folder)所做的所有更改及其在从指定的 start 修订版开始的一系列修订版之间的内容结束于 HEAD。接受 start 修订版之前的更改。

我考虑过使用 git-filter-branch 来执行此操作,但不知道使用什么过滤器。我认为对每个提交的子文件夹使用 'git-reset' 可能有效,但是这个 post 让我质疑这种方法是否有效。

问题:如何重写上面第一段指定的分支?

git-filter-branch--tree-filter 一起使用,可能与 --prune-empty 一起使用。整个命令序列可能是这样的:

GOOD_STATE=<some-revision>
DIR_TO_PRESERVE="relative/path/to/directory"
BRANCH_TO_REWRITE=<branch-to-rewrite>
export GOOD_STATE DIR_TO_PRESERVE BRANCH_TO_REWRITE
git filter-branch --tree-filter \
    'rm -rf "${DIR_TO_PRESERVE}" && git checkout -f ${GOOD_STATE} -- "${DIR_TO_PRESERVE}"' \
    --prune-empty ${BRANCH_TO_REWRITE} --not ${GOOD_STATE}

作为更快的替代方法,可以使用 --index-filter。但是作为命令,您不应该使用 git rm... 因为这会完全删除路径。相反,您应该使用 git reset:

重置给定文件夹的索引
git filter-branch --index-filter \
    'git reset -q ${GOOD_STATE} -- "${DIR_TO_PRESERVE}"'' \
    --prune-empty ${BRANCH_TO_REWRITE} --not ${GOOD_STATE}

为了加快速度,可以暂时将存储库复制到更快的文件系统(为此,我在 Linux 上使用了 tmpfs,9007 次提交已使用 --index-filter 在 14 中重写最少 37 秒)。

最终起作用的是使用 git-filter-branch 结合 git rm 删除提交中添加的新文件和 'git checkout' 恢复状态。

git filter-branch -f --prune-empty --index-filter 'git rm -rfq -- path/to/subdir && git checkout -f <the-good-state-sha1> -- path/to/subdir' -- start..HEAD

这里 start 是起始提交,<the-good-state-sha1>start 的父提交的哈希值。 注意:如图所示,path/to/subdir需要指定两次,一次在rm命令中,一次在checkout命令中。