首页 > 解决方案 > git filter-branch:通过排除过滤目录,不包括?

问题描述

假设我在 repo 中有这个结构:

repo/
  dir1/
  dir2/
  dir3/
  dir4/
  dir5/
  ...

现在我想保留所有目录,除了dir1dir2.

我可以使用此命令来保留指定的目录。

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- dir3 dir4 dir5 ... dirN' --prune-empty -- --all

现在,如果我有很多目录,排除我不需要的目录而不是指定我需要的目录会更简单。我怎么能那样做?

标签: gitgit-filter-branch

解决方案


更改您index-filter以专门删除不需要的路径,并且不对其他任何路径执行任何操作,即,该--index-filter部分变为:

--index-filter 'git rm --cached -qr --ignore-unmatch dir1 dir2'

您现在使用的索引过滤器由两个单独的命令组成,由&&. 这些命令是文本形式的:

  • 删除所有内容。(--ignore-unmatch这里没有意义,因为您使用.指定“存在的一切”,而“存在的一切”显然存在。)
  • 然后,从当前提交中放回dir3,dir4等。

由于您只想(递归地)删除“所有内容dir1”和“所有内容dir2”,请指定这些内容。--ignore-unmatch如果可能存在不存在dir1和/或dir2文件的提交,请保留。删除您想要删除的内容后,您无需放回任何内容:索引 -git filter-branch用于实现过滤的临时索引(见下文) - 现在其中包含正确的文件集。

侧边栏:这个索引到底是什么?

当你进行新的提交时,Git 实际上并没有使用你工作树中的文件。它们在这里并不重要。

相反,Git 有一个东西——主要实现为一个名为 的文件.git/index,实际上——Git 以不同的方式调用 indexstaging area,或者(现在很少)cache。该索引最初保存从当前提交中取出每个文件的副本。然后,您可以使用或更新索引中的文件,或将文件完全从索引中取出。git addgit rm

您可以将索引视为建议的下一次提交。当你运行时git commit,Git 会打包索引中的文件,并将它们冻结成一个新的、永久的、1 个只读提交。您在工作树中看到和使用的文件只为提供,而不是为 Git 提供。这就是为什么,每当您修改工作树文件并希望更改进入下一次提交时,您必须git add始终:git add告诉 Git获取工作树副本并使用它来覆盖索引副本,以便下一次提交会有这个版本。

当您使用 时git filter-branch,您有很多选择。最慢的,--tree-filter,每次提交,将其复制到临时索引中——因为 Git 总是需要这些东西的索引,即使它不是常规的主索引——然后从该临时索引中提取所有文件到临时树中. --tree-filter然后,您可以使用您的代码修改临时树中的文件。Git 然后重新读取临时树,从中构建一个新的(但仍然是临时的)索引,并使用它来进行新的提交。

所有这些复制都很慢。所以 filter-branch 给你--index-filter:这一次,Git 将提交复制到一个临时索引,然后让你直接修改临时索引。该git rm --cached命令通过从中删除文件来修改索引(或者在本例中为临时索引)。然后 filter-branch 从临时索引中进行新的提交。这会跳过最慢的部分--tree-filter

您仍然最终会将存储库中的每个提交复制到一些新的和改进的提交中,但是通过仅在 filter-branch 提供的临时索引中执行此操作,它会快得多。


1 Commits 实际上只是semi-permanent。只要 Git 可以找到它们,它们就会持续存在。有关这方面的更多信息,请参阅Think Like (a) Git。当您使用 filter-branch 时,您将一些提交复制到一些新的和改进的提交中,并让您的 Git 尝试忘记原始提交。最终,你的 Git 可能会忘记它们。


推荐阅读