概述
本教程将介绍如何使用各种工具从git仓库的提交历史中删除大文件。
2. 使用 git filter-branch
这是最常用的方法,它帮助我们重写已提交分支的历史。
例如,假设我们在项目文件夹中意外地删除了一个blob文件,删除后仍然可以在git历史中看到它:
$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 9e87646 (HEAD -> master) blob file removed
* 2583677 blob file
* 34ea256 my first commit
我们可以使用以下命令从git历史中移除blob文件,通过重写树及其内容:
$ git filter-branch --tree-filter 'rm -f blob.txt' HEAD
这里,-rm
选项从树中移除文件。另外,-f
选项在项目中的其他已提交目录中找不到该文件时,防止命令失败。如果没有 -f
选项,当我们的项目中有多个目录时,命令可能会失败。
运行该命令后的git日志如下:
* 8f39d86 (HEAD -> master) blob file removed
* e99a81d blob file
| * 9e87646 (refs/original/refs/heads/master) blob file removed
| * 2583677 blob file
|/
* 34ea256 my first commit
我们可以用提交历史的SHA1键替换HEAD,以最小化重写。
尽管如此,我们的git日志仍然包含对已删除文件的引用。我们可以更新仓库来删除这个引用:
$ git update-ref -d refs/original/refs/heads/master
-d
选项在确认仍然包含旧值后删除命名的引用。
我们需要在仓库中记录引用的更改:
$ git reflog expire --expire=now --all
expire
子命令删除较旧的引用日志条目。
最后,我们需要清理并优化仓库:
$ git gc --prune=now
--prune=now
选项会忽略年龄,修剪松散对象。
运行命令后,我们的git日志如下:
* 6f49d86 (HEAD -> master) my first commit
我们可以看到引用已经被移除。
另一种选择是运行:
$ git filter-branch --index filter 'git rm --cached --ignore-unmatched blob.txt' HEAD
这与tree-filter
的工作方式相同,但因为它只重写索引(即工作目录),所以速度更快。--ignore-unmatched
选项可以防止在项目中的其他已提交目录中找不到文件时命令失败。
需要注意的是,当删除大文件时,使用两个不同命令的方法可能会很慢。
3. 使用 git-filter-repo
另一种方法是使用第三方插件 git-filter-repo
。它比其他方法更易于使用,且速度更快。此外,它是git官方文档中推荐的解决方案:git-filter-branch。
3.1. 安装
最低要求是Python 3.5及以上,Git 2.22.0及以上;部分功能需要Git 2.24.0或更高版本。
要在Linux机器上安装 git-filter-repo
,可以参考文档。以下是安装步骤:
$ sudo apt install python3-pip
$ pip install --user git-filter-repo
或者,可以使用以下命令安装 git-filter-repo
:
# Add to bashrc.
export PATH="${HOME}/bin:${PATH}"
mkdir -p ~/bin
wget -O ~/bin/git-filter-repo https://raw.githubusercontent.com/newren/git-filter-repo/7b3e714b94a6e5b9f478cb981c7f560ef3f36506/git-filter-repo
chmod +x ~/bin/git-filter-repo
3.2. 删除文件
首先,让我们查看git日志:
$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* ee36517 (HEAD -> master) blob.txt removed
* a480073 project folder
接下来,我们需要分析仓库:
$ git filter-repo --analyze
Processed 5 blob sizes
Processed 2 commits
Writing reports to .git/filter-repo/analysis...done.
这将生成一个目录报告我们的仓库状态。报告位于.git/filter-repo/analysis
。这些信息可能有助于确定后续运行中要过滤的内容,也可以帮助我们确认之前的过滤命令是否按预期执行。
然后,我们可以使用--path-match
选项运行此命令,指定要包含在过滤历史中的文件:
$ git filter-repo --force --invert-paths --path-match blob.txt
这是新的git日志:
* 8940776 (HEAD -> master) project folder
执行后,它会改变修改过的提交的哈希值。
4. 使用 BRG Repo-Cleaner
另一个好选择是BRG Repo-Cleaner,这是一个由Java编写的第三方插件。
它比git filter-branch
方法更快,并且适用于删除大文件、密码、凭据和其他私人数据。
假设我们想移除大于200MB的blob文件。这个插件使其变得简单:
$ java -jar bfg.jar --strip-blob-bigger-than 200M my-repo.git
然后,运行此命令清理死数据:
$ git gc --prune=now --aggressive
5. 使用 git-rebase
我们需要从git日志中获取SHA1键才能使用这种方法:
$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 535f7ea (HEAD -> master) blob file removed
* 8bffdfa blob file
* 5bac30b index.html
目标是将blob文件从我们的提交历史中移除。因此,我们将使用历史中紧随我们想要移除的那一行之前的SHA1键。
使用此命令进入交互式重基:
$ git rebase -i 5bac30b
这将打开nano
编辑器显示:
pick 535f7ea blob file removed
pick 8bffdfa blob file
# Rebase 5bac30b..535f7ea onto 535f7ea (2 command)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
现在,我们将修改这段内容,删除文本“pick 535f7ea blob file removed”。这样可以帮助我们修改提交历史,删除先前删除的记录。
保存文件并退出编辑器,回到终端,你会看到以下消息:
interactive rebase in progress; onto 535f7ea
Last command done (1 command done):
pick 535f7ea blob file removed
No commands remaining.
You are currently rebasing branch 'master' on '535f7ea'.
(all conflicts fixed: run "git rebase --continue")
最后,继续重基操作:
$ git rebase --continue
Successfully rebased and updated refs/heads/master.
然后,我们可以验证我们的提交历史:
$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 5bac30b (HEAD -> master) index.html
**应注意,这种方法的速度不如 git-filter-repo
**。
6. 总结
本文介绍了从git仓库提交历史中删除大文件的不同方法。根据git文档,git filter-repo
被推荐,因为它速度快,且相比其他方法,缺点较少。