我不小心将 DVD-rip 放到了一个网站项目中,然后不小心git commit -a -m ...
,然后,zap 的回购膨胀了 2.2 个演出。下次我进行一些编辑,删除视频文件并提交所有内容,但压缩文件仍在历史记录中。
我知道我可以从这些提交开始分支,并将一个分支重新建立到另一个分支。但是,我应该怎么做才能将 2 个提交合并在一起,以便大文件不显示在历史记录中,并在垃圾回收过程中清除?
使用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 通常比运行git-filter-branch
至少快 10-50倍,并且通常更易于使用。
完全公开:我是 BFG Repo-Cleaner 的作者。
如果您已将历史记录发布给其他开发人员,那么您想要做的就是极具破坏性的。有关修复历史记录后的必要步骤,请参见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
这就是你想要的。
为什么不使用这个简单但功能强大的命令?
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
--tree-filter
选项在每次检出项目后运行指定的命令,然后重新提交结果。在这种情况下,请从每个快照中删除一个名为 DVD-rip 的文件,无论该文件是否存在。
如果您知道哪个提交引入了巨大的文件(例如 35dsa2),则可以将 HEAD 替换为 35dsa2..HEAD 以避免重写过多的历史记录,从而避免在尚未推送的情况下分散提交。 @ alpha_989 提供的此评论似乎太重要了,不能在此处忽略。
请参阅此链接。