使用git filter-branch除了指定的文件以外,删除所有历史记录

6
我正在尝试在两个Git仓库repo1repo2之间移动一些文件。我有一个短列表,列出了我想要移动的文件(保留历史记录)。
repo1移动三个文件:
libraryname/file1
libraryname/file2
tests/libraryname/file3

libraryname/tests/libraryname/ 目录下还有其他文件。在 /tests/ 目录下还有其他文件夹。

我的计划是先检出 repo1,然后修改历史记录树,只保留我感兴趣的文件的历史记录。然后检出 repo2,并合并之前操作的输出。看起来 git filter-branch 是第一步正确的工具。

到目前为止,我已经尝试过 git filter-branch --index-filter 'git rm -r --cached <FILES>' 其中 <FILES> 列出了每个不需要的整个文件夹或文件。

但是这样会留下许多文件夹,这些文件夹在此版本库的生命周期中曾经存在,但在当前版本 HEAD 下不存在。找出该仓库历史记录中曾经存在的所有文件夹和文件似乎相当繁琐 - 必须有更好的方法。

如何最终获得只包括这三个文件的 Git 提交树?是否有比我提出的更好的方法?或者,有没有一种方法可以删除所有当前在 HEAD 下不存在的文件的痕迹?

3个回答

9

从 Git 2.24(2019 年第四季度)开始,git filter-branch 已被弃用

相应的替代方法是使用newren/git-filter-repo以及该工具的示例部分

如果您有一长串要筛选的文件、目录、通配符或正则表达式列表,请将它们放入一个文件中,并使用 --paths-from-file 参数;例如,使用名为 stuff-i-want.txt 的文件,内容如下:

README.md
guides/
tools/releases
glob:*.py
regex:^.*/.*/[0-9]{4}-[0-9]{2}-[0-9]{2}.txt$
tools/==>scripts/
regex:(.*)/([^/]*)/([^/]*)\.text$==>\2/\1/\3.txt

那么你可以运行。
git filter-repo --paths-from-file stuff-i-want.txt

在您的情况下,“stuff-i-want.txt”将是:
libraryname/file1
libraryname/file2
tests/libraryname/file3

kubanczyk评论中指出:

在Ubuntu 20.04上运行良好,可以直接pip3 install git-filter-repo,因为它只使用stdlib,不安装任何依赖项。

在Ubuntu 18上,它与distro的git版本不兼容,但可以很容易地在docker run -ti ubuntu:20.04上运行它。


不错的工具!在Ubuntu 20.04上运行良好,您只需pip3 install git-filter-repo,因为它仅使用stdlib,不安装任何依赖项。在Ubuntu 18上,它与发行版的git版本不兼容,但很容易在docker run -ti ubuntu:20.04上运行它。 - kubanczyk
1
@kubanczyk 感谢您的反馈。我已将您的评论包含在答案中以增加可见性。 - VonC

6
你说它会留下文件夹,我猜你指的是它会在这些文件夹中留下文件(因为Git不会保留空文件夹)...
看起来你可能想采取清除索引然后重新添加所需条目的方法。
git filter-branch ...
    --index-filter 'git rm -r --cached * && git reset $GIT_COMMIT -- libraryname/file1 libraryname/file2 tests/libraryname/file3
    ...

由于您正在大量减少内容,不要忘记您可能需要包括一个--prune-empty选项。


这对我起作用了,而 git checkout $GIT_COMMIT <path> 失败了(对于不存在 <path> 的提交)。谢谢! - bossi

1
这里提供了一种基于白名单的方法,可能更快(因为它只需要比较预先排序列表的整行),并且如果涉及大量文件,则更容易实现。
  1. 创建一个包含所有分支提交中文件的排序列表:

    $ export LC_COLLATE=C whitelist="$(mktemp)" && git log --name-status | sed 's/^[A-Z][[:space:]]\{1,\}//; t; d' | sort -u > "$whitelist"

  2. 使用你喜欢的文本编辑器编辑该列表,并删除不需要保留的文件,即创建一个要保留文件的白名单。

    $ "$EDITOR" -- "$whitelist" # 从列表中删除不需要保留的内容

  3. 执行实际的过滤操作:

    $ git filter-branch -f --index-filter 'git ls-files -c | sort | comm -23 -- - "$whitelist" | while IFS= read -r f; do git rm --cached -- "$f"; done' --prune-empty

  4. 在过滤操作成功完成后删除白名单。

    $ rm -- "$whitelist" && unset LC_COLLATE whitelist


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接