写 Git 相关的文章我其实犹豫了很久,因为网上一搜全是教程,但大部分都停留在“给你看命令”的层面。真正让刚入行的同学头疼的从来不是命令本身,而是那些别人踩过但没写出来的坑:为什么明明照教程 rebase 完,推送时被服务端弹回来?为什么 merge 回退以后代码反而丢了?为什么 GitLab 一直提示 something went wrong during merge pre-receive hook?这次的标题是《2026 年 掌握 Git Merge 与 Rebase 避免代码冲突》,我就把 Merge 和 Rebase 这两兄弟彻底讲透,从内核到实操,再配上这几年在真实项目里攒下的排查经验。这篇内容适合所有用 Git 做团队协作的工程师,尤其是那些每次冲突弹出来就头皮发麻的人。看完以后不敢说让你爱上解冲突,但至少能让你心里有底。
1. 内容整体设计与思路拆解
1.1 为什么既要 Merge 又要 Rebase
先说一个很实际的问题:既然 Merge 能完成代码合并,为什么还要学 Rebase?很多开发者的第一反应是“Rebase 能让历史更干净”,这个说法对,但太空了。我的理解是,两者解决的是不同层面的事情。Merge 描述的是“我这边发生过的故事”,它把分支上的每一次提交按时间线合并到一起,历史里有分叉、有合并点,完整但凌乱。Rebase 描述的是“我想要的故事”,它把你分支上的提交摘下来,重新放到另一个分支的最新位置之上,历史变成一条直线,干净但失去了“原本发生在哪”的上下文。
所以你在团队里看不到哪种方式绝对正确,只有哪种方式适合当前场景。功能分支往主干合入,我推荐用 Merge 保留完整脉络;功能分支需要同步主干最新代码,我推荐用 Rebase 来避免无意义的合并圈。这两个场景混着用,才是高手的方式。我见过最糟糕的团队是全组只准用 Rebase,结果只要有人 push 过共享分支,所有人都得反复 graft,最后把主干历史改得面目全非,出了事故还查不到是谁的提交。也见过全组只用 Merge,几个大功能同时开发的时候,合并图上密密麻麻全是箭头,代码评审里全是“please rebase”。
这里要明确一个概念:冲突不是 Merge 或 Rebase 带来的,而是 Git 在三方合并时发现两边都改了同一处代码,无法替你判断谁对谁错。理解了这一点,就不会再纠结“为什么 rebase 比 merge 更容易冲突”这种伪问题。两者只是触发冲突的方式不同:Merge 是在最终合并的那一下集中爆发,Rebase 是把冲突摊到每次提交的重放过程中。摊开来的好处是每次冲突范围小,定位容易,坏处是心理压力大,感觉没完没了。
1.2 冲突的本质:三方合并与合并基
想彻底搞懂冲突,就得从 Git 的合并原理说起。Git 做合并时并不是简单对比两个分支的差异,它的完整流程是:找到两个分支的共同祖先 commit,也就是合并基,然后对比“当前分支相对于合并基的变化”和“目标分支相对于合并基的变化”,双方都修改了同一行以及相关行,Git 无法自动取舍,就把它标记为冲突。
举个例子,你和同事都在main.go里改了第 20 行,你加了一个参数,同事改了调用方式。对 Git 来说,这两个改动都基于同一个原始版本,但各自产生的新版本互不兼容,它不会帮你决定谁优先,只能把两段内容都留在文件里,加上冲突标记。这就是为什么很多时候你觉得“明明只改了一行,怎么会冲突”,因为在 Git 眼里,你们修改的行太近了,或者对同一个函数签名、同一个结构体字段造成了影响。
搞清楚这个原理以后,解决冲突的思路就清晰了:你不是在“选择哪段代码”,而是在“给 Git 讲解两个分支各自的意图,并制造一个新的合理结果”。我解决冲突时从来不是直接看<<<<<<<那段就删掉另一段,而是先看两个版本的上下文,确认谁改了谁、谁的改动依赖谁的改动,再决定是保留一边、合并两边,还是重写一个新版本。
1.3 别人不会告诉你的协作铁律
我在这部分想先说三条真正重要的协作纪律,这三条是我在带团队时最强调的,也是很多教程根本不讲的。
第一条,绝不对共享分支做 Rebase。共享分支指的是已经推送到远端、并且有其他人基于它拉过分支的仓库分支,比如develop、main或者团队公认的集成分支。一旦你对这类分支做了 Rebase,由于 Relase 会改写 commit 的 hash,其他同事本地保存的历史会和远端彻底对不上,他们再git pull时就会得到一堆莫名其妙的重复提交、merge 冲突,甚至丢失推送权限。这不是理论风险,我在项目里亲眼见过有人把developrebase 到自己的 feature 分支上,结果全组三十多号人当场无法同步,最后只能靠git reset --hard到指定 commit 才恢复。所以这条没有商量余地。
第二条,提交前想清楚这次提交的原子性。一个提交只做一件事,这样无论 Merge 还是 Rebase,冲突范围都会小很多。如果一次提交里同时改了接口定义、业务逻辑、测试用例和文档,那冲突时你几乎无法判断哪部分该留代码、哪部分该留注释、哪部分只是格式化产生的噪声。我一般会要求提交粒度控制在“能通过一次 code review”的程度,大一点没关系,但不能杂。
第三条,推送前看一眼远端是否被其他人更新过。很多冲突其实可以在推送前避免,具体操作就是先拉取远端代码,再在本地把别人的提交合并或变基到你的分支上,确认没有冲突再推送。这一步在团队协作中价值巨大,它能把冲突留在本地,而不是等到创建 Merge Request 的时候才被服务端拦截。很多人看到Your merge request is almost ready!这个提示,以为只是 CI 没过,其实 GitLab 已经在检查合并可行性了,如果分支之间有冲突,Merge Request 根本创建不了完整状态。
2. 核心细节解析与实操要点
2.1 冲突标记长什么样,怎么读
很多刚接触冲突的同学看到编辑器里满屏的<<<<<<<直接被劝退。其实冲突标记格式非常固定,总共三段:<<<<<<< HEAD以下到=======之前是当前分支的内容,=======以下到>>>>>>> xxx是要合并进来的分支内容。我建议第一次解冲突的人不要直接淹没在代码里,先在编辑器里搜一遍冲突标记,把所有出现冲突的文件列出来,每个文件里标记的数量统计清楚,再逐个击破。
解冲突时有个技巧:优先分析文件类型。比如package-lock.json、go.sum这类锁文件产生冲突,通常不需要手动编辑,重新运行npm install或go mod tidy就能自动修复。而*.java、*.go、*.ts这类源码文件,就需要仔细看变更逻辑。还有一类是配置文件,比如.env.example、application.yml,两边都会改,冲突时要把所有环境变量都保留下来,各自的新增项合并到一起。
解决完文件内冲突后,下一件事是检查是否漏掉内容。我见过最坑的情况是:表面上解决了冲突,但某个文件里的<<<<<<<标记没删干净,编译也过了,直到有人运行git diff --check才在 CI 里发现。所以要习惯在提交前执行git add -A,然后用git diff --cached --check检查暂存区残留的空格和冲突标记,再把已跟踪的冲突标记全部清掉。这个习惯能避免不少潜在问题。
2.2 Merge 的关键参数与回退操作
Merge 命令看起来简单,但几个参数用错了会有完全不同的效果。默认的git merge feature/xxx会产生一个 merge commit,历史里多了个合并点。有些团队希望历史保持绝对线性,会规定用git merge --ff-only feature/xxx,这个参数的含义是“只有在能快进的情况下才允许合并”,如果目标分支落后于源分支,就直接报错。这个参数对补丁类仓库很实用,但对常规功能开发不太推荐,因为你会被迫先做一次繁琐的同步操作。
另一个高频参数是git merge --no-ff feature/xxx,含义是即使能快进也强制创建一个 merge commit。这个做法的价值是保留一条完整的合入记录,后续追溯问题时能清楚知道“这个功能是什么时候、通过哪个合并进入主干的”。我在发布分支上基本都用--no-ff,因为回滚时可以直接git revert -m 1 <merge-commit-hash>,方便得多。
顺着回滚这个话题,我要着重说一下如何在 IDEA 中回退 Merge 操作。最常见的场景是:刚合并完,代码来了,CI 也跑了,然后发现合入的分支带上了不该有的改动。第一个误区是直接执行git revert,这个操作会产生一个反向提交,对 merge commit 来说默认会走-m 1参数,即忽略第二个父分支的变更。如果你希望整段合入撤销得干干净净,正确的思路有两种:一是用git reset --hard <merge前的commit>,但此时要考虑远端是否已经被其他同事拉取,如果远端已更新,reset 会带来灾难;二是用git revert <merge-commit> -m 1,保留历史记录,手动处理冲突后提交。
IDEA 里对应的操作路径是:在 Git 面板选中分支历史,找到那个 merge commit,右键选择 “Revert Commit”,弹出对话框里勾选 “Revert Merge Commit using first parent” 选项,意思是告诉 Git 回退到合并前的第一个父分支状态。这个操作相对安全,因为它是新增一个反向提交而不是改写历史,真正适合协作分支。
2.3 Rebase 的交互式用法与 amend 技巧
说完 Merge 再来说 Rebase 的核心操作。git rebase main是同步主干最常见的方式,它会把你当前分支上的提交逐个“重放”到 main 最新提交之上。如果中间某个提交与 main 上的改动冲突,Git 会暂停在那一步,等你把冲突解决完、git add、再执行git rebase --continue。很多人忽略一个细节:冲突解决完后,第一步应该先检查当前分支上还挂着几个提交,防止 Rebase 过程中提交顺序混乱。
交互式 Rebase 是另一个法宝。git rebase -i HEAD~3会打开一个编辑器,列出最近 3 个提交,你可以把pick改成reword修改提交说明,改成squash压缩提交,改成edit中途暂停修改内容,或者改成drop丢弃这个提交。这里我要专门讲一下git commit --amend怎么使用,因为很多人把它和 Rebase 混在一起。git commit --amend的核心用途是修改最近一个提交,最常见的使用场景是:刚提交完发现漏了一个文件,或者提交说明写错了一个字,直接改完再 commit --amend 就不会产生多余的历史提交。但注意,这个命令同样会改掉 commit 的 hash,如果这个提交已经被推到远端,需要谨慎;只有确认这个提交还在本地、没有其他人基于它拉分支时,才可以安心修改。
还有个小技巧:如果你想修改的不是最近一次提交,而是中间某个提交,普通 amend 是不行的,得进入交互式 Rebase,把目标提交的pick改成edit,Git 会在该提交结束后停下来,你修改文件后git commit --amend,再git rebase --continue继续重放后续提交。这样既能修改历史中间的提交,又能保持分支整体干净。
2.4 让冲突从源头变少的四个日常习惯
冲突虽然不可避免,但通过习惯能显著减少次数。我在团队里一直强调这几点,虽然看不到立竿见影的效果,但三个月后对比代码评审记录,冲突频率真的能降一个量级。
第一个习惯是按模块切线。多人同时改同一份文件的不同区域,冲突概率不大,但一旦有人动了文件头部的 import 区、包名声明或公共常量定义,后面全乱。所以分工时尽量按模块或包名划分,不要你们这些人全挤在一个utils.go里加函数。
第二个习惯是小步提交、多推远端。本地攒了一大堆改动再 push,和同事其他提交撞上的概率会直线上升。原因是 Git 合并时对比的是提交之间的差异,提交越多、时间跨度越大,中间交叉修改的可能性越高。相反,每天至少 push 一次自己的分支,每隔两三天就从主干拉一次最新代码,就能把冲突的“体积”控制在最小。
第三个习惯是不要轻易格式化别人的代码。一个很常见的现实场景:你接手一个项目,发现代码风格不合你意,于是打开自动格式化,把整份文件全部重新排版。等到你和同事 merge 时,两边都改了文件,Git 因为大段空白行的变化而判定冲突,实际逻辑反而没人动过。我的建议是只格式化自己修改过的那些行,不要动无关区域。
第四个习惯是把通用配置和业务代码分离。版本间经常变动的依赖版本号、环境变量、构建参数尽量集中放在少数几个文件里,这样即使改动频繁,冲突影响面也有限。如果这些配置散落在大量代码里,每一次升级都会引发连锁冲突,排查起来会让你怀疑人生。
3. 实操过程与核心环节实现
3.1 实操:一个典型的 Merge 冲突完整解决流程
假设你正在维护一个后端项目,主干develop上有人合入了一个用户权限模块,你在feature/order分支上做了订单流程改造,两边都改了OrderService.java。合并时执行:
git checkout develop git pull origin develop git merge feature/orderGit 提示CONFLICT (content): Merge conflict in OrderService.java,还列了几个自动合并成功的文件。这里有个新手常犯的错误:一看到冲突就慌,不知道该改哪边。按照我前面说的流程,先用编辑器打开OrderService.java,看到冲突标记,再逐个分析。
我的处理顺序是这样的:先看<<<<<<< HEAD部分,也就是主干上的版本,通常是同事新加的用户权限判断;再看>>>>>>> feature/order部分,这是我的订单逻辑。如果两边只改了不同方法体,Git 其实能自动合并,不用你管。真正冲突是因为两人在同一个方法内部改动了相近的行。这时候要判断自己的订单逻辑是否依赖同事新增的权限判断。比如我的createOrder方法里调用了一个同事新加的checkPermission(userId),那应该保留HEAD里的权限判断代码,同时保留我自己的订单创建逻辑,组合成一段完整的方法。
处理好一个文件后,执行git add OrderService.java,继续检查其他有冲突的文件。全部处理完,执行git commit,Git 会提供一个默认的 merge commit 模板,确认内容无误后提交。如果中途发现自己实在搞不定,或者临时要切去看另一个问题,先执行git merge --abort回到合并前的状态,这个操作不会留下任何痕迹,非常安全。
这里我特别想强调一个细节:merge 时的冲突不全是需要你修改的。有时候 Git 会把同一文件里多个不同区域的改动都标成冲突,但其实每个区域都是“保留双方”这么简单的处理方式,你根本不用动代码逻辑,只要选择一个策略,比如“两边都保留”,然后把冲突标记删除。
3.2 实操:一个典型的 Rebase 冲突完整解决流程
Rebase 的冲突解决跟 Merge 有些区别,过程更像是“过滤器”一样逐个提交重放。场景:你在feature/order分支上有 3 个提交,develop更新了,你想把 develop 的最新改动拿过来,同时保持自己分支历史干净。
git fetch origin git rebase origin/develop假设第 1 个提交重放时没有冲突,第 2 个提交重放时冲突了。Git 提示error: could not apply 8d2a4f5... fix: 增加订单超时处理,此时你处于“rebase 暂停”状态,当前 HEAD 已经指向你要重放的位置之前。和 merge 一样,打开冲突文件手动修改,改完git add,然后:
git rebase --continue注意,--continue之后会打开编辑器,让你确认这个提交的 commit message。这里不建议顺手乱改提交说明,除非你明确知道自己在干嘛。然后 Git 会继续重放第 3 个提交,如果又冲突,就继续解,直到所有提交都重放完。整个过程中如果有个提交越解越乱,可以随时执行git rebase --abort,直接回到变基前的状态,这一点和 merge 的--abort一致。
Rebase 冲突的一个特点:同一个文件可能在多个提交里反复触发冲突。有人以为解完一次就结束了,结果下一个提交又报同样文件冲突,心态直接崩了。实际上这不一定是坏事,说明你只要解决一次,第二次就可以直接把同样方式处理。我通常会用git show <commit-hash> --stat先大致了解一下每个提交改了哪些文件,再决定要不要一次性处理。如果冲突集中在同一个文件,也可以考虑先git rebase --abort,回到原状态,用git rebase -i把多个提交合并成一个,再重新执行 rebase,这样冲突只触发一次。不过这条路会改变提交粒度,需要和团队同步确认。
3.3 实操:IDE 中回退 Merge 与处理 pre-receive 错误
团队里不少人习惯用 IDEA 的图形界面操作 Git,我也经常用,确实省心。但有个问题,很多图形化操作隐藏了底层命令,出问题后你根本不知道它在背后执行了什么。我建议图形化操作至少知道对应的命令行发生什么。
举个例子,IDEA 中有个 “Git -> Rebase my GitHub fork onto develop” 之类的菜单选项,其实等同于是git rebase develop。如果 rebase 过程中冲突了,IDEA 会弹出一个 Resolve Conflicts 对话框,列出所有冲突文件。你可以双击文件,在左边和中间区域手动选择保留哪几行代码。处理完可以直接点 “Continue Rebase”。如果你觉得弄错了,也可以点 “Abort Rebase” 回到之前的状态。
再来说回退 Merge。IDEA 中当你选中一个 merge commit 单击右键,选择 “Revert Commit”,会在“Revert Merge Commit”的选择里让你指定 parent 参数,1 表示保留主干提交历史,2 表示保留被合并分支的描述。默认是 1,适合回退整个合并到之前的主干状态。执行完后会生成一个新的 commit,不会改写历史,所以可以安全推送到远端。但要小心,如果这个 merge commit 之后有人已经在它的基础上继续提交,直接 revert 会制造冲突,你需要手动解,或者重新考虑是否值得回退。
另一个在实际项目中几乎每个人都遇过的报错是:推送时提示! [remote rejected] develop -> develop (pre-receive hook declined),在 GitLab 里面通常会配合一句话something went wrong during merge pre-receive hook. prevented by server hook。第一次遇到的人通常很懵,因为本地的 commit 已经成功,但推送被服务端拒收。这个 hook 不是 Git 本身的行为,是 GitLab 管理员配置了个自定义 pre-receive hook,通常是用来做一些代码规范检查、次品阻止、防止错误合入保护分支等。你要做的不是绕过它,而是查看服务端 hook 的日志或提示,可能它只是要求你确保提交信息包含 issue 编号,或者禁止从非develop分支直接推送main。这类规则是强制性的,必须理解并遵守,不要试图用push --force绕过,那样只会让情况更糟。
4. 常见问题与排查技巧实录
4.1 高频报错速查表:从服务端 hook 到分支校验
我做了一个针对高频报错的速查表,按实际项目中的频率排了序,每条都标注了推荐的解决方向。这不是让你背命令,而是在下次遇到问题时给你一条经验路径,不慌不乱。
| 报错或提示 | 出现场景 | 推荐处理方式 |
|---|---|---|
Your merge request is almost ready! | 在 GitLab 中创建 MR 前的提示 | 通常代表 CI 或冲突检查未通过,先查看 Pipeline 状态和 merge 可行性,不要急着 Merge |
something went wrong during merge pre-receive hook. prevented by server hook | 推送本地 commit 到受保护的分支 | 查看 GitLab 服务端 hook 日志,确认规则原因,按规则修改后重新推送 |
validate branches another open merge request already exists for this source | 想为同一个 source 分支再次创建 MR | 先去现有 MR 页面查看或更新它,不要重复建 MR,分支校验是自动的 |
rebase the current branch | 通常在 GUI 或 CI 中提示分支与目标分支有分叉 | 拉取最新目标分支后执行git rebase origin/develop,或使用 GUI 的 rebase 操作 |
no rebase提示 | 某团队禁止对共享分支做 rebase | 不要强行 rebase,改用 merge 或先与团队同步策略,避免破坏远端历史 |
git commit --amend误操作后推送失败 | 已 push 的提交被修改后再次 push | 如果远端还没有人拉取,可执行强制推送git push --force-with-lease,否则就新建一个提交撤回 |
这个表格里我再展开讲两个最坑的。第一个是pre-receive hook错误,实际工作中 80% 的情况并不是代码问题,而是提交信息格式、权限校验或者目标分支策略导致的。出现这个报错后,最快的排查方式是在 GitLab 管理后台看最近一次 hook 执行的日志,日志里通常会写明拒绝原因。如果项目组没有维护良好的 hook,你也可以在本机用git push时的输出信息观察到具体错误文本,然后针对性地修改。pre-receive hook本身并不是惩罚,它是保护协作流程的第一道防线。
第二个是another open merge request already exists for this source。这类提示在 GitLab 里比较常见,原因是同一个源分支已经有一个未关闭的 MR,你想再建一个会被拒绝。正确的做法是回到已有的 MR 页面,在新改动 push 上去后 MR 会自动更新,而不是重新创建一个重复的 MR。有个小技巧:如果你确实想用同一个源分支开一个全新的 MR,可以先关闭现有的 MR,再重新新建。但注意,关闭 MR 并不会删除分支,分支的联动校验依然存在。
4.2 解冲突时最容易被忽略的两个隐藏问题
第一个容易被忽略的是Re-merge 时的找回方式。很多人处理完冲突后直接提交,然后发现少了一些原来分支上的改动,急得要回滚。其实有几个更温和的回退方法:一是git revert单个提交;二是git reset --hard ORIG_HEAD,不过这个操作只对刚合并之后的瞬间有效,如果后续还有新的提交,ORIG_HEAD可能已被覆盖。还有一种是git reflog,这是我从入行开始就一直强烈推荐的命令,它记录了当前分支引用指针的所有移动历史。当你把一次 rebase 或 merge 搞砸了,git reflog能帮你找到操作前的 commit hash,然后用git reset --hard <那个hash>或git checkout -b <新分支> <那个hash>恢复现场。很多人不知道reflog,导致一遇到 reset 或 rebase 就把历史弄丢了。我说实话,Git 比你想象中更耐用,只要 reflog 还在,大部分错误都能找回。
第二个容易被忽略的是文件模式变更导致的假冲突。有些文件冲突不是因为内容改了,而是因为一边把文件从普通文本改成了软链接,或者修改了执行权限,导致 Git 认为文件类型变了。这类冲突标记里通常没有实际的代码内容差异,只有一行old mode 100644 / new mode 100755之类的信息。处理方式也很简单:选定一方保留即可,或者用git checkout --theirs xxx、git checkout --ours xxx来快速选择。这种假冲突最坑的一点是 merge 时 Git 自动检测不到具体内容差异,但你又不能忽略它,否则后续其他分支合并时会一直冒出来。我建议在解决完同类冲突后,执行git ls-files --stage看看暂存的模式是否统一。
4.3 个人常用的三个防范性技巧
第一个是推送时优先使用--force-with-lease而非--force。当你确实需要改写已推送的 commit(比如 amend 或 rebase 后),用git push --force-with-lease会在推送前检查远端是否和本地预期一致,如果远端已经被其他人更新了,就拒绝推送,避免意外覆盖别人的提交。这个参数只允许在你确认自己了解远端状态的前提下使用。我见过有人图省事用--force覆盖了一次远端,结果让别人本地历史全部乱掉,最后只能通过git reflog在别人机器上找回。永远不要小看共享分支的强推风险。
第二个是在本地模拟冲突,而不是直接推到共享分支制造冲突。如果你的分支和主干分叉较多,在 push 之前先本地执行一次模拟 merge:
git fetch origin git merge --no-commit origin/develop如果冲突了,你会看到冲突文件,可以安全地用git merge --abort退出。这个操作不会产生任何真实合并提交,只是让你提前知道冲突的具体位置,可以在提交前主动解决,减少在 MR 阶段浪费时间。
第三个是养成git diff --check系列检查的习惯。每次提交前我都习惯运行:
git diff --check git diff --cached --check这两个命令分别检查未暂存和已暂存的变更中是否存在结尾空格、空白错误以及冲突标记。配合编辑器自带的 Git 插件提示,基本能把低级问题扼杀在本地。
4.4 几个场景下的最终建议
最后这部分我想把前面讲的内容串到几个真实场景里,给不同协作方式的团队一些明确建议。
如果你在小型开源项目或个人项目中开发,可以大胆多使用 Rebase 保持历史整洁。因为分支不会长期共享,你甚至可以频繁地修改未推送的提交,用git commit --amend、git rebase -i来整理历史。你的目标应该是“提交历史像一封封精心的信”,而不是散乱的草稿。
如果你在中型企业团队中做常规业务迭代,我建议主干合并用merge --no-ff保留合入痕迹,功能分支同步用rebase避免分叉。每当功能完成,合并到 Dev 环境测试后统一删除源分支,这样 MR 列表干净,回滚线索清晰。
如果你在大型多团队协作的仓库里面,尤其是 monorepo 这种很多人共用一个仓库的场景,冲突几乎不可能完全避免。此时最关键的不是纠结用 rebase 还是 merge,而是建立一套自动化的“冲突预警”机制,比如 MR 创建后立刻触发一个检测流水线,测试目标分支与源分支的模拟合并是否能成功。一旦检测出冲突,就由负责人主动协调两边开发者,而不是等到合并时再砰地一下撞上。
至于那些容易产生大段公共配置改动的项目,比如前端项目里package.json经常被各方更新,或者后端项目里多个团队都在动pom.xml,我强烈建议专门定一个“配置专员”角色,负责统一处理这类文件的合入与冲突,不要逼着每个工程师每次都在这些无趣的行里痛苦挣扎。这个思路看起来很小,但在实际团队里价值巨大,能显著减少“冲突恐惧症”。
最后再分享一点我个人的习惯
我这几年的工作流有一个不变的核心:把冲突尽量在本地解决,而不是等到 MR 阶段让服务端来提示。每次开始一个新功能前,我会先从主干拉最新代码,以主干最新提交为基点切出自己的 feature 分支;开发过程中每隔两三天就git fetch一次,用git rebase origin/develop同步主干;功能做完后,合并回主干前先执行一次本地模拟合并,确认没有冲突再推到远端。这看起来有点繁琐,但它特别省心,不会出现那种打开 MR 发现一片红的尴尬局面。
再一个小的经验:如果你正在处理一个特别棘手的 rebase 冲突,花了很多时间、走了几条冤枉路,一定要学会git status和git reflog配合使用。前者让你知道自己当前在 rebase 的哪个阶段,后者让你知道刚才从哪里开始走错的。能定位状态,就能稳定地进入下一步,这比硬着头皮蒙着改代码强得多。
Git 这门工具,本质上学的是如何在混乱中维持秩序。Merge 和 Rebase 只是两种不同性格的工具,没有高低之分,只有你用得适不适合。希望这篇分享能给你一些启发,少走一些我已经踩过的路。愿你的分支永远干净,冲突永远可控。