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

Posted

技术标签:

【中文标题】如何从 Git 历史记录中永久删除提交?【英文标题】:How to permanently delete a commit from Git's history? 【发布时间】:2013-08-03 07:40:53 【问题描述】:

团队中的一位开发人员不小心提交了一个 200 MB 的文件并将其推送到我们的 Git 服务器。几天后它被删除了,但历史记录在那里。我们的代码文件只有大约 75 MB,我们有 4 个分支。由于 200 MB 的文件提交,历史记录被保留,我们的项目文件夹(特别是隐藏的 .git 文件夹)的大小已经膨胀到接近 700 MB。如何从 git 中永久删除两个签入(提交大文件,删除大文件),就好像从未发生过一样?如果这很重要,我正在使用 `TortoiseGit。

【问题讨论】:

How to remove/delete a large file from commit history in Git repository?的可能重复 【参考方案1】:

你可以使用 git filter-branch。请注意,这涉及历史重写,所有克隆都需要重新创建。你可以在 Pro Git 书中找到一个很好的 introduction to the topic。

【讨论】:

【参考方案2】:

正如 forvaidya 建议的那样,git filter-branch 是要走的路。具体来说,在您的情况下,您可以执行以下命令从 repo 的历史记录中删除该文件:

git filter-branch --tree-filter 'rm -f filename' HEAD

用实际文件名替换filename。同样,正如 forvaidya 所说,这会重写 repo 的整个历史记录,因此在您进行此更改后拉取的任何人都会收到错误。

编辑:出于性能原因,实际上最好使用 Git 的 rm 命令:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD

【讨论】:

【参考方案3】:

从结帐中删除文件

Github 有一个 useful page 如何从存储库中永久删除文件,简而言之:

$ git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch 200MB-filename' \
  --prune-empty --tag-name-filter cat -- --all
$ git push --all -f

这将从所有分支中删除该文件。然后在本地恢复空间:

$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now

在 git 服务器上恢复空间

强制推送不会删除远程服务器上的任何提交/对象。如果您不想等待 git 自行清理,可以在服务器上显式运行它:

$ ssh git server
$ cd /my/project/repo.git
$ git gc --prune=now

比较之前和之后的 repo 大小 - 确保它是您期望的大小。如果在未来的任何时候它恢复到更大的大小 - 有人已将已删除的提交推回存储库(需要再次执行所有步骤)。

队友

如果有其他开发人员使用此存储库 - 他们将需要清理他们的结帐。否则,当他们从存储库中提取并推送他们的更改时,他们将添加 back 已删除的文件,因为它仍然在他们的本地历史记录中。有两种方法可以避免这种情况:

    再次克隆 获取并重置

第一个很简单,第二个意思是两件事之一:

用户没有本地提交

$ git fetch
$ git reset origin/master -hard

这将使任何本地结帐与远程结帐完全匹配

用户确实有本地提交

$ git fetch
$ git rebase -i origin/master

用户需要确保他们没有任何引用删除文件的本地提交 - 否则他们会将其添加回存储库。

用户清理

然后(可选,因为 git 不会将未引用的提交推送到服务器)恢复空间,每个人都有一个一致的更苗条的存储库状态:

$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now

【讨论】:

【参考方案4】:

我建议您尝试 The BFG - 它不会删除这两个提交,但它重写历史记录以摆脱您的庞大文件历史。

仔细关注 BFG 的usage instructions - 核心部分就是这样:

$ java -jar bfg.jar  --strip-blobs-bigger-than 100M  my-repo.git

它在大型存储库上也比 git-filter-branch 快得多 - 你可能会发现这个速度比较视频很有趣 - BFG 在 Raspberry Pi 上运行,git-filter-branch 在四核 Mac OS X 机器上运行...... http://youtu.be/Ir4IHzPhJuI ...哪个会更快!?

请注意,在清理之后,您应该运行 git gc 以让 Git 识别它不再需要存储这些大对象并释放存储库副本中的磁盘空间。 git gc 通常在大多数托管版本的 Git 上定期发生,所以当你将清理的历史推送到你的主 Git 服务器时,该服务器最终也会释放它的磁盘空间。也许令人惊讶的是,您不必等待 git gc 运行,然后用户克隆您已清理的 repo 的新副本即可获得已清理的历史记录。

全面披露:我是 BFG Repo-Cleaner 的作者。

【讨论】:

虽然 BFG 本身不会恢复与 BFG 删除的历史条目相关的空间,但其 documentation 表示运行 git gc 之后 BFG 将:摘录:“BFG 将更新您的提交以及所有分支和标签,因此它们是干净的,但它不会物理删除不需要的东西。检查 repo 以确保您的历史记录已更新,并且然后使用标准的git gc 命令去除不需要的脏数据,Git 现在会将其识别为超出要求:"(有关实际命令行,请参见 BFG 文档。) 哇,在查看您的个人资料时,我才发现您是 BFG 的作者。 :-) 我是否正确解释了 BFG 文档,git gc 实际上确实恢复了先前分配给 BFG 从回购历史中删除的文件的空间? @DavidRR 您对 BFG 文档所说的内容是正确的 - 是的,您应该运行 git gc!我已经更新了我的问题以给出我的一些标准免责声明......令人惊讶的是,重写 Git 历史的过程有多少种方式会偏离轨道,只要人们关注rtyley.github.io/bfg-repo-cleaner/#usage 他们应该没问题......见@ 987654327@ 进行稍长的讨论! 感谢您的澄清和创建如此有用的工具。我已投票决定将此问题作为this one 的副本结束,您有highly voted and similar answer。【参考方案5】:

如果是最近的提交,简单的方法是:

# check how many MB your .git dir is before you start
du -m -d0 .git

# rebase to remove the commits with large files
git rebase -i HEAD~2 # or however many commits you need to go back

# force push to remote origin
git push -f origin HEAD

现在重新克隆 repo 并检查大文件是否消失。在新目录中执行此操作。

git clone <url> <new dir>

# check MB of .git dir (should be smaller by the size of the large file)
du -m -d0 .git

如果成功,那么其他开发人员回到正轨的最干净的方法是重新克隆到新目录并手动应用他们正在进行的工作。如果 .git 大小没有减少,请检查是否有标签或任何引用违规提交的内容。您还必须从源中删除任何引用提交的标签。

对于更复杂的情况,你可以试试 AD7six 的答案,但这只是一种简单而干净的方法。

【讨论】:

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

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

git删除所有提交历史记录

git删除所有历史提交记录

如何删除 Git 仓库中的历史提交记录

从存储库历史记录中删除提交 [重复]

删除 git 历史记录中包含合并的特定提交