Git Conflict Resolution

1. 这篇文章解决什么问题?

Git 冲突不是异常情况,而是多人协作和分支开发里的正常现象。

常见问题包括:

text 复制代码
1. 为什么会产生冲突?
2. 冲突文件里的 <<<<<<<、=======、>>>>>>> 是什么?
3. merge 冲突怎么解决?
4. rebase 冲突怎么解决?
5. 解决冲突后为什么还要 git add?
6. 如果不想继续 merge 或 rebase,怎么取消?

这篇只讲冲突处理流程。

mergerebasepull 的概念放在 05-merge-rebase-pull.md 里理解。


2. 冲突为什么会发生?

Git 自动合并的前提是:

text 复制代码
两边修改的位置不冲突
或者 Git 能判断应该保留哪些内容

如果两个分支都修改了同一个文件的同一块内容,Git 就不敢替你决定。

例如 main 上把一行改成:

cpp 复制代码
int timeout = 30;

功能分支上把同一行改成:

cpp 复制代码
int timeout = 60;

这时 Git 不知道最终应该是 30 还是 60,就会产生冲突。


3. 冲突发生时 Git 在做什么?

假设当前历史是:

text 复制代码
A -- B -- C
          |
        main
          \
           D -- E
                |
          feature/cache

main 上执行:

bash 复制代码
git merge feature/cache

如果两边改了同一块内容,Git 会停下来:

text 复制代码
Auto-merging config.cpp
CONFLICT (content): Merge conflict in config.cpp
Automatic merge failed; fix conflicts and then commit the result.

这时仓库处于一个"合并进行中"的状态。

需要做三件事:

text 复制代码
1. 打开冲突文件
2. 手动决定最终内容
3. git add 标记冲突已解决

4. 冲突标记怎么看?

冲突文件里通常会出现:

text 复制代码
marker: <<<<<<< HEAD
int timeout = 30;
marker: =======
int timeout = 60;
marker: >>>>>>> feature/cache

真实文件里不会有 marker: 前缀,这里只是为了避免 Git 把笔记示例误判成未解决冲突。

含义是:

text 复制代码
上半段:<<<<<<< HEAD 到 ======= 之间,是当前分支的内容
下半段:======= 到 >>>>>>> feature/cache 之间,是被合并进来的分支内容

如果你当前在 main 上执行:

bash 复制代码
git merge feature/cache

那么:

text 复制代码
HEAD 通常表示 main 当前内容
feature/cache 表示正在合并进来的内容

5. 冲突文件应该怎么改?

解决冲突不是删除某一边那么简单,而是决定最终代码应该长什么样。

例如上面的冲突,可以改成:

cpp 复制代码
int timeout = 60;

也可以改成:

cpp 复制代码
int timeout = loadTimeoutFromConfig();

关键是:

text 复制代码
最终文件里不能留下 <<<<<<<、=======、>>>>>>>
最终代码必须能表达正确业务逻辑

解决后执行:

bash 复制代码
git status
git add config.cpp

git add 在这里的含义不是"准备提交新功能",而是:

text 复制代码
告诉 Git:这个文件的冲突我已经处理完了

6. merge 冲突的完整流程

典型流程:

bash 复制代码
git checkout main
git merge feature/cache

如果出现冲突:

bash 复制代码
git status

打开冲突文件,修改成最终内容。

然后:

bash 复制代码
git add config.cpp
git status
git commit

如果这是普通 merge,最后的 git commit 会生成一个 merge commit。

有时 Git 会自动准备好 commit message,直接保存即可。


7. rebase 冲突的完整流程

典型流程:

bash 复制代码
git checkout feature/cache
git rebase main

如果某个提交应用时发生冲突,Git 会停下来。

解决文件后执行:

bash 复制代码
git add config.cpp
git rebase --continue

如果后面的提交继续冲突,就继续重复:

text 复制代码
改冲突文件
git add
git rebase --continue

这就是 05 里说的:

text 复制代码
merge 冲突像一次性结算
rebase 冲突像按提交分期结算

8. ours 和 theirs 怎么理解?

冲突时经常会看到:

bash 复制代码
git checkout --ours config.cpp
git checkout --theirs config.cpp

merge 中通常可以这样理解:

text 复制代码
ours   当前分支的版本
theirs 被合并进来的分支版本

例如当前在 main 上:

bash 复制代码
git merge feature/cache

那么:

text 复制代码
ours   main
theirs feature/cache

如果你确定保留当前分支版本:

