ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Git合并冲突实战指南:从三方合并原理到解决流程与特殊场景

Git合并冲突实战指南:从三方合并原理到解决流程与特殊场景 1. 冲突不是灾难是 Git 给你的一次强制对话先别急着复制粘贴覆盖文件。很多新手甚至不少老手碰到CONFLICT这两个字就慌了第一反应是git checkout --ours或者干脆把对方代码手动改成自己想要的版本。我见过太多次因为这种“暴力解决”导致最终合并进主干的代码既丢了别人的功能又留下了没人看得懂的残留注释。Git 合并冲突的本质是两个分支在同一个位置对同一份内容做出了不同的修改它自己无法判断谁的意图优先级更高所以停下来等你拍板。这跟两个人同时改同一份合同里的同一个条款是类似的你不可能让机器替你决定哪一版更有道理。Git 把决定权交还给你这不是 bug而是最合理的设计。所以我的第一建议是把冲突当作一次代码审查的契机而不是麻烦。你在解决冲突的过程中实际上是在和另一位开发者进行异步代码评审你会被迫了解对方改了哪些逻辑、跟上一次提交相比差异在哪、这些改动和你的分支是否互相抵触。这个动作做完一遍你对整个项目结构的理解往往会上一个台阶。这篇东西我会从冲突产生的底层原理说起然后一步一步演示标准解决流程再把各种常见场景——包括很多人栽过跟头的master分支revert之后、其他分支再合回来时冲突爆炸的情况——单独拉出来讲透。最后给一份排查速查表希望能帮你以后遇到冲突时能气定神闲地一步步处理完而不是在终端里满头大汗。2. 搞清楚 Git 为什么会对同一段代码“无法抉择”2.1 合并冲突的本质三方合并Three-Way Merge要理解冲突必须先理解 Git 合并的基本方式三方合并。很多人在学 Git 时把它想象成“对比两份文件把不同的部分拼在一起”听起来简单真这么做会出大问题因为你不知道哪边的改动是新的、哪边是旧的。三方合并引入了第三个参照物两个分支的共同祖先merge base。假设你从master拉出一个分支feature之后两个分支各自演化。现在要把feature合回masterGit 会这样处理读取master当前版本的文件我们叫它ours读取feature当前版本的文件我们叫它theirs找到两个分支在分叉点上的同一个文件的版本我们叫它base。合并时Git 依次比较这三者的关系如果base到ours有改动且base到theirs没有改动那就以ours为准如果base到ours没有改动且base到theirs有改动那就以theirs为准如果两边都改了但改的是不同区域Git 自动把两块改动都保留下来如果两边都改了而且改的是同一区域Git 无法判断谁覆盖谁只能产生冲突。这就是为什么有些人会疑惑“我明明只是改了文件开头对方改了文件结尾为什么没有冲突”因为改动区域不重叠Git 可以安全地同时保留自动合并成功。而要是你俩恰好修改了同一行哪怕改出的结果一字不差Git 也会停下来让你确认。这个概念必须刻在脑子里因为它直接影响你解决冲突时的判断思路你要做的不是“挑一个好版本”而是对照base版本确认两边各自的意图然后把两个意图合理地融合到最终结果里。2.2 冲突标记长什么样每一行都在说什么当冲突发生时Git 会在冲突文件里嵌入标记。如果你用cat或者编辑器打开会看到类似下面的内容function init() { HEAD enableLogging(true); initCache(); enableLogging(false); feature/login initNetwork(); }我来逐行解释这些标记的含义 HEAD冲突区域的起点后面跟的是当前分支的引用名。HEAD表示当前检出的分支也就是“我这边”的版本分隔线上面是当前分支的内容下面是合并进来的分支的内容 feature/login冲突区域的终点feature/login是正在被合并进来的分支名。上面这个例子里当前分支在init()里调用了enableLogging(true)和initCache()而feature/login分支只调用了enableLogging(false)且没有初始化缓存。看到这里正确的处理方式不是删掉任意一边而是想清楚这两段代码表达的是什么逻辑是否应该同时保留比如最终结果可能是function init() { enableLogging(false); initCache(); initNetwork(); }也就是说日志开关采用对方分支的配置但缓存初始化必不可少。这时候冲突就不是“二选一”而是“取两边的合理部分”。注意冲突标记里的HEAD不一定就是当前分支的名字它只是 Git 用来指代当前检出的分支。如果你在执行git rebase那HEAD指向的是你正在变基的分支而合并进来的内容来自被变基的目标分支语义会反过来。这个细节很多人搞混后面我会专门讲。2.3 常见误区预警避免“无脑保留”“无脑删除”我在实际项目里见过不少人解决冲突的思路就是“哪个看着顺眼留哪个”。这确实快但它有几个隐患第一你丢掉了对方分支里精心设计的功能。开发者的代码风格千差万别你的分支上可能没有对方的抽象层级直接删掉对方的实现等于把对方的工作全部作废。后续对方发现功能失踪排查一圈最终定位到你的那次合并提交同事关系就直接“拉满”。第二你在毫无记录的情况下把两段逻辑揉在了一起。有时候冲突区域里的两段代码并不是完全互斥的它们可能各自调用了一个关键函数正确做法是同时调用。如果你偷懒只保留一个程序可能在特定场景下崩溃而因为你已经解决了冲突Git 层面不会再有任何提示这个 bug 潜伏期可能长达数周。第三也是最重要的一点你失去了了解他人代码逻辑的机会。每解决一次冲突你都能借机比对双方的代码设计和意图。这种收获在实际工作中远比“合并成功”这个结果更有价值。所以遇到冲突时不要急着删。先把冲突区域的上下文整体读一遍搞清楚双方各自在做什么再动手。如果你发现自己对某段代码完全陌生先git log看看对方分支的提交历史或者直接去问写这段代码的同事都比盲改来得靠谱。3. 正确处理冲突的标准流程从拉分支到合并的完整步骤3.1 动手之前先看清当前状态无论你是执行了git merge、git pull还是git rebase遇到冲突第一件事永远是看一眼仓库状态弄清楚冲突范围、涉及文件和当前所处的操作阶段。这是整个流程里最不能省略的一步。git status输出结果会明确告诉你当前是否处于merge状态、有哪些文件是both modified双方修改、哪些是deleted by us/theirs等。我把常见状态整理一下方便你对照理解状态含义both modified双方都修改了这个文件且改动区域重叠产生了冲突deleted by us当前分支删除了这个文件但合并进来的分支修改了它deleted by them合并进来的分支删除了这个文件但当前分支修改了它added by us当前分支新增了文件但合并进来的分支从未包含它通常不会冲突added by them合并进来的分支新增了文件当前分支从未包含它通常不会冲突unmerged泛指所有尚未完成合并处理的路径接着用这条命令查看每个文件的具体冲突块数量和位置git diff --name-only --diff-filterU或者更直观一点直接看冲突内容git diff此时git diff显示的是工作区与暂存区之间的差异而由于合并冲突状态下文件处于特殊状态你会看到冲突标记。配合 VSCode、IDEA 或者 Sublime Merge 这类图形化工具查看会舒服得多。图形化工具不是必须的但确实能提高效率。以 VSCode 为例它会把冲突区域高亮显示并提供“Accept Current Change”“Accept Incoming Change”“Accept Both Changes”三个快捷键按钮。我个人的经验是快捷键很快但不要盲点还是要先看清两个版本各自的内容再选。3.2 核心步骤手动编辑冲突文件并完成暂存假设你的冲突文件是src/utils/format.js里面有两个冲突块。首先用编辑器打开它逐个处理冲突区域。处理原则我已经强调过先理解再动手。具体操作步骤如下定位到第一个冲突区域搜索即可阅读当前分支版本HEAD标记到之间的内容阅读合并分支版本到之间的内容判断两个版本的意图是二选一还是复合两者编辑该区域删除所有冲突标记保留最终需要的代码重复上述步骤直到文件中不存在任何标记保存文件执行git add将该文件标记为“已解决”。执行git add这一步非常关键。Git 只认暂存区你手动改了文件但没git add对 Git 来说这个文件仍然是“未合并”的状态。只有当你git add之后Git 才会认为这个冲突文件被解决。全部文件都处理完成后再次运行git status确认没有遗留的both modified或unmerged文件。然后执行git commit这条命令会生成一个合并提交。Git 默认会给你准备好提交信息内容类似Merge branch feature/login into master你可以保持默认也可以补充一些说明比如解决冲突时做了哪些取舍。合理的合并提交信息对后续查阅历史非常有帮助。3.3 解决冲突的三种可视化工具选哪个看习惯如果你觉得手动查找标记效率太低可以考虑这几类工具第一种是编辑器内置的冲突提示。VSCode 和 JetBrains 全家桶IDEA、PyCharm、GoLand 等都内置了冲突解决界面。它们会把两边的版本并排显示你能直观看到差异然后一键接受或合并。这是我日常用到最多的方式也是我推荐新手从最先尝试的方式因为学习成本几乎为零。第二种是专门的可视化合并工具比如kdiff3、Meld、Beyond Compare。它们可以展示三栏对比base、ours、theirs并且能手动从每一栏选取片段到结果栏。这种方式特别适合复杂的、冲突块较多的文件因为你能看到共同祖先版本理解双方的改动逻辑会容易很多。配置方式如下git config --global merge.tool kdiff3 git config --global mergetool.kdiff3.path /usr/bin/kdiff3然后在冲突状态下执行git mergetoolGit 会逐个打开有冲突的文件等你操作完再关掉编辑器然后问你“Was the merge successful?”你确认后它会自动执行git add。第三种是命令行工具比如直接看git diff输出。这种方式对终端党友好但个人觉得只适合解决简单冲突复杂冲突不如上面的可视化工具清晰。我之前碰到一次特别复杂的冲突一个文件出现十几个冲突区域两边还都做了不少重构。用纯手改的方式不到 10 分钟就眼花缭乱了后来切成kdiff3按base版本逐行梳理半小时才完全搞清楚。别看前期配置麻烦了点遇到真实场景省下的时间可太多了。3.4 选好“拉取”时机可以在源头减少冲突很多人习惯每天上班第一件事就是git pull origin master这个动作本身没问题但如果你在自己的功能分支上工作我的建议是每天至少把主干的最新代码合进自己的分支一次。具体做法是git checkout feature/my-work git fetch origin git merge origin/master这不会直接影响主干分支冲突只发生在你的本地工作分支上。你有充足的时间在提交给同行评审之前把冲突处理干净。相反如果你一直埋头苦干等一个星期的功能开发完直接发merge request合并到master那这个冲突规模往往会让你怀疑人生。还有一个团队习惯值得推荐对短期存活的分支尽量用 rebase 而不是 merge。短期分支比如两三天内合回主干的修复分支用 rebase 可以让提交历史变成一条直线后续合并到主干时不会产生多余的合并提交。但注意rebase 会改写提交历史如果有其他人基于你的分支干活就别这么做。这里顺带提一个非常核心的实操建议git fetch之后用git merge origin/master而不是直接git pull origin master这两者其实不相等git pull等于 fetch merge但有时候你只想先看看远端变更再决定怎么合并fetch更安全可控。4. 特殊场景专项解析revert之后的分支合并、amend、丢弃式合并4.1master分支revert之后其他分支合并回master的冲突怎么处理这是网上搜索热度极高、实际工作中特别容易踩坑的一个场景。我详细说一下。假设你的master分支有一个提交 A做了一些重构后来发现这个重构有问题于是你在master上执行了git revert Agit revert不是删掉提交 A 的记录而是产生一个新的提交 BB 的内容是“抵消 A 的改动”。此时master的代码历史里同时存在 A 和 B但代码内容回到了 A 之前的状态。关键在于你的特性分支feature是基于包含 A 的master拉出来的而且你没有把 A 的缺点同步到该分支上。这时候你把feature合并回masterGit 会怎么处理它会发现base是master和feature分叉之前的状态不含 Amaster当前状态有了 A 和 B相当于对base的净改动为零feature当前状态含 A 的改动再加上新开发的内容。于是 Git 认为master没有净变化但feature有变化合并时应该直接应用feature的改动。然而问题来了feature上的 A 改动在master历史里已经不存在了因为被 B 抵消了可 Git 的合并逻辑是基于提交差异的它只知道feature在base基础上改了很多其中就包括 A 所触及的那些行。当这些行在master上已经不存在、被B恢复到了旧状态时合并会产生大量冲突。这个场景的典型冲突表现是你需要合并的代码行在master上已经“被改回去了”而feature还是“改过之后”的状态。此时如果你无脑选择ours即保留master状态那feature上 A 的改动就会彻底消失如果无脑选择theirs即保留feature状态你之前在master上 revert 的问题会“复活”。正确做法是什么分成两种情况情况一master上 revert 的提交 A在feature上其实已经被你手工修复好了。这时候你在冲突区域应该保留feature的版本因为它包含修复后的正确逻辑。但要特别注意feature必须确实包含对应的修复提交否则不要这样做。情况二master上 revert 的提交 A 确实有问题feature还没有修复这个 bug。这时候正确做法是合并时先保留master状态即ours然后回到feature上围绕冲突区域做新的修复代码而不是把旧代码原封不动带回来。我在实际操作中更推荐一个稳妥方案在把feature合并回master之前先把master包含 revert 提交合并进feature在feature上解决这些冲突验证通过后再往master合并。这样至少冲突解决发生在你自己的分支上有充分的时间测试不会破坏主干。4.2 想要重新定义合并方向git merge -X theirs与-X ours的代价有些人为了图快会直接这样处理冲突git merge -X theirs feature/login这个命令的意思是合并时遇到冲突直接采用theirs的版本完全不弹冲突提示。同理-X ours就是遇到冲突时采用当前分支版本。这两个选项确实快但使用场景非常有限。我的建议是只在明确知道冲突方向的情况下用于临时快速合并。比如你只是想临时看一下分支合并后的构建结果不关心细节可以这么干。但最终往主干合并时还是老老实实逐个解决冲突。因为-X theirs会在冲突块级别自动选择而不是在语义层面做正确合并。它不会告诉你“这个冲突块的两边代码其实应该同时保留”它只是粗暴地帮你做了个二选一。尤其当合并分支里有人重构了接口、函数签名而你保留的是旧调用方式时代码可能连编译都过不了。还有更极端的.gitattributes里的mergetheirs策略我基本不推荐在生产项目里用除非你有十足的把握。4.3 关于git commit --amend和它引发的合并冲突另一个热搜词是git commit --amend。虽然它本身不直接引入合并冲突但它和冲突有一个间接关系如果你对一个已经推送到远端的分支使用amend就会改写提交历史导致该分支和远端不同步后续合并时产生莫名的冲突。git commit --amend的用途是修改最近一次提交的提交信息或者把暂存区里的小改动追加到最近一次提交里。比如git add src/format.js git commit --amend它会打开编辑器让你修改提交信息。如果你想保留原提交信息和作者不改变内容地补一句说明也可以git commit --amend --no-edit我的建议是amend只用于处理尚未推送到远端的本地提交。一旦某个提交已经被其他人拉取amend会重写历史两边分叉后后续合并该分支时Git 无法简单识别改动意图很容易产生大量冲突这是典型的“自己制造冲突”的操作。如果你已经误把amend后的分支推到了远端那么对这个分支的同步需要使用强推force push这种方式但这会覆盖远端历史对协作影响很大所以要格外慎重。这里多说一句如果必须强推记得提前通知团队成员否则他们在旧历史上的提交会“凭空失踪”。4.4 安装与环境配置不到位是很多人做不好 Git 的隐形原因这里要插播一个看似无关、但实际影响深远的点很多人对 Git 的操作不熟悉根源不是概念不懂而是环境配置不到位。热搜词里有大量“git 安装”“git 安装教程”“git 配置 gitee 密钥”这类搜索说明这一步就拦住了不少人。我建议大家在开始折腾复杂合并之前先把基础环境收拾利索安装完 Git 后第一时间配置用户名和邮箱否则无法提交git config --global user.name Your Name git config --global user.email youexample.com配置 SSH 密钥以便远程仓库免密操作。以 Gitee 为例生成密钥后把~/.ssh/id_rsa.pub的内容添加到 Gitee 账号的 SSH 公钥列表里即可。不能正常clone/push的分支操作什么合并都无从谈起。设置一个合适的默认编辑器。如果每次提交信息弹出的都是 Vim而你又完全不适应 Vim 的操作会非常难受。改成 VS Code 或者 nanogit config --global core.editor code --wait这些基础配置看似琐碎但真的能救你于水火。我见过有人因为不会退出 Vim把提交信息乱改一通最后把半成品代码提交上去还引发了一系列连锁冲突问题。5. 实操演练解一个真实的多文件、多冲突合并纸上谈兵没什么意思我拿一个真实项目里的合并场景来走一遍完整流程你们可以照着模拟。假设当前在master分支需要把feature/login合进来。这个分支主要做了三件事重命名了LoginService类新增了一个TokenValidator工具类同时修改了登录接口的返回结构。而master在分叉期间也改了登录接口的返回结构并且对LoginService做了一些性能优化。执行合并git checkout master git merge feature/login终端输出大概是这样Auto-merging src/auth/LoginService.java CONFLICT (content): Merge conflict in src/auth/LoginService.java Auto-merging src/controller/AuthController.java CONFLICT (content): Merge conflict in src/controller/AuthController.java Automatic merge failed; fix conflicts and then commit the result.注意到TokenValidator.java没有出现在冲突列表里因为它是新增文件两边没有重叠Git 能自动处理。真正冲突的是两个被两端同时修改的文件。第一步查看状态git status输出会是You have unmerged paths. (fix conflicts and run git commit) (use git merge --abort to abort the merge) Unmerged paths: both modified: src/auth/LoginService.java both modified: src/controller/AuthController.java这里我要先提一句如果你在解决冲突的过程中发现越搞越乱想退回合并之前的状态不要慌执行git merge --abort这个命令会把工作区恢复到git merge之前的状态。它非常安全是我最常挂在嘴边的止损指令。不过它只适用于还没手动git add大量文件的情况如果你已经git add了一部分abort 也能撤销但如果你已经手动改了很多文件并且没提交还是先想一想。稳妥起见暂停合并前最好备份一下自己的工作成果。第二步用 VSCode 打开src/auth/LoginService.java。冲突块大概长这样 HEAD public LoginResult login(String username, String password) { LoginValidator validator new LoginValidator(); validator.validate(username, password); return legacyLoginService.login(username, password); } public LoginResult login(String username, String password) { TokenValidator tokenValidator new TokenValidator(); tokenValidator.validate(username, password); return loginService.login(username, password); } feature/login仔细读一下两边的逻辑master这边引入了LoginValidator做校验feature/login那边用了新建的TokenValidator而且把legacyLoginService换成了loginService。这两边并不是完全对立的。LoginValidator和TokenValidator的名字不同、作用可能也不同正确做法可能是两个校验都做或者根据业务语义确定保留哪个。loginService的替换是feature/login重构的产物应该保留。最终合并如下public LoginResult login(String username, String password) { LoginValidator loginValidator new LoginValidator(); loginValidator.validate(username, password); TokenValidator tokenValidator new TokenValidator(); tokenValidator.validate(username, password); return loginService.login(username, password); }我特意在这里强调你必须在理解业务的前提下才能做出这种决策。如果只是粗暴选择任意一边这个类的功能大概率是不完整的。第三步处理AuthController.java。这个文件里的冲突更简单可能是返回结构改动的命名差异 HEAD return ResponseEntity.status(200).body(buildResultMap(loginResult)); return ResponseEntity.ok(buildLoginResponse(loginResult)); feature/login这里两边都在调整统一的响应结构。看到这种情况就要去查一下master上是否有一个buildResultMap的工具方法feature/login上是否新增了buildLoginResponse方法。最终决定用哪边的封装后把另一个工具方法移除避免重复代码。处理完这两个文件后保存检查冲突标记是否都消失了grep -r src/确认没有输出后逐个添加文件git add src/auth/LoginService.java src/controller/AuthController.java然后执行提交git commit -m Merge branch feature/login into master 保留 TokenValidator 校验及 loginService 重构 同时引入 LoginValidator 统一登录校验逻辑这样一个真实的合并冲突就处理完了。关键在于所有选择都是基于代码语义做的决策而不是随意删改。6. 常见问题与排查技巧实录操作层面讲完了我再把这些年积累的一些排坑经验整理一下这些都是文档里不会写、但实际中几乎一定碰得到的东西。6.1 大型重构类冲突怎么避免“这边改了那边死”有一类冲突最让人头疼一边是全局重命名比如方法名首字母改小写、类名统一前缀另一边是新功能开发加入的大段代码。两边几乎每个文件都有差异打开每个文件都一片红。面对这种情况不要一个个文件硬啃。我推荐的策略是先用git log --oneline --graph --all查看两个分支的分叉点确认对方上一次从master合并代码是什么时候优先选择先把master合并进feature在feature上统一解决冲突如果涉及重命名先看feature分支上是如何做方法调用的把它跟master上的调用方式对齐解决完一个文件后用mvn compile、npm run build或者你的项目构建命令立即验证而不是全部改完再构建单次合并不要积压大量功能分支尽量小步快跑代码频繁合并冲突规模会小很多。之前同事遇到过一次特别极端的情况一个前端项目重构了所有 CSS 类名另一个分支新增了几十个页面组件。两边都是大动静光解决 CSS 和 JSX 的冲突就花了两天。后来我们总结解决方案就是让重命名类的重构尽量用自动化脚本一次性完成并且别和功能分支并行太久。如果必须并行至少每两三天同步一次。6.2 冲突时不小心退出编辑器或误用了错误的解决方案有次我远程协助一位同事他在解决冲突时误按了git checkout --ours导致整个文件被覆盖成了master版本对方的改动全没了。关键是他还git add并提交了合并。这种情况不用绝望。Git 的记录在提交前都能找回来。你可以用git reflog查看所有本地分支的操作历史找到合并冲突发生之前的那个提交哈希然后用git checkout commit-hash -- file把那个时刻的冲突文件恢复出来重新处理。git reflog # 找到类似 commit 914f3a2 的记录 # 恢复某个文件 git checkout 914f3a2 -- src/controller/AuthController.java git add src/controller/AuthController.java git commitgit reflog是每一个 Git 使用者都应该了解的命令。它记录了本地仓库所有引用的变动历史相当于给你吃了颗后悔药。只要你不是真的把.git目录删了这个命令基本都能帮你找回状态。还有一种情况是你解决了冲突提交了合并但后来发现合并结果有 bug你想重来。这时候我不会建议你直接git revert而是会在修复 bug 的基础上新增一个提交因为合并提交一旦推送到公共仓库改写它的历史会让所有人都很难受。6.3 大文件合并冲突谁动谁死怎么拆解有些项目里会存在编译产物、锁文件package-lock.json、yarn.lock、或者自动生成的 POJO / ORM 实体类。这些文件一旦冲突手动改几乎等于灾难比如前端锁文件里每个依赖的解析地址都写在同一行里两边的 lock 版本差异可能引发大量冲突。我的处理思路是编译产物类文件不要解决冲突直接用生成工具重新生成。比如 Java 的target目录可以直接不提交到版本库如果你的仓库不小心提交了就删掉并加入.gitignore锁文件类保留master上的版本然后重新执行依赖安装命令让工具自动合并依赖。比如前端项目在冲突时可以先git checkout --theirs package-lock.json再执行npm install或yarn install来刷新锁文件自动生成的实体类/DTO同理通过重新生成机制刷新而不是手动硬改。用git checkout --theirs或--ours单独恢复这类文件是明确合理的取舍因为它们的正确状态由生成器决定不由冲突解决时的主观判断决定。6.4 快速速查表冲突执行动作与人对应关系当前状态你应该做什么典型命令合并中途想放弃安全退回合并前状态git merge --abort变基中途想放弃退回变基前状态git rebase --abort想知道哪些文件冲突显示未合并文件列表git diff --name-only --diff-filterU只想看冲突内容输出冲突区域上下文git diff单个文件解决冲突完成标记为已解决准备提交git add file误用--ours覆盖了文件从某个提交恢复文件git checkout commit -- filerevert后分支合并冲突先合master进feature逐块判断git merge origin/master这份表是我日常协作中最常用的指令集合建议先收藏或者打印出来放在手边。等你完全熟悉了这些指令基本能形成肌肉记忆就不再需要查表了。6.5 我从大量冲突里总结的三个习惯最后分享一下我自己从处理无数冲突里沉淀下来的三个习惯。第一每次主干有更新第一时间同步到自己的分支且越快越好。拖延只会让冲突成倍增加代码的“新鲜度”直接影响合并成本。这跟家里垃圾要及时倒是一个道理攒一个星期再清理体验完全不同。第二在合并前先阅读对方的提交信息大致了解分支意图。尤其当对方是一个跨多人协作的大分支时光看代码很难判断哪些是核心改动、哪些是实验性的。有了上下文解决冲突时你的每一个决策都会更有依据。第三别硬扛着解决所有冲突该问就问。如果你的分支里有一段代码逻辑完全看不懂随手艾特一下写这段代码的同事直接问一句“这段逻辑在最新 master 上已经改掉了你们分支的用法还要保留吗”换来的是对方立刻的解答远比你在代码里猜来猜去要高效得多。协作开发的本质就是沟通Git 冲突只是把沟通的需求显式化而已。回到最初的那个观点冲突不是 Git 在给你找麻烦而是它在帮你把关。每次合并不顺利多问自己一句“两边的改动为什么必须放在同一个地方”你的代码能力和协作能力大概率就都往上涨了一点。遇到冲突深呼吸打开文件一行一行读一段一段想然后干干净净地解决它。这才是正确的处理流程。
返回列表