【问题标题】:Remove history for everything except a list of files using git filter-branch使用 git filter-branch 删除除文件列表之外的所有内容的历史记录
【发布时间】:2017-08-11 10:30:55
【问题描述】:

我正在尝试在两个 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,但在此存储库生命周期的某个时刻已经存在。弄清楚这个 repo 历史上存在的所有东西似乎很乏味 - 一定有更好的方法

我如何最终得到一个只包含这三个文件的 git 提交树? 有没有比我建议的更好的方法? 或者,有没有办法删除HEAD 上当前不存在的所有文件的痕迹?

【问题讨论】:

    标签: git git-filter-branch


    【解决方案1】:

    使用 Git 2.24(2019 年第四季度),git filter-branch is deprecated

    等价物是,使用newren/git-filter-repo,以及它的example section

    如果您要过滤的文件、目录、glob 或正则表达式的列表很长,您可以将它们粘贴到一个文件中并使用--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 指出的in the comments

    在 Ubuntu 20.04 上运行良好,您只需 pip3 install git-filter-repo,因为它是 stdlib-only 并且不安装任何依赖项。

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

    【讨论】:

    • 不错的工具!在 Ubuntu 20.04 上运行良好,您只需 pip3 install git-filter-repo,因为它是 stdlib-only 并且不安装任何依赖项。在 Ubuntu 18 上,它与发行版的 git 版本不兼容,但很容易在 docker run -ti ubuntu:20.04 上运行它
    • @kubanczyk 感谢您的反馈。我已将您的评论包含在答案中以提高知名度。
    【解决方案2】:

    你说它留下了文件夹;我假设你的意思是它留下文件 in 那些文件夹(因为 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> 的提交)。谢谢!
    【解决方案3】:

    这是一种基于白名单的方法,如果涉及大量文件,它可能会更快(因为它只需要比较整行预先排序的列表)并且更容易。

    1. 创建分支的所有提交中所有文件的排序列表:

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

    2. 使用您喜欢的文本编辑器编辑该列表并删除所有不感兴趣的文件,即。 e.创建要保留的文件的白名单。

      $ "$EDITOR" -- "$whitelist" # remove from list what you don't want to keep

    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

    【讨论】:

      猜你喜欢
      • 2016-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-06
      • 2018-01-13
      • 1970-01-01
      相关资源
      最近更新 更多