906

我不小心把一张 DVD 翻录到一个网站项目中,然后不小心git commit -a -m ...,然后,这个 repo 被 2.2 gigs 膨胀了。下次我进行了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然存在于存储库中,在历史记录中。

我知道我可以从这些提交开始分支并将一个分支重新定位到另一个分支。但是我应该怎么做才能合并 2 个提交,以便大文件不会显示在历史记录中并在垃圾收集过程中被清理?

4

21 回答 21

732

使用BFG Repo-Cleaner,这是一种更简单、更快的替代方案,git-filter-branch专门用于从 Git 历史记录中删除不需要的文件。

仔细按照使用说明进行操作,核心部分就是这样:

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

任何超过 100MB 的文件(不在您的最新提交中)都将从您的 Git 存储库的历史记录中删除。然后,您可以使用git gc清除死数据:

$ git gc --prune=now --aggressive

BFG 通常至少比running快10-50 倍git-filter-branch,并且通常更易于使用。

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

于 2013-07-26T20:15:38.153 回答
648
于 2010-01-28T21:55:32.550 回答
258

为什么不使用这个简单而强大的命令呢?

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

--tree-filter选项在每次签出项目后运行指定的命令,然后重新提交结果。在这种情况下,您从每个快照中删除一个名为 DVD-rip 的文件,无论它是否存在。

如果您知道哪个提交引入了大文件(例如 35dsa2),则可以将 HEAD 替换为 35dsa2..HEAD 以避免重写太多历史记录,从而避免在尚未推送时出现分歧提交。@alpha_989 的这条评论似乎太重要了,不能在这里省略。

请参阅此链接

于 2015-05-16T09:44:10.450 回答
123

