Git:如何从Git历史记录中删除当前不存在的*所有*文件?

11
我看到了很多关于如何从所有git历史记录中删除一个单个文件的文章和问题。例如:在Git存储库中删除/删除大文件的提交历史记录? 我想要做的是删除所有不在主分支头部的文件。
我的用例是我正在从一个单体存储库(称为monolith)分离出一个较小的存储库(称为small)。我想在创建small时保留git历史记录,但只保留与相关的git历史记录。
首先,在GitHub上创建了一个新存储库small。然后,在我的笔记本电脑上,我将其添加为名为origin-small的远程到我的本地monolith存储库,并将monolith的主分支的当前状态推送到origin-small
然后,我从monolith中删除了远程origin-small,更改了目录,并从GitHub克隆了small。看起来,我有了一个带有完整历史记录的原始存储库monolith的副本。
但是,small的历史记录中有很多不再相关的文件,它们使存储库变得臃肿。
我想做的是:
  1. small中删除所有不必要的文件。
  2. 运行一个命令来清除刚刚删除的文件的整个git历史记录。
是否有一种方法可以使用单个命令完成此操作?还是我需要为每个要删除的文件/目录运行一次git filter-branch
2个回答

16

我最终使用了git-filter-repo警告:如果存在标签,则此方法无法更新远程标签。

  1. 安装git-filter-repo

brew install git-filter-repo
  • 克隆你想要的仓库,以镜像形式。

  • git clone --mirror <my-repo-url>
    
  • 进入仓库目录。

    cd <my-repo-name>
    
    分析仓库以识别所有在历史记录中存在但不再存在的文件。
    git filter-repo --analyze
    
    analysis 输出目录中,将有一个名为 path-deleted-sizes.txt 的文件,其中包含了一份所有曾经被提交过的文件清单,这些文件后来被删除了,但仍然存在于 Git 历史记录中。 创建一个新文件,该文件不包含标题和其他列。
    tail +3 ./filter-repo/analysis/path-deleted-sizes.txt \
        | tr -s ' ' \
        | cut -d ' ' -f 5- \
        > ./filter-repo/analysis/path-deleted.txt
    
    清理所有已经不存在的文件在 Git 历史记录中。这将清理不良提交,删除空提交,并为您重新压缩一切。
    git filter-repo --invert-paths --paths-from-file ./filter-repo/analysis/path-deleted.txt
    
  • 清理./filter-repo目录,否则您将无法推送更改。

  • rm -rf ./filter-repo
    
  • 强制推送所有引用到远程仓库。即使命令没有指示,它也将进行强制推送。此外,它将更新远程仓库上的所有分支,这非常方便。如果在GitHub / Bitbucket等平台上启用了某些分支的分支保护,则需要允许强制推送。如果发现某些引用无法进行强制推送,则可以随时重新运行此命令。

  • git push
    

    参见:如何在提交历史中识别大文件 - Martin Thoma
    1
    这很棒,但是应该将 ./filter-repo 替换为 .git/filter-repo,以便使其开箱即用。这是 git-filter-repo 现在默认放置结果的位置。 - FvD
    2
    实际上,如果你镜像了该仓库并且没有 .git 文件夹,并且帖子中描述的路径是正确的。 - Maverick1st

    3
    列出所有存在于旧提交中的文件。
    git rev-list HEAD | sed 1d | xargs -i git ls-tree -r {} --name-only | sort -u
    

    列出head中存在的所有文件。
    git ls-tree -r HEAD --name-only | sort -u
    

    获取在head中不存在的文件 (参考链接)。

    files=$(comm -23 <(git rev-list HEAD | sed 1d | xargs -i git ls-tree -r {} --name-only | sort -u) <(git ls-tree -r HEAD --name-only | sort -u))
    

    将不可见字符(我猜测是换行符)替换为空格,否则在git filter-branch中会导致错误。

    lostfiles=$(echo $files | sed -e 's/\s/ /g')
    

    从历史记录中删除 lostfiles

    git filter-branch -f --tree-filter "rm -rf ${lostfiles}" --prune-emtpy
    

    可以将它们组合成一个命令,但我不知道是否会有性能问题,因此我更喜欢单独的命令。


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