bash 复制代码
git checkout --ours config.cpp
git add config.cpp

如果你确定保留对方分支版本:

bash 复制代码
git checkout --theirs config.cpp
git add config.cpp

注意:

text 复制代码
不要只因为命令方便就整文件保留一边
很多冲突需要把两边逻辑合在一起

9. rebase 里的 ours 和 theirs 容易反直觉

rebase 过程中,ourstheirs 容易让人迷糊。

因为 rebase 的过程是:

text 复制代码
先切到目标基底
再把你的提交一个一个应用上去

所以在 rebase 冲突里,ours 往往表示当前基底那边,theirs 往往表示正在被重新应用的那个提交。

新手不建议在 rebase 冲突里盲目使用:

bash 复制代码
git checkout --ours
git checkout --theirs

更稳的方式是:

text 复制代码
打开文件
读冲突上下文
手动改成最终想要的代码

10. 不想继续 merge 怎么办?

如果 merge 到一半发现不想继续了:

bash 复制代码
git merge --abort

它会尝试回到 merge 之前的状态。

常见场景:

text 复制代码
冲突太多,想换一种合并方式
发现合错分支了
还没准备好处理这些冲突

执行后再看:

bash 复制代码
git status

确认仓库已经回到正常状态。


11. 不想继续 rebase 怎么办?

如果 rebase 到一半不想继续:

bash 复制代码
git rebase --abort

它会回到 rebase 开始之前。

如果已经解决了当前冲突,想继续:

bash 复制代码
git rebase --continue

如果想跳过当前这个提交:

bash 复制代码
git rebase --skip

--skip 要谨慎,因为它表示:

text 复制代码
当前这个提交不要了

12. 冲突解决后要做什么检查?

解决冲突后,不要只看 Git 是否通过,还要确认代码真的正确。

推荐顺序:

bash 复制代码
git status
git diff --cached

然后根据项目情况运行:

bash 复制代码
cmake --build build
ctest --test-dir build

或者至少运行当前模块的编译和测试。

冲突解决最容易出问题的地方不是语法,而是:

text 复制代码
两边逻辑都保留了,但组合后语义不对
删掉冲突标记了,但误删了一段必要逻辑
代码能编译,但配置、路径、条件判断错了

13. 减少冲突的习惯

冲突不能完全避免,但可以减少。

比较实用的习惯:

text 复制代码
1. 功能分支不要拖太久
2. 经常 fetch,然后让自己的分支跟上 main
3. 不要在一个提交里混合太多无关修改
4. 格式化代码和业务修改尽量分开提交
5. 修改公共头文件、配置文件时更谨慎
6. 合并前先看 git status,保证工作区干净

尤其是 C++ 项目里,下面这些文件容易引发多人冲突:

text 复制代码
CMakeLists.txt
公共头文件
配置文件
README.md
接口定义文件

这些文件改动前最好先同步最新主分支。


14. 总结

冲突的本质是 Git 无法自动判断最终内容。

解决冲突时,先用 git status 找到冲突文件,再手动改成正确代码,最后用 git add 标记已解决;如果方向错了,用 merge --abortrebase --abort 退回去。

相关推荐
人工智能培训1 小时前
如何定义和测量“通用具身智能”
大数据·人工智能·机器学习·prompt·agent
高斯林.神犇1 小时前
Git全套流程
git
青槿吖1 小时前
第一篇:Elasticsearch 入门踩坑记:从 “URL 拼写错误” 到跑通第一个搜索服务
大数据·elasticsearch·搜索引擎·spring cloud·微服务·架构·全文检索
互联科技报1 小时前
商城小程序选择哪家平台比较好?预算有限也能选对!
大数据·小程序
Elasticsearch2 小时前
Elasticsearch ES|QL “读取时模式”:你的未映射字段一直都在那里
elasticsearch
Haibakeji2 小时前
长沙定制开发本地生活APP打造城市便民消费场景
大数据·人工智能·生活
Elastic 中国社区官方博客2 小时前
一个索引,所有媒体:介绍 jina-embeddings-v5-omni
大数据·人工智能·elasticsearch·搜索引擎·ai·媒体·jina
名不经传的养虾人2 小时前
从0到1:企业级AI项目迭代日记 Vol.19|两个环节 vs 十几个环节:Hermes厉害在哪里?
大数据·人工智能·ai编程·企业ai·多agent协作
万邦科技-Alan2 小时前
API淘宝关键词搜索:运用场所、使用方式及获客逻辑
大数据·api·开发平台