如何从Git存储库中的提交历史记录中删除/删除大文件?

有时,我将DVD-rip放入一个网站项目中,而后不当心git commit -a -m ... ,而后,zap的回购膨胀了2.2个演出。 下次我进行一些编辑,删除视频文件并提交全部内容,可是压缩文件仍在历史记录中。 html

我知道我能够从这些提交开始分支,并将一个分支从新创建到另外一个分支。 可是,我应该怎么作才能将2个提交合并在一块儿,以便大文件不显示在历史记录中,并在垃圾回收过程当中清除? java


#1楼

请注意,此命令可能具备很大的破坏性。 若是更多的人在回购上工做,他们都将不得不拉新的树。 若是您的目标不是减少大小,则不须要三个中间命令。 因为filter分支会建立已删除文件的备份,所以能够在其中保留很长时间。 git

$ 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

#2楼

git filter-branch --tree-filter 'rm -f path/to/file' HEAD对我来讲效果很好,尽管我遇到了与此处所述相同的问题,我经过遵循此建议解决了这个问题。 github

这本pro-git书包含一整章有关重写历史的内容 -看看filter-branch /从“每次提交中删除文件”部分。 shell


#3楼

这些命令在个人状况下有效: c#

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

与上述版本几乎没有什么不一样。 bash

对于那些须要将其推送到github / bitbucket的用户(我仅使用bitbucket进行了测试): less

# 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

#4楼

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

认真遵循使用说明 ,核心部分就是这样: ide

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

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

$ git gc --prune=now --aggressive

BFG一般比运行git-filter-branch至少快10-50倍,而且一般更易于使用。

彻底公开:我是BFG Repo-Cleaner的做者。


#5楼

若是您已将历史记录发布给其余开发人员,那么您想要作的就是极具破坏性的。 有关修复历史记录后的必要步骤,请参见git rebase文档中的“从上游Rebase恢复”

您至少有两个选择: git filter-branch和交互式rebase,这两个都在下面说明。

使用git filter-branch

我从Subversion导入中获取大量的二进制测试数据时遇到了相似的问题,并写了有关从git存储库中删除数据的信息

说您的git历史记录是:

$ 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

请注意, git lola是非标准但很是有用的别名。 使用--name-status开关,咱们能够看到与每次提交关联的树修改。

在“ Careless”提交(SHA1对象名称为ce36c98)中,文件oops.iso是偶然添加的DVD-rip,并在下一个提交cb14efd中删除。 使用上述博客文章中描述的技术,要执行的命令是:

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不管它在哪里找到,但并不是全部提交中都存在。 git rm --cached -f --ignore-unmatch oops.iso删除存在的DVD-rip,不然不会失败。
  • --tag-name-filter描述了如何重写标签名称。 cat的过滤器是身份操做。 您的存储库与上面的示例同样,可能没有任何标签,可是出于全面考虑,我包括了此选项。
  • --指定git filter-branch选项的结尾
  • --all如下--是全部裁判的简写。 像上面的示例同样,您的存储库可能只有一个ref(主文件),可是出于全面考虑,我包括了此选项。

通过一番搅拌,如今的历史是:

$ 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

请注意,新的“ Careless”提交仅添加other.html ,而“ Remove DVD-rip”提交再也不位于master分支上。 标为refs/original/refs/heads/master的分支包含您的原始提交,以防您出错。 要删除它,请按照“收缩存储库清单”中的步骤进行操做

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

对于更简单的选择,克隆存储库以丢弃不须要的位。

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

使用file:///...克隆URL复制对象而不是仅建立硬连接。

如今您的历史记录是:

$ 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对象名称保持不变,由于过滤操做未修改这些提交。 “ Careless”丢失了oops.iso ,“ Login page”有了新的父代,所以其SHA1 确实发生了变化。

互动基础

具备如下历史:

$ 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

您想从“无忧无虑”中删除oops.iso ,就好像您从未添加过它同样,而后“删除DVD-rip”对您毫无用处。 所以,咱们计划进行交互式基础调整的是保留“管理员页面”,编辑“无忧无虑”并丢弃“删除DVD-rip”。

运行$ git rebase -i 5af4522启动具备如下内容的编辑器。

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.
#

执行咱们的计划,咱们将其修改成

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

也就是说,咱们用“ Remove DVD-rip”删除该行,并将“ Careless”上的操做更改成edit而不是pick

保存退出编辑器后,在命令提示符处显示如下消息。

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

消息告诉咱们,咱们处于咱们要编辑的“ Careless”提交中,所以咱们运行两个命令。

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

第一个从索引中删除有问题的文件。 第二个将“ Careless”修改或修改成更新的索引,而且-C HEAD指示git重用旧的提交消息。 最后, git rebase --continue继续进行其他的rebase操做。

这提供了如下历史记录:

$ 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

这就是你想要的。

相关文章
相关标签/搜索