概述

本教程将介绍如何使用各种工具从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被推荐,因为它速度快,且相比其他方法,缺点较少。


« 上一篇: JBang指南