如何从 Git 的历史记录中永久删除提交?
团队中的一位开发人员不小心提交了一个 200 MB 的文件并将其推送到我们的 Git 服务器。几天后它被删除了,但历史记录在那里。我们的代码文件只有大约 75 MB,我们有 4 个分支。由于 200 MB 的文件提交,历史记录被保留,我们的项目文件夹(特别是隐藏的 .git 文件夹)的大小已经膨胀到接近 700 MB。如何从 git 中永久删除两个签入(提交大文件,删除大文件),就好像从未发生过一样?如果这很重要,我正在使用`TortoiseGit。
如何从 Git 的历史记录中永久删除提交?
团队中的一位开发人员不小心提交了一个 200 MB 的文件并将其推送到我们的 Git 服务器。几天后它被删除了,但历史记录在那里。我们的代码文件只有大约 75 MB,我们有 4 个分支。由于 200 MB 的文件提交,历史记录被保留,我们的项目文件夹(特别是隐藏的 .git 文件夹)的大小已经膨胀到接近 700 MB。如何从 git 中永久删除两个签入(提交大文件,删除大文件),就好像从未发生过一样?如果这很重要,我正在使用`TortoiseGit。
Github 有一个有用的页面如何从存储库中永久删除文件,简而言之:
$ 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 自行清理,可以在服务器上显式运行它:
$ ssh git server
$ cd /my/project/repo.git
$ git gc --prune=now
比较之前和之后的 repo 大小 - 确保它是您期望的大小。如果在未来的任何时候它恢复到更大的大小 - 有人已将已删除的提交推回存储库(需要再次执行所有步骤)。
如果有其他开发人员使用此存储库 - 他们将需要清理他们的结帐。否则,当他们从存储库中提取并推送他们的更改时,他们将添加回已删除的文件,因为它仍在其本地历史记录中。有两种方法可以避免:
第一个很简单,第二个意味着两件事之一:
$ 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
我建议您尝试BFG - 它不会删除这两个提交,但它会重写历史记录以从您的历史记录中删除庞大的文件。
仔细遵循 BFG 的使用说明——核心部分是这样的:
$ 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运行,然后用户克隆您的已清理存储库的新副本即可获得已清理的历史记录。
全面披露:我是 BFG Repo-Cleaner 的作者。
您可以使用 git filter-branch。请注意,这涉及历史重写,并且需要重新创建所有克隆。您可以在 Pro Git 书籍中找到对该主题的良好介绍。
正如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
如果是最近的提交,简单的方法是:
# 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 的答案,但这只是一种简单而干净的方法。