How to remove/delete a large file from commit history in Git repository?
偶尔我会在一个网站项目中插入一个DVD翻录,然后不小心地删除了
我知道我可以从这些提交中启动分支,并将一个分支重新设置为另一个分支。但是,我应该怎么做才能将2个提交合并在一起,从而使大文件不显示在历史记录中,并在垃圾收集过程中被清除?
如果您已经向其他开发人员发布了历史记录,那么您想要做的是高度破坏性的工作。有关修复历史记录后的必要步骤,请参阅
您至少有两个选项:
我有一个类似的问题,从Subversion导入大量的二进制测试数据,并写了关于从Git存储库中删除数据的内容。
假设你的Git历史是:
1 2 3 4 5 6 7 8 9 10 11 12 | $ git lola --name-status * f772d66 (HEAD, master) Login page | A login.html * cb14efd Remove DVD-rip | D oops.iso * ce36c98 Careless | A oops.iso | A other.html * 5af4522 Admin page | A admin.html * e738b63 Index A index.html |
注意,
在"粗心"提交(sha1对象名为ce36c98)中,文件
1 2 3 | git filter-branch --prune-empty -d /dev/shm/scratch \ --index-filter"git rm --cached -f --ignore-unmatch oops.iso" \ --tag-name-filter cat -- --all |
选项:
--prune-empty 删除由于筛选器操作而变为空(即不更改树)的提交。在典型情况下,此选项会生成更清晰的历史记录。-d 命名了一个临时目录,该目录还不存在,无法用于构建筛选的历史记录。如果您运行的是现代Linux发行版,那么在/dev/shm 中指定树将导致更快的执行速度。--index-filter 是历史上最主要的事件,在历史的每一步都与指数背道而驰。您希望在找到oops.iso 的地方删除它,但它并不存在于所有提交中。当DVD翻录出现时,命令git rm --cached -f --ignore-unmatch oops.iso 将删除该翻录,否则不会失败。--tag-name-filter 描述了如何重写标记名。cat 的过滤器是身份操作。与上面的示例一样,您的存储库可能没有任何标记,但出于完全通用性考虑,我包含了这个选项。-- 规定了git filter-branch 期权的终止。-- 后面的--all 是所有refs的简写。像上面的示例一样,您的存储库可能只有一个引用(master),但出于完全通用性考虑,我包含了这个选项。
经过一番翻腾,现在的历史是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ git lola --name-status * 8e0a11c (HEAD, master) Login page | A login.html * e45ac59 Careless | A other.html | * f772d66 (refs/original/refs/heads/master) Login page | | A login.html | * cb14efd Remove DVD-rip | | D oops.iso | * ce36c98 Careless |/ | A oops.iso | A other.html * 5af4522 Admin page | A admin.html * e738b63 Index A index.html |
注意,新的"粗心"提交只添加了
1 2 3 | $ git update-ref -d refs/original/refs/heads/master $ git reflog expire --expire=now --all $ git gc --prune=now |
为了更简单的选择,克隆存储库以丢弃不需要的位。
1 2 3 | $ cd ~/src $ mv repo repo.old $ git clone file:///home/user/src/repo.old repo |
使用
现在你的历史是:
1 2 3 4 5 6 7 8 9 | $ git lola --name-status * 8e0a11c (HEAD, master) Login page | A login.html * e45ac59 Careless | A other.html * 5af4522 Admin page | A admin.html * e738b63 Index A index.html |
前两次提交的sha1对象名("index"和"admin page")保持不变,因为筛选操作没有修改这些提交。"粗心"丢失了
有以下历史:
1 2 3 4 5 6 7 8 9 10 11 12 | $ git lola --name-status * f772d66 (HEAD, master) Login page | A login.html * cb14efd Remove DVD-rip | D oops.iso * ce36c98 Careless | A oops.iso | A other.html * 5af4522 Admin page | A admin.html * e738b63 Index A index.html |
你想从"粗心"中删除
运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | pick ce36c98 Careless pick cb14efd Remove DVD-rip pick f772d66 Login page # Rebase 5af4522..f772d66 onto 5af4522 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like"squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # |
执行我们的计划,我们将其修改为
1 2 3 4 5 | edit ce36c98 Careless pick f772d66 Login page # Rebase 5af4522..f772d66 onto 5af4522 # ... |
也就是说,我们删除了带有"删除DVD翻录"的行,将"粗心"的操作改为
保存退出编辑器会在命令提示下显示以下消息。
1 2 3 4 5 6 7 8 | Stopped at ce36c98... Careless You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue |
正如消息告诉我们的,我们正在"粗心"提交我们想要编辑的内容,所以我们运行两个命令。
1 2 3 | $ git rm --cached oops.iso $ git commit --amend -C HEAD $ git rebase --continue |
第一个从索引中删除有问题的文件。第二个修改或修改"粗心"为更新的索引,
这提供了以下历史:
1 2 3 4 5 6 7 8 9 | $ git lola --name-status * 93174be (HEAD, master) Login page | A login.html * a570198 Careless | A other.html * 5af4522 Admin page | A admin.html * e738b63 Index A index.html |
这就是你想要的。
使用bfg repo cleaner,这是一种比
仔细遵循使用说明,核心部分如下:
1 | $ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git |
任何大小超过100MB的文件(不在最新提交的文件中)都将从Git存储库的历史记录中删除。然后可以使用
1 | $ git gc --prune=now --aggressive |
bfg通常比运行
完全公开:我是bfg repo cleaner的作者。
为什么不使用这个简单但强大的命令呢?
1 | git filter-branch --tree-filter 'rm -f DVD-rip' HEAD |
请参见此链接。
(我看到的这个问题的最佳答案是:https://stackoverflow.com/a/42544963/714112,因为这个线程在谷歌搜索排名中显示得很高,但另一个没有。)
?一个极快的壳牌一号班轮??此shell脚本显示存储库中的所有blob对象,从最小到最大排序。
对于我的样本回购,它比这里发现的其他回购快100倍。在我信任的AthlonIIX4系统上,它只需一分钟多的时间就可以处理带有5622155个对象的Linux内核存储库。
基础脚本1 2 3 4 5 6 | 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 |
当您运行上面的代码时,您将得到很好的可读输出,如下所示:
1 2 3 4 | ... 0d99bb931299 530KiB path/to/some-image.jpg 2ba44098e28f 12MiB path/to/hires-image.png bd1741ddce0d 63MiB path/to/some-video-1080p.mp4 |
??快速删除文件??
然后,假设您想从每个可从
1 | git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD |
这些命令适用于我的情况:
1 2 3 4 5 | 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测试过这个):
1 2 3 4 5 6 7 8 | # 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 |
在尝试了So中几乎所有的答案之后,我终于找到了这个gem,它可以快速删除和删除存储库中的大型文件,并允许我再次同步:http://www.zyxware.com/articles/4027/how-to-delete-files-permanently-from-your-local-and-remote-git-stores
CD到本地工作文件夹并运行以下命令:
1 | git filter-branch -f --index-filter"git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all |
将foldername替换为要从给定Git存储库中删除的文件或文件夹。
完成后,运行以下命令清理本地存储库:
1 2 3 4 | rm -rf .git/refs/original/ git reflog expire --expire=now --all git gc --prune=now git gc --aggressive --prune=now |
现在将所有更改推送到远程存储库:
1 | git push --all --force |
这将清理远程存储库。
pro-git这本书有一整章都是关于重写历史的——看看
请注意,这个命令可能非常具有破坏性。如果更多的人在回购业务上工作,他们都将不得不拔出新的树。如果您的目标不是缩小大小,则不需要使用三个中间命令。因为过滤器分支会创建一个已删除文件的备份,并且它可以在那里停留很长时间。
1 2 3 4 5 | $ 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 |
如果您知道您的提交是最近的,而不是浏览整个树,请执行以下操作:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
我在一个bitback帐户中遇到了这个问题,在那里我意外地存储了大量的*.jpa备份。
重新调整
来源:http://naleid.com/blog/2012/01/17/finding-and-purging-big-files-from-git-history
您可以使用
我基本上是这样回答的:https://stackoverflow.com/a/11032521/1286423
(对于历史,我会复制粘贴在这里)
1 2 3 4 5 | $ 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无法删除对这些文件的引用,因为在指向这些文件的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 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(
当你遇到这个问题时,
更糟的是,重新定位也不容易,因为对blob的任何引用都会阻止git垃圾收集器清理空间。这包括远程引用和重新记录引用。
我把
一旦你的blob完全没有引用,
使用非常简单的
Completely remove a file from a git repository with git forget-blob
多亏了堆栈溢出和一些博客条目的回答,我把这些放在一起。他们的功劳!
使用git扩展,这是一个用户界面工具。它有一个名为"查找大文件"的插件,可以在存储库中查找大文件并允许永久删除它们。
在使用此工具之前不要使用"git filter branch",因为它将找不到"filter branch"删除的文件(尽管"filter branch"不会从存储库包文件中完全删除文件)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 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 -rm test # Push it with force $ git push --force origin master |