Appearance
冲突处理
当 Git 无法自动合并两个分支的改动时,就会产生冲突(Conflict)。冲突需要手动解决,是协作开发中必须掌握的技能。
冲突产生的原因
冲突发生在以下情况:
- 两个分支修改了同一文件的同一区域
- 一个分支删除了某文件,另一个分支修改了该文件
- 两个分支都修改了同一个二进制文件
不会产生冲突的情况:
- 修改了不同文件
- 修改了同一文件的不同区域(Git 能自动合并)
冲突标记的含义
发生冲突时,Git 在文件中插入冲突标记:
javascript
<<<<<<< HEAD
// 当前分支(HEAD)的代码
function login(username, password) {
return authenticate(username, password);
}
=======
// 被合并分支的代码
function login(email, password) {
const user = findUser(email);
return authenticate(user, password);
}
>>>>>>> feature/email-login| 标记 | 含义 |
|---|---|
<<<<<<< HEAD | 当前分支(你所在分支)的内容开始 |
======= | 两个版本的分隔线 |
>>>>>>> feature/email-login | 被合并分支的内容结束 |
手动解决冲突
步骤 1:查看冲突文件
bash
git status
# Both modified: src/auth.js
# Both modified: config/settings.json步骤 2:编辑冲突文件
打开文件,根据业务逻辑决定保留哪个版本(或合并两者):
javascript
// 解决方案1:保留当前分支版本
function login(username, password) {
return authenticate(username, password);
}
// 解决方案2:保留被合并分支版本
function login(email, password) {
const user = findUser(email);
return authenticate(user, password);
}
// 解决方案3:融合两者
function login(emailOrUsername, password) {
const user = findUser(emailOrUsername);
return authenticate(user, password);
}必须删除所有的冲突标记(<<<<<<<、=======、>>>>>>>)。
步骤 3:标记冲突已解决
bash
git add src/auth.js
git add config/settings.json步骤 4:完成合并
bash
# 合并冲突
git commit
# (系统会生成合并提交信息,可以编辑)
# rebase 冲突
git rebase --continuegit mergetool 使用图形化工具
配置并使用图形化工具来解决冲突,比手动编辑更直观:
bash
# 配置 merge 工具
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# 其他工具配置
git config --global merge.tool vimdiff
git config --global merge.tool kdiff3
# 启动 merge 工具
git mergetool
# 为特定文件使用特定工具
git mergetool --tool=vscode src/auth.js常用图形化 merge 工具:
- VS Code:内置三栏视图,直观易用
- IntelliJ IDEA / WebStorm:功能强大的 merge 工具
- KDiff3:开源免费
- Meld:Linux 上常用
- Beyond Compare:付费,功能强大
查看冲突的三个版本(:1 :2 :3)
在暂存区中,冲突文件同时存在三个版本:
bash
# :1 - 共同祖先版本(base)
git show :1:src/auth.js
# :2 - 当前分支版本(ours)
git show :2:src/auth.js
# :3 - 被合并分支版本(theirs)
git show :3:src/auth.js
# 查看三个版本的差异
git diff :1:src/auth.js :2:src/auth.js # base vs ours
git diff :1:src/auth.js :3:src/auth.js # base vs theirsgit checkout --ours / --theirs
快速选择其中一方的版本,不用手动编辑:
bash
# 保留当前分支(HEAD)的版本,放弃对方的改动
git checkout --ours src/config.json
git restore --ours src/config.json # 新语法
# 保留被合并分支的版本,放弃当前分支的改动
git checkout --theirs src/config.json
git restore --theirs src/config.json # 新语法
# 批量操作:对某目录下所有冲突文件选择 ours 版本
git checkout --ours -- .
git add .注意:在 rebase 过程中,--ours 和 --theirs 的含义与 merge 时相反:
- merge 时:--ours = 当前分支,--theirs = 被合并分支
- rebase 时:--ours = 正在 rebase 到的目标分支,--theirs = 正在被 rebase 的提交
rerere(Reuse Recorded Resolution)
rerere("Reuse Recorded Resolution")是 Git 的一个功能,它会记录冲突的解决方案,下次遇到相同冲突时自动应用。
启用 rerere
bash
git config --global rerere.enabled true工作原理
- 发生冲突时,Git 记录冲突的"模式"(两侧的代码)
- 你手动解决冲突
- Git 记录你的解决方案
- 下次遇到相同模式的冲突,自动应用已记录的方案
bash
# 发生冲突后,git 提示:
# Recorded preimage for 'src/auth.js' ← rerere 记录了冲突
# 解决冲突后,执行 git add:
# Recorded resolution for 'src/auth.js' ← rerere 记录了解决方案
# 下次遇到相同冲突:
# Resolved 'src/auth.js' using previous resolution. ← 自动解决!rerere 常用命令
bash
# 查看 rerere 记录的解决方案
git rerere status
git rerere diff # 查看记录的差异
# 手动触发 rerere 重新应用已记录的解决方案
git rerere
# 忘记某个冲突的解决方案(如果解决方案有误)
git rerere forget src/auth.js
# 清理所有 rerere 记录
rm -rf .git/rr-cache/rerere 的强大场景:
- 长期功能分支反复与主线 rebase 时
- 多人同时修改同一区域,冲突反复出现时
复杂冲突处理技巧
查看是谁最后修改了冲突行
bash
git log -p -S "conflicting_line" -- src/auth.js
git blame src/auth.js查看分叉点(共同祖先)的文件状态
bash
# 找到共同祖先
git merge-base HEAD feature/login
# 查看共同祖先时的文件内容
git show $(git merge-base HEAD feature/login):src/auth.js中止并重新开始
bash
# 合并时遇到复杂冲突,想重新来过
git merge --abort
# rebase 时中止
git rebase --abort预防冲突的最佳实践
- 频繁同步主线:定期将主线合并到功能分支(或 rebase),减少分叉程度
bash
# 每天开始工作前同步主线
git switch feature
git fetch origin
git rebase origin/main小批量提交:避免长时间在功能分支上堆积大量改动
模块化代码:合理拆分模块,减少多人修改同一文件的机会
沟通协调:团队成员提前沟通,避免同时修改相同区域
使用 CODEOWNERS:通过代码所有权机制,让修改某模块前必须通知相关负责人
常见冲突场景示例
场景1:同行被不同方式修改
bash
# 解决:分析哪种修改是正确的,或综合两者
# 关键是理解业务意图,不要机械地选择一方场景2:一方删除了文件,另一方修改了文件
bash
git status
# deleted by us: src/old-module.js ← 我们删除了,对方修改了
# 选项1:确认删除,忽略对方的修改
git rm src/old-module.js
# 选项2:保留对方的修改
git add src/old-module.js场景3:二进制文件冲突
bash
# 二进制文件无法合并,只能选择其中一方
git checkout --ours -- assets/logo.png
git checkout --theirs -- assets/logo.png总结
| 操作 | 命令 |
|---|---|
| 查看冲突文件 | git status |
| 选择当前分支版本 | git checkout --ours <file> |
| 选择对方分支版本 | git checkout --theirs <file> |
| 使用图形化工具 | git mergetool |
| 标记冲突已解决 | git add <file> |
| 完成合并 | git commit |
| 继续 rebase | git rebase --continue |
| 放弃合并/rebase | git merge --abort / git rebase --abort |
| 启用 rerere | git config rerere.enabled true |