从 Git 历史记录中删除大文件

Posted

技术标签:

【中文标题】从 Git 历史记录中删除大文件【英文标题】:Removing big files from Git history 【发布时间】:2020-03-20 12:55:06 【问题描述】:

我已经阅读了多个建议使用 filter-branch 或 BFG 来完成此任务的答案,但我觉得我需要进一步的建议,因为我的情况有点特殊。

我必须管理两个存储库,一个基本上是另一个的克隆,理想情况下,我希望每天将更改从源提取到克隆中。但是,原始存储库在其历史记录中包含非常大的文件,超出了 Github 的大小限制。所以我必须删除这些文件,但与此同时,除了对这些特定文件的更改之外,我不想损害现有的提交历史记录。据我了解,BFG 对历史记录进行了完全重写,这会让 Github 误以为所有现有文件都被删除并重新创建为新文件,而 filter-branch 并没有这样做,但相比之下它也非常慢,而且我的存储库非常大,大约有 100000 次提交......

所以我正在尝试找出解决此问题的最佳方法。我应该在某些时候使用 BFG,并且简单地接受由于它的修改我会看到荒谬的拉取请求,或者我应该以某种方式使用 filter-branch 吗? 澄清一下,只有 3 个文件是造成这种不满的原因。

【问题讨论】:

如果要删除这些文件,则无法重写 git 历史记录。 Git 有一些其他工具可以处理大型存储库,您也许可以使用其中的一些。你可以使用git clone --depth 克隆一个 repo 的一部分。您还可以使用git clone --reference,使用本地存储库进行克隆以加快传输速度。注意--reference 有一些非常特殊的边缘情况,所以在使用它之前仔细阅读它,以确定它是否是你想要的。 是的,我之前读过git clone --depthgit pull --depth,但未能完全理解它们。如果我只提取这些肤浅的更改,这是否有助于我避免放置这些不再存在的大型历史文件? 是的。 --depth 标志用于减少服务器上的负载,因此它不会下载给定深度的提交不需要的任何内容。如果您的大文件的历史比深度更早,它们将不会被下载。 听起来不错。我会尝试这样做,谢谢。 【参考方案1】:

Git 中的提交历史只不过是提交。

任何提交都不能更改。因此,对于 anything 从某个现有提交中删除一个大文件,那个东西——无论是 BFG,还是git filter-branch,或者git filter-repo,或者其他——将不得不提取一个“坏”提交,进行一些更改(例如,删除大文件),并进行新的和改进的替代提交。

这其中可怕的部分是每个后续提交都以不可更改的方式对bad提交的原始哈希ID进行编码。错误提交的直接子代将其编码为它们的父哈希。因此,您(或工具)必须将 那些 提交复制到新的和改进的提交中。他们的改进之处在于他们缺少大文件并且引用他们刚刚为最初的错误提交所做的替换。

当然,他们的孩子将他们的哈希 ID 编码为父哈希 ID,因此现在该工具必须复制这些提交。这一直重复到每个分支中的 last 提交,由分支名称标识:

...--o--o--x--o--o--o   [old, bad version of branch]
         \
          ●--●--●--●   <-- branch

x 是错误的提交:x 必须复制到第一个新的和改进的,但随后所有后续提交也必须复制。

作为不同提交的副本具有不同的哈希 ID。 每个克隆现在必须放弃“坏”提交——x 一个及其所有后代——转而支持新的和改进的提交。

所有这些存储库编辑工具都应努力进行最小的更改。 BFG 可能是最快和最方便使用的,但git filter-branch 可以被告知复制所有不良和后代提交并使用--index-filter,这是最快的(仍然很慢!) 筛选。为此,请使用:

git filter-branch --index-filter <command> -- <hash>..branch1 <hash>..branch2 ...

&lt;command&gt; 是一个适当的"git rm --cached --ignore-unmatch" 命令(请务必引用整个内容),&lt;hash&gt; 和分支名称指定要复制的提交。请记住,A..B 语法意味着不要查看提交 A 或更早,而查看提交 B 和更早所以如果提交 x 是,比如说,deadbeefbadf00d...,你'会想要使用其 parent 的哈希作为限制器:

git filter-branch --index-filter "..." -- deadbeefbadf00d^..master

例如(使用正确的删除命令填写... 部分)。

(注意:我实际上并没有使用过 BFG,但如果它不必要地重新复制提交,那真的很糟糕,我敢打赌它不会。)

【讨论】:

因此,如果正确理解这一点,如果这些文件在两个月前作为主分支的一部分存在,这自动意味着从那时起所有提交都必须重写为新提交......?这意味着无论我使用 BFG 还是 filter-branch,重写的数量最终都是一样的? 差不多,是的。 (但请注意,“从那时起”与 graph 中的祖先/后代关系特别相关。例如,如果您有一个旧版本,您一直在修补,并且您修补了它,并且that 分支上的所有提交都没有大文件,提交是否是昨天并不重要:重要的是提交及其父级和祖父级,等等随着时间的推移从来没有有过大文件。) 好的,感谢您为我解决这个问题...我想我会尝试使用 MrBerta 建议的 --depth 选项,因为我不介意丢失原始存储库中的历史记录。

以上是关于从 Git 历史记录中删除大文件的主要内容,如果未能解决你的问题,请参考以下文章

从GIT历史记录中删除文件

如何从 Git 历史记录中永久删除提交?

Git如何删除历史记录中的大文件详解

从 git 存储库中删除文件(历史记录)

sh 此脚本将帮助您从git repo历史记录中删除大文件,并缩小存储库的大小。

从远程 git 历史记录中删除我在本地没有的提交