:解决合并冲突,处理“代码打架”)
1. 问题场景你已经学会了创建分支和合并分支。在上一篇文章里合并过程顺滑得像切黄油——Git 自动完成了所有工作。但真实世界里你和一个同事可能同时修改了同一个文件的同一处代码。当你试图把两个分支合并在一起时Git 会停下来告诉你“我搞不定这两边都改了同一个地方你来决定留哪个。”这种现象就叫合并冲突。它不是什么系统故障而是 Git 的一种保护机制防止你的代码被悄无声息地覆盖。理解冲突的产生原因并学会解决它是掌握 Git 分支协作的关键一步。2. 核心概念合并冲突发生在两个分支修改了同一个文件的同一区域时。这里的“同一区域”可以理解为相邻的几行代码。如果两个人分别修改了不同文件或者同一文件的不同位置Git 通常能自动合并不会引发冲突。但一旦双方的修改在空间上重叠Git 就无法自动判断谁对谁错只能把这个决策权交给你。冲突并不可怕也不意味着你做错了什么。它只是一个信号告诉你“这里有两个版本需要你人工确认最终版本。” 解决冲突的过程本质上是人工审查、决定取舍、然后告诉 Git“我已经搞定了”。3. 核心命令除了上一篇文章已经学过的git merge这里引入一个重要的参数gitmerge --no-ff-mmessagebranch_name--no-ff代表no fast forward即禁用快进模式。即使 Git 本可以快进合并也会强制生成一个新的合并提交。这样做的好处是在项目历史记录里能清晰地看到“这里发生过一次分支合并”保留了分支的存在痕迹有利于后续追溯。这在团队协作和 Git Flow 等模型中是非常常用的做法。4. 实战演示下面我们手动制造一个冲突然后一步步解决它。你可以在myproject仓库里操作或者新建一个干净仓库。4.1 准备战场假设当前在master分支ReadMe文件已经有一些内容。现在创建并切换到dev1分支$gitcheckout-bdev1 Switched to a new branchdev1在dev1分支上修改ReadMe中的一行将某处内容从aaa改为bbb。如果文件里没有aaa可以先添加一行含aaa的内容。为了演示清楚我们这样操作# 假设 ReadMe 目前内容为# hello git# hello git again# version: aaa$echoversion: aaaReadMe# 确保文件内容统一便于冲突$gitaddReadMe $gitcommit-mset version aaa on dev1然后切回master分支$gitcheckout master在master分支上对同一个文件的同一位置做不同修改。将aaa改为ccc$echoversion: cccReadMe# 这里直接覆写了同一行$gitaddReadMe $gitcommit-mset version ccc on master现在两个分支对同一文件的同一行有了不同的最终状态。4.2 触发冲突在master分支上尝试合并dev1$gitmerge dev1 Auto-merging ReadMe CONFLICT(content): Merge conflictinReadMe Automatic merge failed;fix conflicts andthencommit the result.Git 直接告诉你合并失败并指出冲突文件是ReadMe。用git status查看详情$gitstatus On branch master You have unmerged paths.(fix conflicts and rungit commit)(usegit merge --abortto abort the merge)Unmerged paths:(usegit add file...to mark resolution)both modified: ReadMe no changes added to commit(usegit addand/orgit commit -a)both modified表明ReadMe被两边都改了。4.3 解读冲突标记打开ReadMe文件你会看到类似这样的内容 HEAD version: ccc version: bbb dev1这就是 Git 的冲突标记 HEAD到之间的部分是当前分支这里是master的内容。到 dev1之间的部分是被合并分支这里是dev1的内容。你需要做的就是决定保留哪一版或者手动改写成一个新的、综合了两边需求的版本然后删除这些标记符号。4.4 解决冲突假设经过判断最终决定采用dev1的版本bbb。那么就把文件修改为version: bbb删除掉所有、、这些行只留下你要的内容。如果你想让最终版本是ccc就留version: ccc。如果想结合两者比如改成version: bbb and ccc也完全可以。重点是删除 Git 添加的三行标记。4.5 完成合并文件改好后保存退出。接下来把解决完冲突的文件标记为已解决并完成这次合并提交$gitaddReadMe $gitcommit-mmerge dev1: resolve conflict, keep bbb此时 Git 会生成一个合并提交两个分支的历史重新交汇。可以用git log --graph --oneline查看分支图谱确认合并成功。最后如果不再需要dev1分支可以删除$gitbranch-ddev14.6 使用 --no-ff 保留合并记录前面的操作并没有使用--no-ff默认可能会采用快进模式。为了在历史中保留明确的分支合并记录可以在合并时加上该参数$gitmerge --no-ff-mmerge dev1 with --no-ffdev1这样即使可以快进也会产生一个新的合并提交让你的提交历史图更清晰。5. 注意事项在开始合并前最好确保工作区是干净的git status无未提交改动避免冲突解决过程中掺杂其他问题。如果你暂时不想解决冲突或者觉得需要回退重新来可以执行git merge --abort它会取消本次合并回到合并前的状态所有冲突标记都会被清除。对于大型项目冲突可能涉及多个文件。git status会列出所有有冲突的文件你需要逐个打开、解决、标记。在多人协作中如果你频繁遇到冲突往往是沟通不足或者任务拆分粒度过粗的信号。频繁拉取最新代码并保持小步提交可以显著减少冲突的发生。6. 要点总结合并冲突产生于两个分支修改了同一文件的同一区域是 Git 的保护机制而非错误。冲突文件中Git 用、、标记出双方差异你需要手动决策最终内容并删除标记。解决冲突的流程编辑文件 → 删除标记 →git add→git commit。git merge --no-ff可以强制生成合并提交保留分支历史痕迹。不要害怕冲突掌握解决流程后它只是日常协作中的一个常规操作。7. 练习题创建一个新仓库或沿用myproject从master分支创建两个分支分别对同一个文件同一行做出不同修改然后模拟合并冲突。执行合并观察 Git 输出的冲突提示并用git status查看状态。打开冲突文件识别、、标记的区域手动编辑解决冲突保留你认为合理的版本。git add冲突文件然后完成合并提交。使用git log --graph --oneline查看分支合并历史。练习git merge --abort再次制造一个冲突但在解决之前执行git merge --abort确认工作区恢复到了合并前状态。思考题如果冲突文件不止一个你会如何高效地定位并解决尝试制造多文件冲突场景并练习。