(我看到的这个问题的最佳答案是:https ://stackoverflow.com/a/42544963/714112 ,复制到这里,因为这个帖子在谷歌搜索排名中看起来很高,但另一个没有)

超快的外壳单线

此 shell 脚本显示存储库中的所有 blob 对象,从小到大排序。

对于我的示例存储库,它的运行速度比此处找到的其他存储库快约100 倍。
在我信赖的 Athlon II X4 系统上,它只需一分钟多一点就可以处理包含 5,622,155 个对象的Linux 内核存储库

基本脚本

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

当你运行上面的代码时,你会得到很好的人类可读的输出,如下所示:

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

快速文件删除

假设您随后想要删除文件a,并且b从 可访问的每个提交中HEAD,您可以使用以下命令:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
于 2017-10-07T00:37:00.550 回答
85

在尝试了 SO 中的几乎所有答案之后,我终于找到了这个可以快速删除并删除我的存储库中的大文件并允许我再次同步的 gem:http ://www.zyxware.com/articles/4027/how-to-delete -files-permanently-from-your-local-and-remote-git-repositories

CD 到您的本地工作文件夹并运行以下命令:

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

将 FOLDERNAME 替换为您希望从给定 git 存储库中删除的文件或文件夹。

完成此操作后,运行以下命令来清理本地存储库:

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

现在将所有更改推送到远程存储库:

git push --all --force

这将清理远程存储库。

于 2017-04-26T17:35:48.490 回答
47

比 git filter-branch 快 100 倍并且更简单

此线程中有很好的答案,但同时其中许多已过时。git-filter-branch不再推荐使用,因为它在大型存储库上很难使用并且速度非常慢。

git-filter-repo使用起来更快更简单。

git-filter-repo是一个 Python 脚本,在 github 上可用:https ://github.com/newren/git-filter-repo 。安装后,它看起来像一个常规的 git 命令,可以通过git filter-repo.

您只需要一个文件:Python3 脚本 git-filter-repo。将其复制到 PATH 变量中包含的路径。在 Windows 上,您可能必须更改脚本的第一行(请参阅 INSTALL.md)。您需要在系统上安装 Python3,但这没什么大不了的。

首先你可以运行

git filter-repo --analyze

这可以帮助您确定下一步要做什么。

您可以在任何地方删除您的 DVD-rip 文件:

git filter-repo --invert-paths --path-match DVD-rip
 

Filter-repo 真的很快。filter-branch 在我的计算机上花费了大约 9 个小时的任务,通过 filter-repo 在 4 分钟内完成。你可以用 filter-repo 做更多的好事。请参阅文档。

警告:在存储库的副本上执行此操作。filter-repo 的许多操作都无法撤消。filter-repo 将更改所有已修改提交(当然)及其所有后代的提交哈希,直到最后一次提交!

于 2020-05-04T22:56:00.410 回答
42

这些命令在我的情况下有效:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

它与上述版本略有不同。

对于那些需要把它推送到 github/bitbucket 的人(我只用 bitbucket 测试过):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work
于 2013-06-14T02:35:36.637 回答
15

根据 GitHub Documentation,只需按照以下步骤操作:

  1. 摆脱大文件

选项 1:您不想保留大文件:

rm path/to/your/large/file        # delete the large file

选项 2:您希望将大文件保存到未跟踪的目录中

mkdir large_files                       # create directory large_files
touch .gitignore                        # create .gitignore file if needed
'/large_files/' >> .gitignore           # untrack directory large_files
mv path/to/your/large/file large_files/ # move the large file into the untracked directory
  1. 保存您的更改
git add path/to/your/large/file   # add the deletion to the index
git commit -m 'delete large file' # commit the deletion
  1. 从所有提交中删除大文件
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch path/to/your/large/file" \
  --prune-empty --tag-name-filter cat -- --all
git push <remote> <branch>
于 2020-09-10T13:37:23.083 回答
12

我使用 bitbucket 帐户遇到了这个问题,我不小心在其中存储了我网站的大量 *.jpa 备份。

git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all

MY-BIG-DIRECTORY有问题的文件夹相对,以完全重写您的历史记录(包括标签)。

来源:https ://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/

于 2014-08-31T19:33:54.787 回答
10

请注意,此命令可能非常具有破坏性。如果有更多的人在进行回购,他们都必须拔出新树。如果您的目标不是减小大小,则不需要三个中间命令。因为过滤器分支创建了被删除文件的备份,并且它可以在那里保留很长时间。

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
于 2012-06-14T11:53:46.350 回答
9
于 2012-10-25T12:24:36.620 回答
8

如果您知道您的提交是最近的,而不是遍历整个树,请执行以下操作: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD

于 2016-01-01T06:21:33.863 回答
6

这会将其从您的历史记录中删除

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
于 2020-01-14T14:57:51.337 回答
5

使用Git Extensions,它是一个 UI 工具。它有一个名为“查找大文件”的插件,该插件可以在存储库中查找大型文件并允许永久删除它们。

在使用此工具之前不要使用 'git filter-branch',因为它无法找到被 'filter-branch' 删除的文件(尽管 'filter-branch' 不会从存储库包文件中完全删除文件) .

于 2016-12-31T13:22:11.583 回答
5

我基本上做了这个答案: https ://stackoverflow.com/a/11032521/1286423

(对于历史,我将在这里复制粘贴)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

它没有用,因为我喜欢重命名和移动很多东西。因此,一些大文件位于已重命名的文件夹中,我认为 gc 无法删除对这些文件的引用,因为引用了tree指向这些文件的对象。我真正杀死它的最终解决方案是:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

我的 repo (the .git) 从 32MB 变为 388KB,甚至 filter-branch 都无法清理。

于 2017-06-05T10:21:47.667 回答
4

git filter-branch是一个强大的命令,您可以使用它从提交历史记录中删除一个大文件。该文件将保留一段时间,Git 将在下一次垃圾回收中将其删除。以下是从提交历史中删除文件的完整过程。为了安全起见,下面的过程首先在新分支上运行命令。如果结果是您需要的,则将其重置回您实际想要更改的分支。

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master
于 2019-03-28T10:05:55.363 回答
2

您可以使用以下branch filter命令执行此操作:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD

于 2017-04-05T23:28:30.750 回答
1

当你遇到这个问题时,git rm这还不够,因为 git 记得该文件在我们的历史中存在过一次,因此会保留对它的引用。

更糟糕的是,变基也不容易,因为对 blob 的任何引用都会阻止 git 垃圾收集器清理空间。这包括远程引用和 reflog 引用。

我整理了git forget-blob一个尝试删除所有这些引用的小脚本,然后使用 git filter-branch 重写分支中的每个提交。

一旦你的 blob 完全没有被引用,git gc就会摆脱它

用法很简单git forget-blob file-to-forget。你可以在这里获得更多信息

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

感谢 Stack Overflow 的回答和一些博客文章,我把这些放在一起。归功于他们!

于 2017-01-23T12:21:46.803 回答
0

除了git filter-branch(缓慢但纯粹的 git 解决方案)和BFG(更简单且非常高效)之外,还有另一个过滤工具具有良好的性能:

https://github.com/xoofx/git-rocket-filter

从它的描述来看:

git-rocket-filter 的目的与命令类似,git-filter-branch同时提供以下独特功能:

  • 快速重写提交和树(按 x10 到 x100 的顺序)。
  • 内置支持使用 --keep(保留文件或目录)的白名单和使用 --remove 选项的黑名单。
  • 使用类似 .gitignore 的模式进行树过滤
  • 用于提交过滤和树过滤的快速简便的 C# 脚本
  • 支持按文件/目录模式在树过滤中编写脚本
  • 自动修剪空/未更改的提交,包括合并提交
于 2019-10-10T11:25:32.377 回答
-1
git reset --soft HEAD~1

它将保留更改但删除提交,然后您可以重新提交这些更改。

于 2021-12-23T11:23:37.553 回答
-2

这对我来说非常有效:在 git 扩展中:

右键单击选定的提交:

将当前分支重置到这里:

硬重置;

令人惊讶的是,没有其他人能够给出这个简单的答案。

将当前分支重置到这里

硬重置

于 2020-06-26T09:52:35.623 回答