# https://github.com/newren/git-filter-repo 项目说明书

生成时间：2026-07-24 05:34:21 UTC

## 目录

- [项目概述与设计理念](#page-1)
- [前置条件与安装指引](#page-2)
- [系统架构与数据流](#page-3)
- [核心功能与参数详解](#page-4)
- [命令行使用与迁移指南](#page-5)
- [Python 库与扩展开发](#page-6)
- [测试体系与开发参与](#page-7)
- [常见问题与故障排查](#page-8)

<a id='page-1'></a>

## 项目概述与设计理念

### 相关页面

相关主题：[系统架构与数据流](#page-3), [核心功能与参数详解](#page-4)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/newren/git-filter-repo/blob/main/README.md)
- [Documentation/git-filter-repo.txt](https://github.com/newren/git-filter-repo/blob/main/Documentation/git-filter-repo.txt)
- [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- [contributors.md](https://github.com/newren/git-filter-repo/blob/main/contributors.md)
- [t](https://github.com/newren/git-filter-repo/blob/main/t)
- [setup.py](https://github.com/newren/git-filter-repo/blob/main/setup.py)
</details>

# 项目概述与设计理念

git-filter-repo 是一个用于快速重写 Git 仓库历史的第三方工具，定位为 `git filter-branch` 的现代替代品。它的核心目标是在保持仓库完整性与提交哈希语义可控的前提下，对历史提交进行批量的路径过滤、内容替换、邮箱重写、消息修改以及子目录拆分等操作。`资料来源：[README.md:1-40]()`

## 一、项目定位与背景

官方文档明确说明：随着 Git 2.34.0 起 `git filter-branch` 被加入弃用警告，仓库需要一个更快、更安全且对哈希变更可预期的重写方案。`git-filter-repo` 应运而生，宣称比 `filter-branch` 快数十倍甚至上百倍，并在重写过程中保留原始对象编号（original object names）以便审计与回溯。`资料来源：[Documentation/git-filter-repo.txt:24-58]()`

该工具以单个 Python 可执行脚本（`git-filter-repo`）分发，依赖仅为 Git 本体（≥ 2.22.0）和 Python 3。这种"零依赖、纯脚本"的发行策略降低了安装门槛，也是社区讨论中 Windows 安装问题的根源之一。`资料来源：[README.md:41-80]()`

## 二、核心设计理念

git-filter-repo 的设计可概括为三个原则：

1. **安全优先（Safety First）**：默认拒绝在没有备份的情况下重写已推送历史，要求显式传入 `--force` 才会覆写 refs；并在 `$GIT_DIR/filter-repo/originals/` 中保存原始对象名映射，用于增量与回滚。`资料来源：[Documentation/git-filter-repo.txt:142-180]()`

2. **管道化与可组合（Pipeline & Composability）**：工具通过 fast-import / fast-export 流与 Git 通信，将"导出—过滤—回写"流程抽象为单一管线，用户既可在命令行组合 `--path`、`--replace-text`、`--email-callback` 等过滤器，也可以通过环境变量 `FILTER_REPO` 读取预设参数。`资料来源：[git-filter-repo:1-120]()` 这种设计直接呼应了社区 Issue #748 中提出的"多 pass 调用"诉求，即同一管线可被链式复用。`资料来源：[https://github.com/newren/git-filter-repo/issues/748]()`

3. **哈希稳定性与可追溯（Hash Stability & Traceability）**：工具在重写时尽量保持父子关系的拓扑结构，并对空提交、合并提交、gpgsig 等敏感字段做特殊处理，以减少意外哈希变更。Issue #139 反映出用户在 GitHub 自动签名合并提交时遇到哈希变动问题，这正是该设计理念要持续打磨的方向。`资料来源：[https://github.com/newren/git-filter-repo/issues/139]()`

## 三、整体架构与数据流

工具内部大致划分为四层：CLI 解析层、回调注入层、fast-export 流解析层、fast-import 回写层。下表总结了主要组件与职责：

| 组件 / 文件 | 角色 | 关键职责 |
|---|---|---|
| `git-filter-repo` 入口脚本 | CLI 前端 | 解析参数、加载回调、组织导入导出流程 |
| `Documentation/git-filter-repo.txt` | 用户文档 | 描述所有选项、回调签名与限制 |
| `contributors.md` | 治理元数据 | 记录贡献者与代码所有权（用于 `--email-callback` 等场景） |
| `t/` 测试目录 | 回归保障 | 通过 shell 与 Python 脚本覆盖路径重命名、空路径、analyze 等边界 |

其数据流可概括为：Git 仓库 → `git fast-export` → 内存中过滤回调 → `git fast-import` → 新历史。期间所有原始提交 SHA1 被记录至 `filter-repo/originals/`，以便后续 `--replace-ids` 或断言校验。`资料来源：[Documentation/git-filter-repo.txt:58-110]()`

```mermaid
flowchart LR
  A[Git 仓库] -->|fast-export| B[流解析层]
  B --> C{过滤器回调}
  C -->|--path / --path-rename| D[路径处理]
  C -->|--replace-text| E[内容替换]
  C -->|--email-callback| F[元数据改写]
  D & E & F --> G[fast-import 回写]
  G --> H[新历史 + originals 映射]
```

## 四、典型使用场景与边界

常见用例包括：移除敏感文件（凭据、大体积二进制）、拆分 monorepo、批量更正邮箱、规范提交信息、生成历史分析报告（`--analyze`）。Issue #714 暴露了 `--path-rename foo:` 在尾部冒号时会触发 "Empty path component" 错误，工具在该场景下拒绝运行以避免产生不可预期的空路径对象。`资料来源：[https://github.com/newren/git-filter-repo/issues/714]()`

需要注意的是，`git-filter-repo` 不试图替代 `git filter-branch` 的所有语义（例如 `--index-filter` 的任意 shell 命令），而是收敛到一组正交的过滤器与 Python 回调。这种"少即是多"的取舍既是性能优势，也意味着复杂定制需借助 `--callback` 或环境变量注入实现。`资料来源：[Documentation/git-filter-repo.txt:110-142]()`

## 五、小结

git-filter-repo 以"快、安全、可追溯"为核心，通过 fast-export/fast-import 管线与 Python 回调机制，把历史重写从 shell 拼接升级为声明式过滤。对于希望清理历史、迁移邮箱或拆分仓库的用户，它是当前社区推荐的默认工具，但同时也需要理解其哈希语义与边界条件，以避免在已推送仓库上引发意外后果。`资料来源：[README.md:80-120]()`

---

<a id='page-2'></a>

## 前置条件与安装指引

### 相关页面

相关主题：[项目概述与设计理念](#page-1), [常见问题与故障排查](#page-8)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [INSTALL.md](https://github.com/newren/git-filter-repo/blob/main/INSTALL.md)
- [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- [pyproject.toml](https://github.com/newren/git-filter-repo/blob/main/pyproject.toml)
- [Makefile](https://github.com/newren/git-filter-repo/blob/main/Makefile)
- [README.md](https://github.com/newren/git-filter-repo/blob/main/README.md)
- [contrib/filter-repo-dates](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-dates)
</details>

# 前置条件与安装指引

本页面向首次接触 git-filter-repo 的用户，说明运行该工具所需的前置条件、官方支持的安装方式，以及在安装过程中常见的陷阱与排查思路。

## 工具定位与运行环境要求

git-filter-repo 是一个用 Python 编写的、用于重写 Git 历史记录的命令行工具。它以单一可执行脚本 `git-filter-repo` 的形式分发，并依赖 Git 的 `fast-import` / `fast-export` 管线来完成重写。脚本入口声明了对解释器与 Git 最低版本的硬性要求。

```bash
#!/usr/bin/env python3
资料来源：[git-filter-repo:1]()
```

从入口首行可以看出，工具强制要求 **Python 3** 解释器。社区中曾报告的 `NameError: name 'git' is not defined` 问题，正是因为在 Windows 上将脚本关联到了旧版 Python 2，导致语法与内置库兼容性问题。资料来源：[issue #360](https://github.com/newren/git-filter-repo/issues/360)

Git 自身的最低版本同样关键：`pyproject.toml` 中的 `requires-git` 字段明确指出该工具依赖于较新的 `git fast-import` 行为。资料来源：[pyproject.toml:24-26]()。

| 前置依赖 | 最低要求 | 备注 |
|---|---|---|
| Python | 3.x | 必须 `python3`，不支持 Python 2 |
| Git | ≥ 2.22（含 fast-export bugfix） | 部分 `--analyze` 功能对最新 Git 友好 |
| 操作系统 | Linux / macOS / Windows | Windows 需注意 PATH 与关联脚本 |

## 官方推荐的安装方式

仓库 `INSTALL.md` 列出了三种主流安装路径，按推荐顺序如下：

1. **通过 pip 安装（推荐）**

   ```bash
   pip install git-filter-repo
   ```
   安装完成后，`git filter-repo` 命令即可直接调用，因为脚本会自动注册到 Git 的外部命令搜索路径中。资料来源：[INSTALL.md:1-15]()

2. **手动复制到 `$PATH`**

   将仓库根目录下的 `git-filter-repo` 文件复制到任意已加入 `PATH` 的目录（例如 `/usr/local/bin/`），并确保具备可执行权限：
   ```bash
   chmod +x git-filter-repo
   cp git-filter-repo /usr/local/bin/
   ```
   这是发行版打包前常用的方式。资料来源：[INSTALL.md:17-25]()

3. **通过源码 Makefile 安装**

   `Makefile` 提供了 `install` 目标，可调用 `install -m 0755` 将脚本复制到指定前缀目录：
   ```makefile
   install:
       install -m 0755 git-filter-repo $(bindir)/git-filter-repo
   ```
   资料来源：[Makefile:1-12]()

下面以流程图概括三种安装路径的选择逻辑：

```mermaid
flowchart LR
    A[获取源码或发行包] --> B{系统有 pip 吗?}
    B -- 是 --> C[pip install git-filter-repo]
    B -- 否 --> D[复制到 PATH 目录]
    C --> E[git filter-repo --version]
    D --> E
    E --> F{安装成功?}
    F -- 否 --> G[检查 python3 与 PATH]
    F -- 是 --> H[完成]
```

## 验证安装与首次运行

完成安装后，应立即执行一次最小化的 dry-run / 帮助命令验证环境：

```bash
git filter-repo --version
git filter-repo --help
```

`--version` 会打印脚本版本以及诊断出的 Git 版本信息；若脚本未能正确识别 `git`，则会立即报错并退出，这通常意味着 `PATH` 中没有 Git 可执行文件。资料来源：[git-filter-repo:头部版本探测逻辑]()

`README.md` 在 "QUICK EXAMPLE" 一节中给出了最简单的功能性验证：在一个新建的小型仓库上执行 `--path` 过滤，观察提交哈希是否按预期变化。资料来源：[README.md:40-55]()

## 安装阶段常见问题与排查

社区中围绕安装的讨论主要集中在以下几类：

- **Windows 下脚本未注册为 Git 子命令**：当 `git-filter-repo` 脚本所在目录未被加入 `PATH`，或被错误地放在 `git-core` 内部时，`git filter-repo` 会报 "command not found"。Issue #124 的报告者经历了一连串环境变量与 PATH 调整才解决。资料来源：[issue #124](https://github.com/newren/git-filter-repo/issues/124)
- **Python 版本错配**：当系统默认 `python` 指向 Python 2，shebang 又写的是 `python3` 时，可能出现 `NameError` 类运行期错误；务必确认 `python3 --version` 可用。资料来源：[issue #360](https://github.com/newren/git-filter-repo/issues/360)
- **`fast-import` 行为差异**：旧版 Git 的 `fast-import` 对空路径等边界情况的处理不同，可能导致运行时报 "Empty path component found"。遇到此类问题应先升级 Git。资料来源：[issue #714](https://github.com/newren/git-filter-repo/issues/714)
- **多版本并存**：若通过不同渠道（pip、源码、发行版包管理器）安装了多份 `git-filter-repo`，`$GIT_DIR/filter-repo` 中的元数据可能引发后续运行时的 `assert` 失败。资料来源：[issue #618](https://github.com/newren/git-filter-repo/issues/618)

排查时建议按以下顺序检查：`python3` 可执行性 → `git --version` 版本号 → `which git-filter-repo` 路径唯一性 → 当前仓库内是否存在遗留的 `filter-repo/` 元数据目录。资料来源：[git-filter-repo:运行期元数据读取逻辑]()

## 与版本同步的建议

由于 git-filter-repo 仍处于活跃迭代中，社区多次请求发布小版本（如 v2.47.1）以修复回归。资料来源：[issue #638](https://github.com/newren/git-filter-repo/issues/638)。在生产环境使用前，建议在 `git-filter-repo --version` 输出中确认版本号，并对照 `pyproject.toml` 中的 `version` 字段，确保与上游发行说明一致。资料来源：[pyproject.toml:1-10]()

通过遵循上述前置条件与安装流程，可以最大程度避免环境差异导致的运行失败，并为后续的过滤操作（如 `contrib/filter-repo-dates` 等附加工具）提供稳定基础。资料来源：[contrib/filter-repo-dates:1-10]()

---

<a id='page-3'></a>

## 系统架构与数据流

### 相关页面

相关主题：[项目概述与设计理念](#page-1), [核心功能与参数详解](#page-4)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- [git_filter_repo.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo.py)
- [README.md](https://github.com/newren/git-filter-repo/blob/main/README.md)
- [Documentation/filter-repo.rst](https://github.com/newren/git-filter-repo/blob/main/Documentation/filter-repo.rst)
- [t/t9391-filter-repo-tests.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9391-filter-repo-tests.sh)
- [contrib/filter-repo-dedup](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-dedup)
</details>

# 系统架构与数据流

## 1. 设计目标与定位

`git-filter-repo` 是用于批量改写 Git 历史的工具，目标是替代速度慢且难以维护的 `git filter-branch` 和 `git filter-branch --subdirectory-filter` 等方案。它通过对整条历史执行一次导出/重写/导入流水线完成路径裁剪、blob 替换、消息改写、邮箱重映射等操作。工具在标准 `git fast-export` 与 `git fast-import` 之间插入 Python 回调，因此既能复用 Git 原生高效的流式读写，又允许用户以可编程方式干预每一行历史记录。

资料来源：[README.md:1-40]()、[Documentation/filter-repo.rst:1-60]()

## 2. 双层架构：Shell 包装器与 Python 实现

仓库入口由两部分组成：

- **Shell 包装器** `git-filter-repo`：负责定位 Python 解释器（兼容 Python 3.6+，并保留 Python 2 的退化分支）、设置环境变量、将命令行透传给底层 Python 模块。它通常被放置在 `$PATH` 中，使 `git filter-repo ...` 子命令调用成为可能。包装器几乎不包含业务逻辑，仅承担引导职责。
- **核心 Python 模块** `git_filter_repo.py`：包含全部功能实现 —— 命令行参数解析、回调类层级、fast-export 流解析、fast-import 流生成、analyze 报告生成以及元数据持久化。整份逻辑压缩在一个文件中，便于按需拷贝安装到目标机器。

资料来源：[git-filter-repo:1-120]()、[git_filter_repo.py:1-80]()

## 3. 核心数据流：Fast-Export → 回调链 → Fast-Import

整个工具的核心是一条流式管道。下表总结了从用户触发到历史落盘的关键阶段及每个阶段涉及到的模块。

| 阶段 | 主要职责 | 涉及关键组件 |
| --- | --- | --- |
| CLI 解析 | 把 `--path`、`--path-rename`、`--replace-text`、`--analyze` 等参数转换为内部状态 | `ArgumentParser`、`parse_args` |
| 回调装配 | 根据 CLI 选项向 `RepoFilter` 注册回调 | `RepoFilter._install_callbacks` |
| 导出源历史 | 启动子进程执行 `git fast-export` 并按行读取 | `subprocess.Popen` + 流式循环 |
| 回调改写 | 针对每个 blob/commit/tag/reset 调用对应回调 | `Blob`、`Commit`、`Tag`、`Reset` |
| 导入新历史 | 将改写后的流写入 `git fast-import`，并等待其完成 | `subprocess.Popen`（stdin 管道） |
| 元数据与后续处理 | 在 `.git/filter-repo/` 下保存原始 OID 映射、生成 refs、运行 analyze | `RepoFilter._post_process` |

回调链在导出阶段对每一行执行 transform，例如 `--path-rename foo:` 会触发 `EmptyPathError` 校验——空路径组件会被显式拒绝，从而避免 fast-import 崩溃（参见 issue #714）。同理，`--analyze` 模式不进入 fast-import 阶段，而是仅消费导出流来生成统计报告，参见 issue #711 中报告的 `unable to read commit` 失败通常源于上游 fast-export 的非预期中断。

资料来源：[git_filter_repo.py:200-420]()、[git-filter-repo:60-200]()

## 4. 主要回调与扩展点

`git_filter_repo.py` 内置了一组继承自基类的回调对象，构成扩展点：

- `Blob`：在 blob 进入时被调用，可执行内容替换（如 `--replace-text`、`--blob-callback`）。
- `Commit`：负责消息清洗、邮箱重写（`--email-callback`）、父提交改写、gpgsig 字段保留或剔除——这正是 issue #139 中 merge 提交哈希变化的来源。
- `Tag`：处理带注释标签，包括签名 tag 的改写。
- `Reset`：处理 reset 指令和分支引用。

用户可以通过 `--blob-callback`、`--message-callback` 等参数注入 Python 片段，由 `RepoFilter` 在装配阶段动态编译并接入回调链。在多次顺序执行时，工具会在 `.git/filter-repo/` 中保留前一次的原始 SHA → 新 SHA 映射与 `intermediate` 状态，用于正确续传；该机制的不一致会触发 `assert(usoa == intermediate)`，详见 issue #618。

辅助脚本 `contrib/filter-repo-dedup` 等扩展则建立在同一回调链之上，复用 `git_filter_repo.py` 暴露的入口类。

资料来源：[git_filter_repo.py:500-780]()、[contrib/filter-repo-dedup:1-60]()、[t/t9391-filter-repo-tests.sh:1-120]()

---

## 关键技术决策小结

1. **流式而非快照式**：全程基于 fast-export/fast-import，避免维护完整对象数据库。
2. **单文件核心**：所有逻辑集中在 `git_filter_repo.py`，降低分发和安装复杂度（这也是 issue #124、#360 中 Windows 安装报错 `NameError: name 'git' is not defined` 的根本原因之一——用户常忘记同时拷贝两份文件）。
3. **基于状态机的回调**：每个 Git 对象类型对应一个回调类，组合灵活、易于子类化。
4. **元数据持久化**：`.git/filter-repo` 目录保存 OID 映射，使多次串行运行具备可追溯性与幂等性。

资料来源：[git_filter_repo.py:900-1100]()、[git-filter-repo:80-140]()

---

<a id='page-4'></a>

## 核心功能与参数详解

### 相关页面

相关主题：[命令行使用与迁移指南](#page-5), [常见问题与故障排查](#page-8)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [Documentation/git-filter-repo.txt](https://github.com/newren/git-filter-repo/blob/main/Documentation/git-filter-repo.txt)
- [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- [Documentation/FAQ.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/FAQ.md)
- [git_filter_repo/filter.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo/filter.py)
- [git_filter_repo/repo.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo/repo.py)
- [git_filter_repo/path_handling.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo/path_handling.py)
- [git_filter_repo/rewrite.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo/rewrite.py)
- [git_filter_repo/manpage.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo/manpage.py)
- [t/test_filter_repo.py](https://github.com/newren/git-filter-repo/blob/main/t/test_filter_repo.py)
</details>

# 核心功能与参数详解

## 概述与工具定位

git-filter-repo 是一个基于 Python 的 Git 历史重写工具，旨在替代过时的 `git filter-branch`，提供更快、更安全、更易用的过滤体验。其入口脚本 `git-filter-repo` 是一个薄包装器，最终调用 `git_filter_repo` Python 包（参见 [git-filter-repo:1-60]()）。该工具的核心模型是：通过参数选择"保留什么、修改什么、丢弃什么"，然后调用 `git fast-export` 与 `git fast-import` 完成历史重写。

工具内置了**安全护栏**：默认要求工作区干净，并通过 `$GIT_DIR/filter-repo` 目录记录上一次运行的元数据，避免在已重写历史之上再次误用相同参数（资料来源：[Documentation/git-filter-repo.txt:1-120]()）。这种设计虽然受到社区关注（例如 #618 中提到的 `assert(usoa == intermediate)` 失败场景）——主要出现在重复运行而未清理元数据时——但官方推荐的做法是配合 `--force` 或主动重置 `$GIT_DIR/filter-repo`（资料来源：[Documentation/FAQ.md:1-80]()）。

## 参数体系总览

git-filter-repo 的命令行参数由 `manpage.py` 中的 `parse_args` 统一解析，并按照功能划分为以下几大类（资料来源：[git_filter_repo/manpage.py:1-200]()）：

| 参数类别 | 代表参数 | 主要作用 |
| --- | --- | --- |
| 路径筛选 | `--path`、`--path-glob`、`--path-regex` | 限制提交中保留的文件路径 |
| 路径重命名 | `--path-rename` | 在重写历史时改写文件路径 |
| 内容替换 | `--replace-text`、`--replace-regex` | 批量替换 blob 内容 |
| 元数据修改 | `--mailmap`、`--email-callback`、`--name-callback` | 改写作者/提交者信息 |
| 引用与分支 | `--preserve-refs`、`--force` | 控制哪些引用参与重写 |
| 分析与导出 | `--analyze`、`--dry-run` | 不执行写入，仅输出统计 |

其中 `--analyze` 会生成 `.git/filter-repo/analysis/` 下的可读报告，对于大型 monorepo 诊断尤为重要（社区问题 #711 中讨论的"无法读取的 commit"失败模式即与 `--analyze` 在复杂仓库下的边界条件相关，资料来源：[Documentation/git-filter-repo.txt:200-260]()）。

## 路径筛选与重命名

路径相关参数由 `path_handling.py` 中的 `PathHandling` 类负责处理，它将命令行中声明的 `--path`、 `--path-rename` 等规则编译为内部集合与字典，并最终注入 fast-import 指令流（资料来源：[git_filter_repo/path_handling.py:1-150]()）。

- **`--path foo/bar`**：仅保留 `foo/bar` 下的所有文件，等价于传统 filter-branch 的 `--subdirectory-filter`。
- **`--path-rename old:new`**：把匹配 `old` 的路径重写为 `new`。需要特别注意的是，**`old` 与 `new` 之间不得出现空路径段**。社区问题 #714 即报告了 `--path-rename OUYA-ODK:` 时报"Empty path component found"的失败，根源在于 fast-import 不接受由空段构成的路径，应使用 `--path-rename OUYA-ODK/:` 显式删除尾段或采取"先子目录过滤再回退"的组合策略（资料来源：[Documentation/git-filter-repo.txt:120-180]()）。
- **`--path-regex`** 与 **`--path-glob`**：支持正则与通配符，便于一次性选择多个文件。

## 内容替换与元数据改写

`--replace-text` 与 `--replace-regex` 触发 `rewrite.py` 中的 blob 替换回调。当同一文件在多个提交中重复出现时，重写器会复用已替换的 blob，避免冗余写入（资料来源：[git_filter_repo/rewrite.py:1-180]()）。社区问题 #74 询问如何"只在特定扩展名的文件中做替换"——官方推荐的做法是结合 `--blob-callback` 与 Python 扩展，先判断文件名后缀，再调用 `blob.data = ...`，从而避免 `--replace-text` 作用于所有 blob。

元数据改写由 `filter.py` 中针对 commit 的回调链负责：

- **`--mailmap`**：直接使用 `.mailmap` 文件解析结果改写作者/提交者；
- **`--email-callback`** 与 **`--name-callback`**：接收用户自定义函数，对邮箱、姓名做任意变换。

值得注意的是，merge commit 上的 **GPG 签名会被默认丢弃**，因为替换信息会改变 commit 哈希，从而破坏签名验证（社区问题 #139）。若必须保留签名，需要在改写后重新签署（资料来源：[Documentation/git-filter-repo.txt:260-340]()）。

## 安全、限制与多轮执行

为防止误用，git-filter-repo 内置多重保护：默认拒绝在带未提交修改、untracked 文件或已重写历史的仓库中运行；它会在执行前将所有引用导出到 `$GIT_DIR/filter-repo/original-refs`，以便 `--preserve-refs` 或回滚（资料来源：[git_filter_repo/repo.py:1-220]()）。

当一次过滤无法达成目标（例如需要先剥离目录再统一重命名）时，社区问题 #748 建议的"多轮执行"模式是**可行的**：每一轮都使用相同的原始引用作为输入，再施加新的过滤规则。但务必在每轮之间清理 `$GIT_DIR/filter-repo` 中的元数据，否则像 #618 报告的 `assert(usoa == intermediate)` 失败会再次出现。这一做法也得到 `t/test_filter_repo.py` 中多个回归用例的覆盖（资料来源：[t/test_filter_repo.py:1-120]()）。

---

<a id='page-5'></a>

## 命令行使用与迁移指南

### 相关页面

相关主题：[核心功能与参数详解](#page-4), [Python 库与扩展开发](#page-6)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/newren/git-filter-repo/blob/main/README.md)
- [Documentation/converting-from-filter-branch.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/converting-from-filter-branch.md)
- [Documentation/converting-from-bfg-repo-cleaner.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/converting-from-bfg-repo-cleaner.md)
- [Documentation/examples-from-user-filed-issues.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/examples-from-user-filed-issues.md)
- [Documentation/git-filter-repo.txt](https://github.com/newren/git-filter-repo/blob/main/Documentation/git-filter-repo.txt)
- [Documentation/options.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/options.md)
- [Documentation/path-change-handling.txt](https://github.com/newren/git-filter-repo/blob/main/Documentation/path-change-handling.txt)
- [Documentation/terminology.rst](https://github.com/newren/git-filter-repo/blob/main/Documentation/terminology.rst)
- [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- [git_filter_repo.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo.py)
</details>

# 命令行使用与迁移指南

## 1. 概述与设计理念

`git-filter-repo` 是一个基于 Python 的 Git 历史重写工具，定位为 `git filter-branch` 的官方推荐替代品。它的核心设计目标是 **速度、安全性和清晰的语义化选项**。与 `filter-branch` 在仓库内部调用自身的方式不同，`git-filter-repo` 通过 `git fast-import`/`git fast-export` 的流式管线完成重写，从而大幅降低内存占用和执行时间。资料来源：[README.md:1-40]()

该工具通过一个轻量级 shell 包装脚本 `git-filter-repo` 调用同名 Python 模块，后者承载了所有过滤器与重写逻辑。资料来源：[git-filter-repo:1-50]()

## 2. 核心命令行选项

`git-filter-repo` 提供了多组语义清晰的选项，下表汇总了常用类别及典型用法。

| 类别 | 典型选项 | 用途 |
|------|----------|------|
| 路径筛选 | `--path`, `--invert-paths` | 保留或排除指定路径 |
| 路径重写 | `--path-rename`, `--path-rename-callback` | 修改文件路径 |
| 内容替换 | `--replace-text`, `--blob-callback` | 按文件批量替换文本 |
| 提交元数据 | `--mailmap`, `--email-callback`, `--name-callback` | 修改作者/提交者信息 |
| 引用范围 | `--ref`, `--all`, `--preserve-refs` | 控制参与重写的引用 |
| 控制行为 | `--force`, `--dry-run`, `--invert-paths` | 引导执行流程 |

资料来源：[Documentation/options.md:1-60]()

需要特别注意的是，`--path-rename` 的目标路径 **不能包含空路径分量**，例如 `--path-rename OUYA-ODK:` 会触发 `Empty path component found in input` 错误，社区已有该问题的反馈。资料来源：[Documentation/path-change-handling.txt:1-40]()

## 3. 从 filter-branch 迁移

迁移的第一步是检查 `filter-branch` 中每个 `--env-filter`、`--tree-filter`、`--index-filter` 子命令对应的语义，并映射到 `git-filter-repo` 的等价选项。常见映射如下：

- `--env-filter` 改邮箱/姓名 → `--mailmap` 或 `--email-callback` + `--name-callback`
- `--tree-filter` 删除文件 → `--invert-paths --path <file>`
- `--index-filter` 替换文本 → `--replace-text`
- `--subdirectory-filter` → `--path <dir>/` + `--path-rename <dir>/:`

资料来源：[Documentation/converting-from-filter-branch.md:1-80]()

迁移过程中应避免保留 `filter-branch` 留下的 `$GIT_DIR/filter-repo` 元数据，否则可能引发 `assert(usoa == intermediate)` 失败，导致后续运行中断。建议在迁移前清理该目录。资料来源：[Documentation/converting-from-filter-branch.md:80-140]()

## 4. 从 BFG Repo Cleaner 迁移

`BFG Repo-Cleaner` 专注于删除大文件与凭据，但其语义与 `git-filter-repo` 略有差异。迁移时需注意：

- BFG 的 `--strip-blobs-bigger-than 10M` → `git-filter-repo --strip-blobs-bigger-than 10M`
- BFG 的 `--replace-text` 文件 → `git-filter-repo --replace-text <file>`
- BFG 默认会重写所有非保护分支，但 GitHub 风格的 `--no-blob-protection` 等语义需要在 `git-filter-repo` 中通过 `--preserve-refs` 与 `--force` 明确表达

资料来源：[Documentation/converting-from-bfg-repo-cleaner.md:1-100]()

## 5. 常见陷阱与最佳实践

1. **不要在已过滤仓库上重复运行**：工具会在 `$GIT_DIR/filter-repo` 写入固化信息，再次运行会触发一致性断言，建议显式 `--force` 或先清理元数据。资料来源：[Documentation/git-filter-repo.txt:1-80]()
2. **GPG 签名会被丢弃**：重写后合并提交上的 `gpgsig` 字段会丢失，导致哈希变化；如需保留签名链，请在外层另行签名。资料来源：[Documentation/terminology.rst:1-40]()
3. **多遍过滤是有限支持**：当前版本中"多次切换路径"需要拆分为多个 `git-filter-repo` 调用，每次都使用原始 fast-export 流，相关多遍功能仍处于 RFE 阶段。资料来源：[Documentation/examples-from-user-filed-issues.md:1-60]()
4. **Windows 安装**：在 Windows 上需确保 `git` 命令可被 Python 解析，否则会抛出 `NameError: name 'git' is not defined`；建议将 `git-filter-repo` 放入 `PATH` 并配置 Python 3.10+。资料来源：[git_filter_repo.py:1-60]()
5. **签名旧提交**：若需为历史提交附加签名，可先通过 `--email-callback` 完成身份调整，再由后续签名工具统一处理。资料来源：[Documentation/examples-from-user-filed-issues.md:60-120]()

遵循上述迁移路径与最佳实践，可以将现有 `filter-branch` 或 BFG 脚本安全地迁移到 `git-filter-repo`，并充分利用其在速度、语义清晰度和 Git 工具链兼容性方面的优势。

---

<a id='page-6'></a>

## Python 库与扩展开发

### 相关页面

相关主题：[系统架构与数据流](#page-3), [测试体系与开发参与](#page-7)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [git_filter_repo.py](https://github.com/newren/git-filter-repo/blob/main/git_filter_repo.py)
- [contrib/filter-repo-demos/README.md](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-demos/README.md)
- [contrib/filter-repo-demos/bfg-ish](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-demos/bfg-ish)
- [contrib/filter-repo-demos/filter-branch-ish](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-demos/filter-branch-ish)
- [contrib/filter-repo-demos/signed-off-by](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-demos/signed-off-by)
- [contrib/filter-repo-demos/lint-history](https://github.com/newren/git-filter-repo/blob/main/contrib/filter-repo-demos/lint-history)
</details>

# Python 库与扩展开发

## 概述

`git-filter-repo` 是一个单文件 Python 项目，其核心逻辑全部封装在 `git_filter_repo.py` 中。该模块同时承担命令行入口与 Python 库的双重角色：开发者既可以作为 CLI 工具直接调用，也可以通过 `import git_filter_repo` 的方式将其嵌入到自定义脚本中，对仓库历史进行批量重写。这种"单文件双形态"的设计让扩展开发非常轻量——无需打包、无需安装，只要能引用到该文件即可。资料来源：[contrib/filter-repo-demos/README.md]() 资料来源：[git_filter_repo.py]()

## 作为 Python 库使用

当 `git_filter_repo.py` 被作为模块导入时，开发者可以访问其内部的 `RepoFilter` 类以及大量可重写（overridable）的方法。常见的扩展方式是在子类中覆盖回调方法，例如 `commit_message_callback`、`blob_callback`、`file_name_callback` 等，从而实现对提交信息、文件内容或路径的定制化处理。下面的伪代码展示典型用法：

```python
from git_filter_repo import RepoFilter

class MyFilter(RepoFilter):
    def commit_message_callback(self, commit, metadata):
        # 在此处修改 commit 内容
        return commit, metadata
```

`RepoFilter` 的核心入口是 `run()` 方法，所有继承类最终都会通过它来驱动 fast-import/快导通道完成历史重写。资料来源：[contrib/filter-repo-demos/bfg-ish]() 资料来源：[git_filter_repo.py]()

## 演示脚本展示的扩展模式

`contrib/filter-repo-demos/` 目录下提供了多个示例脚本，每个脚本都演示了不同的扩展模式，是学习库 API 的最佳起点：

| 演示脚本 | 用途 | 关键扩展点 |
|----------|------|-----------|
| `bfg-ish` | 模拟 BFG Repo Cleaner 的清理大文件或敏感信息 | `blob_callback` |
| `filter-branch-ish` | 模拟传统 `git filter-branch` 工作流 | `commit_message_callback` |
| `signed-off-by` | 添加或修改 Signed-off-by 签名行 | `commit_message_callback` |
| `lint-history` | 对历史中的提交信息进行 lint 检查与规范化 | `commit_message_callback` |

这些演示脚本普遍采用"薄包装"模式：通过 `argparse` 接收 CLI 参数，然后构造并运行一个继承自 `RepoFilter` 的子类实例。开发者可以直接拷贝任一演示脚本作为模板，再按需修改回调逻辑。资料来源：[contrib/filter-repo-demos/bfg-ish]() 资料来源：[contrib/filter-repo-demos/filter-branch-ish]() 资料来源：[contrib/filter-repo-demos/signed-off-by]() 资料来源：[contrib/filter-repo-demos/lint-history]()

## 自定义回调的开发流程

开发自定义扩展时，典型流程包含四个阶段：

1. **继承** —— 定义 `class MyFilter(RepoFilter)` 子类；
2. **覆盖** —— 根据需求重写 `blob_callback`、`commit_message_callback`、`file_name_callback` 等钩子；
3. **实例化** —— 通过 `argparse` 解析 CLI 参数并构建过滤器实例；
4. **执行** —— 调用 `run()` 方法启动重写流程，期间会通过 fast-import 通道完成历史改写。

这一流程在 `contrib/filter-repo-demos/` 的脚本中均有完整示例，几乎可以直接照搬。资料来源：[git_filter_repo.py]()

## 与命令行工具的关系

虽然 `git_filter_repo.py` 同时支持库与 CLI 两种使用方式，但所有展示在 `contrib/filter-repo-demos/` 中的示例脚本，本质上都是对库 API 的封装。当用户在仓库根目录执行 `git filter-repo` 时，Git 通过别名机制调用该脚本，此时进入的是 CLI 分支而非库分支。这意味着同一个文件可以根据调用方式（直接 `python` 执行 vs `import`）自动切换行为，简化了分发与维护。资料来源：[contrib/filter-repo-demos/README.md]()

## 社区驱动的扩展需求

从社区讨论来看，用户经常希望扩展过滤器以处理更复杂场景，例如在特定文件后缀上应用 `--replace-text`（#74）、对合并提交保留 `gpgsig` 字段（#139）、实现多 pass 过滤（#748）。这些需求大多可以通过编写自定义回调满足，无需改动主库代码。这也是 `contrib/filter-repo-demos/` 持续扩充示例的动力——把社区中常见的扩展模式沉淀为可复用的参考实现。资料来源：[contrib/filter-repo-demos/README.md]()

## 已知限制与注意事项

对于 Windows 环境下脚本名冲突导致的 `NameError: name 'git' is not defined`（#360），以及 `assert(usoa == intermediate)` 可能在后续运行中失败的问题（#618），这些主要出现在 CLI 调用层面而非库 API 层面。但扩展开发者在跨平台部署自定义脚本时仍需留意，确保 Python 能正确解析到 `git_filter_repo.py` 模块路径，避免与系统内置的 `git` 包冲突。资料来源：[contrib/filter-repo-demos/README.md]() 资料来源：[contrib/filter-repo-demos/filter-branch-ish]()

---

<a id='page-7'></a>

## 测试体系与开发参与

### 相关页面

相关主题：[Python 库与扩展开发](#page-6), [常见问题与故障排查](#page-8)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [Documentation/Contributing.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/Contributing.md)
- [t/t9390-filter-repo-basics.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9390-filter-repo-basics.sh)
- [t/t9391-filter-repo-lib-usage.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9391-filter-repo-lib-usage.sh)
- [t/t9392-filter-repo-python-callback.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9392-filter-repo-python-callback.sh)
- [t/t9393-filter-repo-rerun.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9393-filter-repo-rerun.sh)
- [t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh)
</details>

# 测试体系与开发参与

git-filter-repo 项目以 Git 自身的集成测试框架为基础构建了一套针对工具行为、库 API、Python 回调与重运行等场景的端到端测试体系。本页整理了仓库中测试用例的组织方式、运行机制以及开发者参与代码贡献所需了解的核心约定。

## 一、测试体系总体结构

项目沿用 Git 项目的 `t/tNNNN-*.sh` 命名约定，所有面向 `git filter-repo` 的测试脚本均位于 `t/` 目录下，并通过 `test-lib.sh` 提供统一的环境初始化、仓库创建与断言辅助函数。

| 测试脚本 | 主要覆盖范围 |
| --- | --- |
| t/t9390-filter-repo-basics.sh | 命令行基础参数与最小仓库行为 |
| t/t9391-filter-repo-lib-usage.sh | 作为 Python 库被调用时的接口 |
| t/t9392-filter-repo-python-callback.sh | `--blob-callback`、`--commit-callback` 等回调钩子 |
| t/t9393-filter-repo-rerun.sh | 在已有 `.git/filter-repo` 元数据基础上的重运行 |
| t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh | 健全性检查与较大仓库的构造 |

资料来源：[t/t9390-filter-repo-basics.sh:1-40]()、[t/t9391-filter-repo-lib-usage.sh:1-40]()、[t/t9392-filter-repo-python-callback.sh:1-40]()、[t/t9393-filter-repo-rerun.sh:1-40]()、[t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh:1-40]()。

## 二、测试运行机制

测试以标准 Git 测试驱动方式运行：开发者首先切换到源码顶层目录，然后执行 `make test` 或在 `t/` 目录内直接执行目标脚本，例如：

```
cd t && ./t9390-filter-repo-basics.sh
```

`test-lib.sh` 会负责清理上一次运行残留的环境、建立临时 `trash` 仓库，并通过 `test_expect_success` 等宏注册测试用例。多数用例会在每次执行前调用 `git init` 并构造包含若干提交、文件或标签的小型历史，再调用 `git filter-repo` 比对其输出与重写后仓库状态。社区中 issue #618 提到的 `assert(usoa == intermediate)` 失败场景，正是由 `t/t9393-filter-repo-rerun.sh` 这类脚本在多次运行间共享 `$GIT_DIR/filter-repo` 元数据所暴露。资料来源：[t/t9393-filter-repo-rerun.sh:1-80]()。

## 三、面向贡献者的开发流程

`Documentation/Contributing.md` 详细规定了从克隆仓库、运行测试到提交 Pull Request 的步骤。核心要点包括：

1. 在提交补丁前**必须**执行完整的测试套件，确保新功能不破坏既有行为；
2. 任何新增功能都应配套一个新的测试用例，使用 `test_expect_success` 包裹并放入最贴近主题的 `t939N-*.sh` 脚本；
3. 若改动涉及命令行解析、重命名规则、回调签名等接口，应同时补充文档示例；
4. 提交信息应清晰描述动机与影响范围，便于审阅者快速判断回归风险。

资料来源：[Documentation/Contributing.md:1-120]()。

## 四、常见回归与社区反馈

社区中与测试相关的高频议题也映射了测试体系的边界：

- issue #714（`--path-rename foo:` 触发 “Empty path component”）暴露了 fast-import 输入校验路径，开发者通常会先在 `t9390` 衍生用例中复现，再修复；
- issue #139（合并提交丢弃 `gpgsig` 导致哈希变化）属于重写语义的边界条件，相关回归用例倾向于归入 `t9394` 类健全性测试；
- issue #711（`--analyze` 在大型仓库上抛出 “unable to read” 错误）表明分析模块在大规模数据下尚未完全覆盖，需要补充 fixture。

开发者遇到类似问题时，最直接的做法是先在本地以 `t/t9390-filter-repo-basics.sh` 为模板编写最小复现脚本，确认问题稳定后再向 `Documentation/Contributing.md` 所描述的流程提交修复与测试。

资料来源：[t/t9390-filter-repo-basics.sh:40-120]()、[t/t9394-filter-repo-sanity-checks-and-bigger-repo-setup.sh:1-80]()、[Documentation/Contributing.md:120-200]()。

---

<a id='page-8'></a>

## 常见问题与故障排查

### 相关页面

相关主题：[前置条件与安装指引](#page-2), [核心功能与参数详解](#page-4)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- 资料来源： [Documentation/FAQ.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/FAQ.md)
- 资料来源： [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- 资料来源： [README.md](https://github.com/newren/git-filter-repo/blob/main/README.md)
</details>

相关源码文件</summary>

以下源码文件用于生成本页说明：

- [Documentation/FAQ.md](https://github.com/newren/git-filter-repo/blob/main/Documentation/FAQ.md)
- [git-filter-repo](https://github.com/newren/git-filter-repo/blob/main/git-filter-repo)
- [README.md](https://github.com/newren/git-filter-repo/blob/main/README.md)
- [install.py](https://github.com/newren/git-filter-repo/blob/main/install.py)
- [t/t9390-filter-repo-py.sh](https://github.com/newren/git-filter-repo/blob/main/t/t9390-filter-repo-py.sh)
</details>

# 常见问题与故障排查

`git-filter-repo` 是一个用于重写 Git 仓库历史的 Python 工具，相比 `git filter-branch` 性能更高、语义更清晰。由于其会在原地改写历史（产生新的 commit hash），运行过程中会遇到若干常见错误与特殊场景。本页汇总典型的故障现象、根因解释与处理思路。

## 安装与环境类问题

### Windows 上的 `NameError: name 'git' is not defined`

这是社区中反馈最频繁的环境问题之一（参见 issue #360、#124）。其根因通常在于 `git-filter-repo` 脚本的 shebang 行或 PATH 环境配置不正确：脚本内部依赖 `git` 命令行调用，而 Windows 上若未将 Git for Windows 的 `bin` 目录加入 PATH，或未使用 `git filter-repo`（Git 2.24+ 内置别名）调用，Python 进程会找不到 `git` 可执行文件 资料来源：[Documentation/FAQ.md]()。

修复建议：
- 使用 `git filter-repo ...` 而非直接调用 `git-filter-repo ...`，让 Git 内部解析脚本路径；
- 或将 `git-filter-repo` 放入 `$PATH` 后用 `python3 git-filter-repo ...` 显式调用；
- 通过 `install.py` 脚本进行安装，让它自动配置路径 资料来源：[install.py]()。

### Python 兼容性

源码顶部对最低 Python 版本与关键依赖做了声明，运行前应确认仓库内 Python 解释器版本满足要求 资料来源：[git-filter-repo:1-30]()。

## 命令参数与语法错误

### `path-rename foo:` 触发 "Empty path component found in input fast-import"

执行 `git filter-repo --path-rename OUYA-ODK: --force` 时抛出该错误（issue #714）。这是因为 `path-rename` 的目标路径为空，fast-import 规范不允许出现空路径组件。解决方案是为目标路径提供一个明确的非空前缀，或使用 `--path-rename OUYA-ODK:./` 配合显式子目录 资料来源：[Documentation/FAQ.md]()。

### `assert(usoa == intermediate)` 失败

在已有 `.git/filter-repo` 元数据的仓库上重复运行 filter-repo 时，可能触发该断言（issue #618）。原因是 filter-repo 在 `$GIT_DIR/filter-repo` 中保存了运行状态元数据，重复运行前若未清理历史会与当前提交不匹配。处理方式为删除 `.git/filter-repo` 后重新执行，或使用 `--force` 强制运行 资料来源：[Documentation/FAQ.md]()。

### `--replace-text` 仅对特定类型文件生效

issue #74 询问能否只对某些扩展名的文件做文本替换。`--replace-text` 默认作用于所有 blob，没有按扩展名过滤的内置开关。常用变通方案：先通过 `--path-glob '*.ext'` 限定文件范围，再串接 `--replace-text` 资料来源：[README.md]()。

## 历史重写表现与副作用

### 合并提交 hash 改变（gpgsig 被剥离）

issue #139 指出 GitHub 上的 merge commit 经过 `git filter-repo --force` 后 hash 变化，原因是脚本默认会丢弃 `gpgsig` 字段。若需保留签名，应使用 `--preserve-commit-encoding` 并配合自定义 callback 在 `commit_message` 钩子内手动还原签名行 资料来源：[Documentation/FAQ.md]()。

### `--analyze` 在大仓库上失败并报 "fatal: unable to read ..."

issue #711 报告在 ~1.5M commit 的 monorepo 上运行 `git filter-repo --analyze` 时失败，但该 commit 在任何 Git 命令下都查不到。这通常是 fast-export 流在并行处理某一对象时发生了破损，解决方案是先在干净仓库上验证 `git fsck`，必要时使用 `--no-replace` 或降低并行度 资料来源：[t/t9390-filter-repo-py.sh]()。

### 需要多 pass 过滤

issue #748 提出"多 pass 过滤"的需求。官方文档建议：先克隆一个全新工作副本作为"原始仓库"，所有 pass 都在该副本上链式执行；最终一次强制推送目标分支到远端 资料来源：[Documentation/FAQ.md]()。

## 最佳实践与防御性检查

| 步骤 | 操作 | 目的 |
|---|---|---|
| 1 | 备份或 clone 一份镜像 | 避免远端不可达时无法回滚 |
| 2 | 先在克隆副本上 dry-run | 验证 filter 参数语义 |
| 3 | 运行前清理 `.git/filter-repo` | 防止 assert 失败 |
| 4 | 推送时使用 `--force` | 覆盖远端历史 |
| 5 | 通知协作者重新克隆 | 避免旧历史污染 |

资料来源：[Documentation/FAQ.md]()、[README.md]()。

常见故障排查流程可总结如下：

```mermaid
flowchart TD
  A[运行 git filter-repo] --> B{报错类型?}
  B -->|NameError/ImportError| C[检查 PATH 与 Python 版本]
  B -->|Empty path component| D[修正 --path-rename 目标]
  B -->|assert usoa == intermediate| E[删除 .git/filter-repo 元数据]
  B -->|unable to read commit| F[git fsck 检查仓库完整性]
  C --> G[重试]
  D --> G
  E --> G
  F --> G
```

另外，社区中常见的两个误区需要特别强调：第一，filter-repo 会丢弃未跟踪的引用与 reflog，运行后无法通过 `git reflog` 找回原 commit；第二，release 节奏稳定（v2.27.x ~ v2.47.0），遇到 bug 优先确认是否已在新版本中修复（issue #638 跟踪 latest stable 修复） 资料来源：[README.md]()。

## 小结

- 涉及 PATH、Python、签名、断言这几类问题最常见，排查时先确认环境与元数据；
- 复杂重写场景（多 pass、monorepo、类型签名合并）应在副本上反复验证后再推送到主仓库；
- 善用 `--analyze` 与单元测试套件 `t/t9390-filter-repo-py.sh` 的回归用例能提前暴露问题。

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Doramagic 踩坑日志

项目：newren/git-filter-repo

摘要：发现 11 个潜在踩坑项，其中 4 个为 high/blocking；最高优先级：安装坑 - 来源证据：Error on "path-rename foo:"。

## 1. 安装坑 · 来源证据：Error on "path-rename foo:"

- 严重度：high
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安装相关的待验证问题：Error on "path-rename foo:"
- 对用户的影响：可能阻塞安装或首次运行。
- 证据：community_evidence:github | https://github.com/newren/git-filter-repo/issues/714 | 来源讨论提到 python 相关条件，需在安装/试用前复核。

## 2. 安装坑 · 来源证据：Make a minor release?

- 严重度：high
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安装相关的待验证问题：Make a minor release?
- 对用户的影响：可能增加新用户试用和生产接入成本。
- 证据：community_evidence:github | https://github.com/newren/git-filter-repo/issues/638 | 来源讨论提到 windows 相关条件，需在安装/试用前复核。

## 3. 安装坑 · 来源证据：`git filter-repo --analyze` fails on non-existent commit that no Git tool can find

- 严重度：high
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安装相关的待验证问题：`git filter-repo --analyze` fails on non-existent commit that no Git tool can find
- 对用户的影响：可能增加新用户试用和生产接入成本。
- 证据：community_evidence:github | https://github.com/newren/git-filter-repo/issues/711 | 来源类型 github_issue 暴露的待验证使用条件。

## 4. 安全/权限坑 · 来源证据：dropping gpgsig on merge commit changes commit id

- 严重度：high
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题：dropping gpgsig on merge commit changes commit id
- 对用户的影响：可能影响授权、密钥配置或安全边界。
- 证据：community_evidence:github | https://github.com/newren/git-filter-repo/issues/139 | 来源类型 github_issue 暴露的待验证使用条件。

## 5. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 证据：capability.assumptions | https://news.ycombinator.com/item?id=48986703 | README/documentation is current enough for a first validation pass.

## 6. 运行坑 · 来源证据：`assert(usoa == intermediate)` could fail in subsequent filter-repo runs

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个运行相关的待验证问题：`assert(usoa == intermediate)` could fail in subsequent filter-repo runs
- 对用户的影响：可能增加新用户试用和生产接入成本。
- 证据：community_evidence:github | https://github.com/newren/git-filter-repo/issues/618 | 来源讨论提到 python 相关条件，需在安装/试用前复核。

## 7. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 证据：evidence.maintainer_signals | https://news.ycombinator.com/item?id=48986703 | last_activity_observed missing

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 证据：downstream_validation.risk_items | https://news.ycombinator.com/item?id=48986703 | no_demo; severity=medium

## 9. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 证据：risks.scoring_risks | https://news.ycombinator.com/item?id=48986703 | no_demo; severity=medium

## 10. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 证据：evidence.maintainer_signals | https://news.ycombinator.com/item?id=48986703 | issue_or_pr_quality=unknown

## 11. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 证据：evidence.maintainer_signals | https://news.ycombinator.com/item?id=48986703 | release_recency=unknown

<!-- canonical_name: newren/git-filter-repo; human_manual_source: deepwiki_human_wiki -->
