1. 这篇文章解决什么问题Git 冲突不是异常情况而是多人协作和分支开发里的正常现象。常见问题包括1. 为什么会产生冲突 2. 冲突文件里的 、、 是什么 3. merge 冲突怎么解决 4. rebase 冲突怎么解决 5. 解决冲突后为什么还要 git add 6. 如果不想继续 merge 或 rebase怎么取消这篇只讲冲突处理流程。merge、rebase、pull的概念放在05-merge-rebase-pull.md里理解。2. 冲突为什么会发生Git 自动合并的前提是两边修改的位置不冲突 或者 Git 能判断应该保留哪些内容如果两个分支都修改了同一个文件的同一块内容Git 就不敢替你决定。例如main上把一行改成inttimeout30;功能分支上把同一行改成inttimeout60;这时 Git 不知道最终应该是30还是60就会产生冲突。3. 冲突发生时 Git 在做什么假设当前历史是A -- B -- C | main \ D -- E | feature/cache在main上执行gitmerge feature/cache如果两边改了同一块内容Git 会停下来Auto-merging config.cpp CONFLICT (content): Merge conflict in config.cpp Automatic merge failed; fix conflicts and then commit the result.这时仓库处于一个“合并进行中”的状态。需要做三件事1. 打开冲突文件 2. 手动决定最终内容 3. git add 标记冲突已解决4. 冲突标记怎么看冲突文件里通常会出现marker: HEAD int timeout 30; marker: int timeout 60; marker: feature/cache真实文件里不会有marker:前缀这里只是为了避免 Git 把笔记示例误判成未解决冲突。含义是上半段 HEAD 到 之间是当前分支的内容 下半段 到 feature/cache 之间是被合并进来的分支内容如果你当前在main上执行gitmerge feature/cache那么HEAD 通常表示 main 当前内容 feature/cache 表示正在合并进来的内容5. 冲突文件应该怎么改解决冲突不是删除某一边那么简单而是决定最终代码应该长什么样。例如上面的冲突可以改成inttimeout60;也可以改成inttimeoutloadTimeoutFromConfig();关键是最终文件里不能留下 、、 最终代码必须能表达正确业务逻辑解决后执行gitstatusgitaddconfig.cppgit add在这里的含义不是“准备提交新功能”而是告诉 Git这个文件的冲突我已经处理完了6. merge 冲突的完整流程典型流程gitcheckout maingitmerge feature/cache如果出现冲突gitstatus打开冲突文件修改成最终内容。然后gitaddconfig.cppgitstatusgitcommit如果这是普通merge最后的git commit会生成一个 merge commit。有时 Git 会自动准备好 commit message直接保存即可。7. rebase 冲突的完整流程典型流程gitcheckout feature/cachegitrebase main如果某个提交应用时发生冲突Git 会停下来。解决文件后执行gitaddconfig.cppgitrebase--continue如果后面的提交继续冲突就继续重复改冲突文件 git add git rebase --continue这就是05里说的merge 冲突像一次性结算 rebase 冲突像按提交分期结算8. ours 和 theirs 怎么理解冲突时经常会看到gitcheckout--oursconfig.cppgitcheckout--theirsconfig.cpp在merge中通常可以这样理解ours 当前分支的版本 theirs 被合并进来的分支版本例如当前在main上gitmerge feature/cache那么ours main theirs feature/cache如果你确定保留当前分支版本gitcheckout--oursconfig.cppgitaddconfig.cpp如果你确定保留对方分支版本gitcheckout--theirsconfig.cppgitaddconfig.cpp注意不要只因为命令方便就整文件保留一边 很多冲突需要把两边逻辑合在一起9. rebase 里的 ours 和 theirs 容易反直觉在rebase过程中ours和theirs容易让人迷糊。因为 rebase 的过程是先切到目标基底 再把你的提交一个一个应用上去所以在 rebase 冲突里ours往往表示当前基底那边theirs往往表示正在被重新应用的那个提交。新手不建议在 rebase 冲突里盲目使用gitcheckout--oursgitcheckout--theirs更稳的方式是打开文件 读冲突上下文 手动改成最终想要的代码10. 不想继续 merge 怎么办如果 merge 到一半发现不想继续了gitmerge--abort它会尝试回到 merge 之前的状态。常见场景冲突太多想换一种合并方式 发现合错分支了 还没准备好处理这些冲突执行后再看gitstatus确认仓库已经回到正常状态。11. 不想继续 rebase 怎么办如果 rebase 到一半不想继续gitrebase--abort它会回到 rebase 开始之前。如果已经解决了当前冲突想继续gitrebase--continue如果想跳过当前这个提交gitrebase--skip--skip要谨慎因为它表示当前这个提交不要了12. 冲突解决后要做什么检查解决冲突后不要只看 Git 是否通过还要确认代码真的正确。推荐顺序gitstatusgitdiff--cached然后根据项目情况运行cmake--buildbuild ctest --test-dir build或者至少运行当前模块的编译和测试。冲突解决最容易出问题的地方不是语法而是两边逻辑都保留了但组合后语义不对 删掉冲突标记了但误删了一段必要逻辑 代码能编译但配置、路径、条件判断错了13. 减少冲突的习惯冲突不能完全避免但可以减少。比较实用的习惯1. 功能分支不要拖太久 2. 经常 fetch然后让自己的分支跟上 main 3. 不要在一个提交里混合太多无关修改 4. 格式化代码和业务修改尽量分开提交 5. 修改公共头文件、配置文件时更谨慎 6. 合并前先看 git status保证工作区干净尤其是 C 项目里下面这些文件容易引发多人冲突CMakeLists.txt 公共头文件 配置文件 README.md 接口定义文件这些文件改动前最好先同步最新主分支。14. 总结冲突的本质是 Git 无法自动判断最终内容。解决冲突时先用git status找到冲突文件再手动改成正确代码最后用git add标记已解决如果方向错了用merge --abort或rebase --abort退回去。