Loading... # Git Rebase 完全指南:从恐惧到精通 > 来源:https://www.brethorsting.com/blog/2026/01/git-rebase-for-the-terrified/ ## 前言 作为 OneBusAway 项目的维护者,我经常要求贡献者在合并前对他们的分支进行 rebase。回应往往是犹豫甚至恐惧。我理解这种感受——rebase 在开发者社区中有着毁坏工作的恶名,网上的各种警告更加剧了这种恐惧。 但事实是:rebase 出错的**最坏情况**不过是删除本地克隆并重新开始。就这样。你的远程 fork 仍然存在,主仓库也还在。你总是可以恢复的。 既然消除了恐惧,让我来展示如何正确地使用 rebase。 ## 为什么维护者要求 rebase 当你从 main 分支创建一个分支并工作几天后,main 分支仍在持续前进。其他 PR 被合并。当你的 PR 准备好时,你的分支历史已经与 main 产生了分歧。 虽然合并提交可以结合它们,但这会创建混乱的历史,提交交错在一起,使理解变更内容和原因变得更加困难。 Rebase 会将你的提交在当前的 main 分支上重放,就像你今天才创建这个分支一样。结果是清晰、线性的历史,更容易审查和 bisect(二分查找)以追踪 bug。  ## 实际操作命令 ### 第一步:配置上游仓库 首先,确保你已将上游仓库配置为 remote。如果你 fork 了一个仓库并克隆了你的 fork,你可能只有 `origin` 指向你的 fork: ```bash git remote -v ``` 如果没有看到主仓库,添加它: ```bash git remote add upstream https://github.com/OneBusAway/onebusaway-ios.git ``` ### 第二步:获取最新变更 从上游获取最新变更: ```bash git fetch upstream ``` ### 第三步:切换到特性分支 确保你在你的特性分支上: ```bash git checkout your-branch-name ``` ### 第四步:推送备份 在 rebase 之前,将当前工作推送到你的远程 fork。这为你提供了一个备份,以防出错时可以恢复: ```bash git push origin your-branch-name ``` ### 第五步:执行 rebase 现在将你的分支 rebase 到上游的 main 分支: ```bash git rebase upstream/main ``` 如果没有冲突,你就完成了 rebase。如果有冲突,Git 会停止并告诉你哪些文件需要处理。 ## 理解冲突标记 当打开一个有冲突的文件时,你会看到类似这样的内容: ```git <<<<<<< HEAD const timeout = 5000; ======= const timeout = 10000; >>>>>>> upstream/main ``` 这在理解之前很困惑,但每个部分的意思是: - `<<<<<<< HEAD` 和 `=======` 之间的内容是**你的代码**,来自正在重放的提交 - `=======` 和 `>>>>>>> upstream/main` 之间的内容是**main 中的代码**,与你的代码冲突 你的工作是决定最终代码应该是什么样的。有时你想要你的版本,有时是他们版本,有时是两者的结合。删除标记并只保留你想保留的代码。 **推荐工具**:我总是使用 VS Code 来完成这一步。它的合并冲突 UI 是我找到的最清晰的:它在每个冲突上方显示"接受当前更改"、"接受传入更改"、"接受两者更改"和"比较更改"按钮。你可以逐个点击冲突,无需手动查找标记。 ## 当冲突变得棘手时 有些冲突是直接的:两个人以不同方式更改了同一行。选择一个或组合它们。 其他的则更困难。如果你在 rebase 多个提交,并且同一文件反复冲突,通常意味着你的更改以某种方式相互构建,无法干净地应用于新的基础。 ### 处理策略 **1. 先压缩,再 rebase** 如果你有很多小提交,在 rebase 之前将它们合并成一两个逻辑提交。更少的提交意味着更少的冲突机会。 ```bash # 交互式 rebase 来压缩提交 git rebase -i HEAD~n # n 是要压缩的提交数 ``` **2. 中止并尝试不同的方法** 如果冲突太多,执行 `git rebase --abort` 并考虑你的分支是否分歧太远。有时从 main 创建新分支并手动重新应用你的更改会更容易。 ```bash git rebase --abort git checkout main git pull upstream main git checkout -b new-branch-name # 手动应用你的更改 ``` **3. 使用 `git rerere`** 如果你发现自己反复解决相同的冲突,使用以下命令启用 rerere(重用记录的解决方案): ```bash git config --global rerere.enabled true ``` Git 会记住你如何解决冲突,并在下次自动应用相同的解决方案。 ### 解决冲突后 解决每个文件的冲突后: ```bash git add path/to/resolved/file git rebase --continue ``` 重复直到 rebase 完成。如果出现问题并想要中止: ```bash git rebase --abort ``` 这会将你的分支恢复到开始前的状态。  ## 验证你的更改 rebase 后,验证你的更改仍然有效: ```bash git log --oneline upstream/main..HEAD ``` 这只显示你领先于 main 的提交。确保它们看起来正确。然后构建项目并运行测试。rebase 有时会导致微妙的问题,如果上游更改与你的工作冲突,而合并没有捕获到这些问题。 ## Force Push 这是人们紧张的地方。rebase 后,你的本地分支已经与远程分支分歧。正常的 `git push` 会失败。你需要 force push: ```bash git push --force-with-lease origin your-branch-name ``` `--force-with-lease` 标志比 `--force` 更安全,因为如果有人在你上次获取后推送到你的分支,它会失败。这可以防止你意外覆盖其他人的工作。 **重要**:永远不要 force push 到 main 或任何共享分支。只对你自己的特性分支进行 force push。 ## 当一切都出错时 如果你把事情搞砸了,无法弄清楚如何恢复,这里是"核选项": 1. 将你想保存的任何工作推送到你的远程 fork(甚至到临时分支) 2. 删除你的本地克隆 3. 从你的 fork 重新克隆 4. 再次添加上游 remote 5. 重新开始 rebase 过程 这对我来说从未失败过。你的提交存在于 GitHub 上,直到你明确删除它们。你总是可以恢复。 ## 重要注意事项 **Rebase 会重写提交历史。**这对于只有你工作的特性分支是没问题的。对于其他人基于其工作的分支,这**不行**。如果你在一个分支上与某人合作,在 rebase 之前协调,或者直接使用合并提交。 ## 总结 一旦你理解了你总是可以恢复,rebase 就不再可怕了。最坏的情况是几分钟的重新克隆。好处是清晰的项目历史,更容易理解和维护。 **核心要点**: 1. 在 rebase 之前推送备份 2. 使用 `--force-with-lease` 而不是 `--force` 3. 永远不要 rebase 共享分支 4. 理解冲突标记的含义 5. 最坏情况:重新克隆,一切恢复 --- ## 参考资料 - 原文:[Git Rebase for the Terrified](https://www.brethorsting.com/blog/2026/01/git-rebase-for-the-terrified/) - Git 官方文档:[git-rebase](https://git-scm.com/docs/git-rebase) - VS Code 合并冲突文档:[Resolve merge conflicts](https://code.visualstudio.com/docs/sourcecontrol/overview#_merge-conflicts) 最后修改:2026 年 01 月 14 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