是否可以使用 `git filter-repo` 从 git 存储库中删除特定版本的文件?

问题描述

假设我有一个包含三个提交的小型 Git 存储库:

commit cccc:
    updated smile.png  (LFS)
    updated manual.md

commit bbbb:
    updated smile.png (LFS) <==== Don't want this specific one anymore!
    added manual.md

commit aaaa:
    added smile.png (LFS)
    added README.md
    added .gitattributes

我已经添加了 LFS 文件 smile.png 的 3 个不同版本,但我确定我不想或不需要中间版本再存在于我的存储库中。我不介意改变 git 历史。我还想缩小存储库的整体大小。

我知道 git filter-repo --path smile.png --invert-paths 可用于完全删除 smile.png 的所有实例和引用。但是,有没有办法从提交 bbbb删除特定版本,同时保留 aaaacccc 中的版本?

解决方法

Git-LFS 的使用给原本非常简单的事情增加了一个小问题。

可以“删除”提交 bbbb。为此,您还必须“删除”提交 cccc。我在这里将“删除”放在引号中,因为 Git 实际上并不删除提交。它只是将它们推到一边。它们会在您的存储库中保留一段时间,以便在您认为“删除”它们是错误的情况下取回它们。

它们保留多久——以及为什么——是一件有点复杂的事情,但默认情况下是将删除的提交保留至少 30 天。同时,在删除 cccc 时必须删除 bbbb 的原因很简单:每个提交取决于所有上一个提交的存在。所以你不能只是从链条中间撕下一个。你必须删除那个以及所有后续提交

这意味着要保留提交 cccc内容,您需要制作一个新的和改进的版本 cccc。替换的新鲜度是自动的:现有的提交不能改变,但新的提交总是可以添加的。提交的改进之处在于它包含您想要的快照——不管你选择如何安排——并且它链接回提交 aaaa。因此,在查看提交时,Git 现在将从最后一次提交 cccd(或任何其哈希 ID)开始并查看该提交,然后返回到 aaaa 并查看该提交,然后您'会看到你喜欢的历史。

git filter-branchgit filter-repo 都可以轻松完成此类手术。还有其他方法可以做同样的手术;在这种特殊情况下,只需复制一次提交,我们就可以使用 git commit-tree(制作新的和改进的 cccd)和 git reset(将分支名称移动到 find cccd),例如。在此处查看有关多个选项(git replace、提交树方法、BFG、过滤器分支、过滤器存储库等)的编辑历史记录的许多 StackOverflow 问题中的任何一个。

以下是关于使用 Git-LFS 的一些知识:当您在 Git-LFS 中添加并提交“大文件”时,LFS 软件已秘密将您的文件替换为“LFS 指针文件”(很小:通常远低于 1 KiB)。这意味着Git 根本不存储您的文件。 Git 存储这个 LFS 指针文件。 LFS 代码已经将您的文件存储在其他地方(在其他网站上),1 并使用指针文件来查找存储的文件。当您让 Git 检出某个特定提交时,Git-LFS 软件会拦截检出,注意到某些文件已被秘密替换为指针,然后前往 LFS 网站检索大文件。

当您重写历史记录时,您将进行一个新的提交 cccd,它具有与 cccc 完全相同的内容。这很好,因为 cccd 中的指针文件将是来自 cccc 的文件。所以 LFS 拦截器会用相同的更大的文件替换它。但是:commit bbbb 包含一个指向存储在other 网站上的某个文件的指针文件,其中保存了大文件。另一个网站不知道您永远也不会再次引用提交 bbbb2因此他们将保留大文件。

如果你想让他们摆脱大文件的 for-bbbb 版本,你需要一些其他的机制——一种完全在 Git 本身之外的机制——来摆脱它。这不是 Git 的任何部分都会做的事情。请注意,如果您专门使用 GitHub,您可能会在这里遇到一些问题:How to delete a file tracked by git-lfs and release the storage quota?


1这个“独立的网站”可以是主要的托管服务提供商网站,也可以是一个辅助站点,或者完全独立于某些托管服务提供商的网站。详细信息取决于您和您的 LFS 配置。

2假设,也就是说,您不改变主意并恢复提交bbbb