repo-filter的安装

  1. 这个是依赖于pythonpip的 所以需要先安装python
  2. 安装repo-filter
1
2
3
4
5
6
7
8
python -m pip install --user git-filter-repo  # 对于个人用户安装
# 或者全局安装(可能需要管理员权限)
python -m pip install git-filter-repo
````
3. 需要添加环境变量让`git` 可以寻找到
在`Unix/Linux`中被安装到`.local/bin`
```sh
export PATH=$PATH:~/.local/bin/

windows中就被安装在site-packages

使用场景

  1. 删除仓库历史中的大文件
    1
    git filter-repo --force --invert-paths --path large_file.txt
    参数解释:
    • –force:允许覆盖当前仓库。
    • –invert-paths:保留不匹配路径的提交,这里是反选,即删除匹配路径的文件。
    • –path large_file.txt:指定要从历史中删除的文件名。
  2. 替换文件内容
    1
    git filter-repo --replace-text <replacement-file>
    <replacement-file> 中定义替换规则。
  3. 根据大小过滤文件
    1
    git filter-repo --strip-blobs-bigger-than 10M
    删除大于10MB的二进制文件。

高级操作

下面给出一条一次性完成「删除仓库里全部文件并统一重写所有提交信息」的 git filter-repo 命令示例。
(⚠️ 会彻底清空工作目录内容并改写历史,务必先备份!)


✅ 1. 克隆裸仓库(避免工作区干扰)

1
2
git clone --mirror https://github.com/you/repo.git
cd repo.git # 进入裸仓库目录

✅ 2. 删除全部文件

1
git filter-repo --invert-paths --path '*' --all

解释

  • --invert-paths--path '*' 变成“排除所有文件”,即保留空提交,但把文件全部删掉
  • --all 同时作用于所有分支、标签。

✅ 3. 批量重写所有提交信息

创建一个文本文件 msg-map.txt,每行一条替换规则(支持正则):

1
2
# 旧内容<tab>新内容
.* Rewrite: new commit message

然后执行:

1
git filter-repo --replace-message msg-map.txt

如果想完全统一成一句话,可直接:

1
echo '.*	Initial commit' | git filter-repo --replace-message -

✅ 4. 强制推回远程

1
git push --force

所有协作者需重新克隆。


小结

目的 命令
删除全部文件 git filter-repo --invert-paths --path '*' --all
重写所有提交信息 git filter-repo --replace-message <map-file>

注意以上的做法是错误的可以重置message但是无法删除文件

总之依靠git filter-repo --invert-paths --path 删除文件是不靠谱的

使用commit-callback 删除文件

下面给出 git-filter-repo 官方思路 的「终极方案」:
--commit-callback(而不是 --path--strip-blobs-bigger-than
在 Python 层里把 每一次提交直接置空(tree = 空树),并且把消息统一改掉。
这样无论普通文件、子模块、LFS、任何 blob 都会消失,100 % 成功。


✅ 1. 进入裸仓库

1
2
git clone --mirror https://github.com/you/repo.git
cd repo.git

✅ 2. 一条命令:空树 + 统一消息

1
2
3
4
5
git filter-repo --force 
--commit-callback '
commit.file_changes = [] # 清空所有文件变更
commit.message = b"Initial commit"
'

说明

  • commit.file_changes = []:把本次提交携带的 所有文件、子模块、重命名、删除 全部抹掉 → 生成空树。
  • commit.message = b"Initial commit":提交信息统一重写成你想要的文字。
  • --force:避免“已过滤过”的拒绝提示。

✅ 3. 验证

1
2
git ls-tree -r HEAD        # 没有任何文件/子模块
git log --oneline # 所有提交消息都变成 “Initial commit”

✅ 4. 强制推回

1
git push --force

⚠️ 备份提醒

这条命令会永久重写整个仓库历史,务必先在别处备份裸仓库或打 tag 再做。

这个操作下会导致整个仓库空 因为git 会丢掉空提交默认

所以
出现这条提示说明:你刚才那条 --commit-callback 把「所有提交」都变成了「空提交」,结果 filter-repo 认为这些空提交没有实际意义,于是把它们全部丢弃了,仓库里一个提交都不剩,HEAD 指向的 main 分支也就空了。


为什么会这样

git-filter-repo 的默认策略是:

如果一个提交被清空后 既没文件变更,又没父级变化,它就 会被删除,以免留下一堆无意义的空节点。
所以 commit.file_changes = [] 后,所有提交都被当成“可被丢弃”,最终仓库归零。


正确做法:保留空提交

告诉 filter-repo “即使空提交也给我留下”,加 --prune-empty never

1
2
3
4
git filter-repo --force --prune-empty never 
--commit-callback '
commit.file_changes = []
'
  • --prune-empty never:强制保留空提交。
  • 不写 --message-callback:提交信息保持原样。

注意以上的命令写法是针对bash
如果考虑到powershell应该这么写

1
git filter-repo --force --prune-empty never --commit-callback "commit.file_changes=[];commit.message=b'Initial commit'"

验证

1
2
git log --oneline          # 所有原提交都在,只是 tree 为空
git ls-tree -r HEAD # 没有任何文件

推送

1
git push --force

仓库现在就是「所有历史提交都在,但每个提交里都没有文件」的状态。

后记

本来这个repo-filter是由python api的但是我没有成功过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import git_filter_repo as fr

# 创建过滤选项
args = fr.FilteringOptions.default_options()
args.force = True
args.replace_refs = 'delete-no-add'

# 定义 blob 回调 - 删除所有文件
def blob_callback(blob, metadata):
return None # 删除文件

# 定义 commit 回调 - 保持消息不变
def commit_callback(commit, metadata):
return commit # 不做任何修改

# 创建并运行过滤器
filter = fr.RepoFilter(
args,
blob_callback=blob_callback,
commit_callback=commit_callback
)
filter.run()