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

时间:2013-08-02 06:20:58

标签: git git-rewrite-history

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

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

5 个答案:

答案 0 :(得分:5)

从结帐中删除文件

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

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

队友

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

  1. 再次克隆
  2. 获取并重置
  3. 第一个非常简单,第二个意味着两件事之一:

    用户没有本地提交

    $ 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
    

答案 1 :(得分:3)

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

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

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

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

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

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

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

用实际文件名替换filename。再说一遍,正如forvaidya所说,这会重写整个回购历史,所以任何人在你做出这一改变之后都会收到错误。

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

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

答案 4 :(得分:0)

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

# 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

现在重新登记回购并检查大文件是否消失。在一个新的目录中这样做。

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的答案,但这只是一种简单而干净的方法。