# Git 线性历史处理手册
这份文档记录一个常见误判场景:
- 表面上看,`main.v4` 像是把 `main.v5` 合入了
- 实际上,代码只是在 `main.v4` 上 cherry-pick 了某一笔提交
- 之所以图上看着像合并,是因为后来又出现了一个 merge commit
这个仓库里对应的真实案例是:
- `main.v5` 上原始提交:`1e327c18`
- `main.v4` 上对应的 cherry-pick:`207ea35e`
- 后面生成的 merge commit:`12aa1738`
- 最后把历史重写成线性提交后,`main.v4` 变成了普通提交链
## 先判断是不是"真的合入"
先看分支图和提交父节点:
```bash
git log --oneline --decorate --graph --all --date-order
git show --no-patch --pretty=raw <commit>
```
重点看:
- 如果一个 commit 有两个 parent,它就是 merge commit
- 如果两边提交信息相同、diff 也相同,通常就是同一笔补丁的 cherry-pick
再用 patch-id 验证是不是同一个改动:
```bash
git show <commit-a> --pretty=format:%b --patch | git patch-id --stable
git show <commit-b> --pretty=format:%b --patch | git patch-id --stable
```
如果 patch-id 相同,说明代码改动本质一致,只是 commit hash 不同。
## 这个案例里发生了什么
当时的链路是:
```text
207ea35e <- main.v4 上的 cherry-pick
1e327c18 <- main.v5 上的原始提交
12aa1738 <- merge commit,带着两个 parent
```
问题不在代码,而在历史结构:
- `207ea35e` 只是把 `1e327c18` 的改动复制到 `main.v4`
- `12aa1738` 又把 `1e327c18` 作为第二父提交接进来
- 所以图上看起来像 `main.v5` 合入 `main.v4`
## 如果你想把它改成"线性历史"
目标是:
- 保留代码改动
- 去掉 merge 结构
- 让 `main.v4` 只是一串普通提交
操作步骤:
### 1. 回到 merge 之前的基点
```bash
git reset --hard <merge-commit-之前的那个提交>
```
在这个案例里是:
```bash
git reset --hard 207ea35e
```
### 2. 把 merge commit 按第一父提交重放成普通提交
```bash
git cherry-pick -m 1 <merge-commit>
```
`-m 1` 的意思:
- 以第一个 parent 作为主线
- 把 merge commit 相对这条主线带来的改动,重新生成一个普通 commit
在这个案例里:
```bash
git cherry-pick -m 1 12aa1738
```
### 3. 如果还有后续提交,继续逐个 cherry-pick
```bash
git cherry-pick <commit>
```
在这个案例里,后面又补了:
```bash
git cherry-pick 8af8bfe9
```
## 如何确认结果正确
看分支链:
```bash
git log --oneline --decorate --first-parent -n 6 main.v4
```
你希望看到的是普通线性提交,而不是一个带两个 parent 的 merge commit。
再看当前 commit 的父节点:
```bash
git show --no-patch --pretty=raw HEAD
git rev-list --parents -n 1 HEAD
```
如果只显示一个 parent,就说明已经是线性历史。
## 推送到远端
如果本地历史重写了,远端通常需要强推:
```bash
git push --force-with-lease origin main.v4
```
为什么用 `--force-with-lease`:
- 比 `--force` 安全
- 它会检查远端分支有没有被别人更新
- 如果远端有新提交,它会拒绝覆盖
## 这次案例的最短命令序列
```bash
git reset --hard 207ea35e
git cherry-pick -m 1 12aa1738
git cherry-pick 8af8bfe9
git push --force-with-lease origin main.v4
```
## 以后遇到类似问题的判断顺序
1. 先看提交图,确认是不是 merge commit
2. 再看 `--pretty=raw`,确认 parent 数量
3. 再比 patch-id,确认是不是同一笔改动
4. 需要线性化时,用 `reset --hard` 回到基点,再 `cherry-pick -m 1`
5. 最后用 `--force-with-lease` 推远端
分支揉在一起怎么办
安卓程序员_谢伟光2026-06-17 9:43