我不小心把一个dvd光盘放到了一个网站项目中,然后不小心提交-a -m…而且,快,回购膨胀了2.2 g。下次我做了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然在存储库中,在历史中。
我知道我可以从这些提交中启动分支,并将一个分支重置到另一个分支上。但是我应该怎么做才能合并两次提交,使大文件不显示在历史记录中,并在垃圾收集过程中被清理?
我不小心把一个dvd光盘放到了一个网站项目中,然后不小心提交-a -m…而且,快,回购膨胀了2.2 g。下次我做了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然在存储库中,在历史中。
我知道我可以从这些提交中启动分支,并将一个分支重置到另一个分支上。但是我应该怎么做才能合并两次提交,使大文件不显示在历史记录中,并在垃圾收集过程中被清理?
当前回答
请注意,这些命令可能非常具有破坏性。如果更多的人在回购,他们都将不得不拔出新的树。如果您的目标不是减小大小,那么中间的三个命令是不必要的。因为过滤器分支创建了已删除文件的备份,并且它可以在那里停留很长时间。
$ 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 --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 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是一个非标准但非常有用的别名。(详见答案末尾的附录)git日志的——name-status开关显示与每次提交相关的树修改。
在“粗心”提交(其SHA1对象名称为ce36c98)中,文件出错。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 removes commits that become empty (i.e., do not change the tree) as a result of the filter operation. In the typical case, this option produces a cleaner history. -d names a temporary directory that does not yet exist to use for building the filtered history. If you are running on a modern Linux distribution, specifying a tree in /dev/shm will result in faster execution. --index-filter is the main event and runs against the index at each step in the history. You want to remove oops.iso wherever it is found, but it isn’t present in all commits. The command git rm --cached -f --ignore-unmatch oops.iso deletes the DVD-rip when it is present and does not fail otherwise. --tag-name-filter describes how to rewrite tag names. A filter of cat is the identity operation. Your repository, like the sample above, may not have any tags, but I included this option for full generality. -- specifies the end of options to git filter-branch --all following -- is shorthand for all refs. Your repository, like the sample above, may have only one ref (master), but I included this option for full generality.
经过一番折腾,现在的历史是:
$ 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
注意,新的“粗心”提交只添加了other.html,而“Remove DVD-rip”提交不再在主分支上。标记为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
使用文件:///…克隆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
前两个提交(“Index”和“Admin page”)的SHA1对象名称保持不变,因为过滤操作没有修改这些提交。“粗心”输了。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
你想要移除。“粗心”中的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
正如消息告诉我们的那样,我们正处于想要编辑的“粗心”提交中,因此我们运行两个命令。
$ 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
这就是你想要的。
附录:通过~/.gitconfig启用git lola
引用康拉德·帕克的话:
我在Scott Chacon在linux.conf.au 2010上的演讲中学到的最好的技巧是:Git的高级技巧和窍门:
Lol = log -graph - decoration -pretty=oneline -commit
这提供了一个非常好的树图,显示了合并等分支结构。当然,有非常好的GUI工具来显示这样的图形,但git lol的优势在于它可以在控制台或ssh上工作,所以它对于远程开发或嵌入式板上的本地开发非常有用……
因此,只需将下面的代码复制到~/。Gitconfig为您的全彩git Lola行动: (别名) Lol = log -graph - decoration -pretty=oneline -commit Lola = log -graph - decoration -pretty=oneline -commit -all (颜色) 分支=自动 Diff =自动 交互=自动 状态= auto
你可以使用branch filter命令:
git filter-branch -tree-filter 'rm -rf path/to/your/file' HEAD
如果你知道你的提交是最近的,而不是遍历整个树,执行以下操作: git filter-branch -tree filter 'rm LARGE_FILE.zip' HEAD~10.
使用BFG Repo-Cleaner,这是一个更简单、更快的Git -filter-branch的替代方案,专门用于从Git历史记录中删除不需要的文件。
仔细按照使用说明,核心部分就是这样:
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
任何超过100MB大小的文件(不是最近提交的文件)都将从Git存储库的历史记录中删除。然后你可以使用git gc清除死数据:
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
修剪后,我们可以强制推送到远程repo*
$ git push --force
*注意:不能在GitHub上强制推送一个保护分支
BFG通常比运行git-filter-branch快10-50倍,而且通常更容易使用。
完全披露:我是好心眼巨人回收清理器的作者。