Skip to content

解决冲突

概述

合并冲突是团队协作中常见的情况。当两个分支修改了同一文件的同一位置时,Git 无法自动合并,需要人工干预解决冲突。

冲突产生原因

内容冲突

两个分支修改了同一文件的同一行。

bash
# 分支 A 修改了 file.txt 的第 10 行
echo "Version A" >> file.txt

# 分支 B 也修改了 file.txt 的第 10 行
echo "Version B" >> file.txt

# 合并时产生冲突
git merge branch-b
# CONFLICT (content): Merge conflict in file.txt

文件名冲突

两个分支对同一文件进行了不同的重命名或删除操作。

bash
# 分支 A 重命名文件
git mv file.txt file-a.txt

# 分支 B 重命名文件
git mv file.txt file-b.txt

# 合并时产生冲突
git merge branch-b
# CONFLICT (rename/rename): file.txt renamed to file-a.txt in HEAD and to file-b.txt in branch-b

目录冲突

两个分支创建了同名但类型不同的条目。

bash
# 分支 A 创建文件
touch lib/module

# 分支 B 创建同名目录
mkdir -p lib/module

# 合并时产生冲突
git merge branch-b
# CONFLICT (file/directory): directory in the way of file

识别冲突

查看冲突状态

bash
# 合并后查看状态
git status
# 输出:
# Unmerged paths:
#   (use "git add <file>..." to mark resolution)
#   both modified:   src/main.js
#   deleted by them: src/old.js
#   added by them:   src/new.js

冲突状态说明

状态含义
both modified双方都修改了同一文件
deleted by them当前分支修改,对方分支删除
deleted by us当前分支删除,对方分支修改
added by them对方分支添加的文件与当前冲突
added by us当前分支添加的文件与对方冲突
both added双方都添加了同一文件
both deleted双方都删除了同一文件

查看冲突文件列表

bash
# 列出所有冲突文件
git diff --name-only --diff-filter=U

# 列出冲突文件及状态
git diff --name-status --diff-filter=U

手动解决冲突

理解冲突标记

# 冲突文件示例
function greet() {
<<<<<<< HEAD
    console.log("Hello from main");
=======
    console.log("Hello from feature");
>>>>>>> feature
}

标记说明:

  • <<<<<<< HEAD(7 个 <):当前分支(HEAD)的内容开始
  • =======(7 个 =):分隔线,分隔两个版本
  • >>>>>>> feature(7 个 >):被合并分支的内容结束

解决步骤

bash
# 1. 打开冲突文件
vim src/main.js

# 2. 找到冲突标记

# 3. 编辑文件,解决冲突
# 选择保留的内容,删除冲突标记

# 4. 保存文件

# 5. 标记冲突已解决
git add src/main.js

# 6. 继续处理其他冲突文件

# 7. 完成合并
git commit

解决示例

原始冲突:

javascript
function greet() {
<<<<<<< HEAD
    console.log("Hello from main");
=======
    console.log("Hello from feature");
>>>>>>> feature
}

解决方案 1:保留当前分支

javascript
function greet() {
    console.log("Hello from main");
}

解决方案 2:保留被合并分支

javascript
function greet() {
    console.log("Hello from feature");
}

解决方案 3:合并两者

javascript
function greet() {
    console.log("Hello from main");
    console.log("Hello from feature");
}

解决方案 4:全新内容

javascript
function greet() {
    console.log("Hello from both branches");
}

复杂冲突处理

bash
# 多处冲突
# 文件中可能有多个冲突块

<<<<<<< HEAD
console.log("First conflict - main");
=======
console.log("First conflict - feature");
>>>>>>> feature

// 其他代码...

<<<<<<< HEAD
console.log("Second conflict - main");
=======
console.log("Second conflict - feature");
>>>>>>> feature

使用合并工具

内置合并工具

bash
# 启动合并工具
git mergetool

# Git 会依次打开每个冲突文件

配置合并工具

bash
# 查看可用的合并工具
git mergetool --tool-help

# 配置使用 vimdiff
git config --global merge.tool vimdiff

# 配置使用 VS Code
git config --global merge.tool code
git config --global mergetool.code.cmd 'code --wait $MERGED'

# 配置使用其他工具
git config --global merge.tool meld      # Linux
git config --global merge.tool opendiff  # macOS
git config --global merge.tool kdiff3    # Cross-platform

使用 vimdiff

bash
# vimdiff 显示四个窗口
# +----------------+----------------+
# |    LOCAL       |    REMOTE      |
# | (当前分支版本) | (被合并分支版本)|
# +----------------+----------------+
# |       MERGED (底部,编辑窗口)    |
# +---------------------------------+
# |       BASE (共同祖先)            |
# +---------------------------------+

# 常用命令
# :diffg LO  - 使用 LOCAL 版本
# :diffg RE  - 使用 REMOTE 版本
# :diffg BA  - 使用 BASE 版本
# :wqa       - 保存并退出

使用 VS Code

bash
# VS Code 提供可视化冲突解决界面
# 点击 "Accept Current Change" 保留当前分支
# 点击 "Accept Incoming Change" 保留被合并分支
# 点击 "Accept Both Changes" 保留两者
# 点击 "Compare Changes" 查看差异

快速解决策略

选择特定版本

bash
# 使用当前分支版本
git checkout --ours file.txt

# 使用被合并分支版本
git checkout --theirs file.txt

# 使用 git restore(Git 2.23+)
git restore --ours file.txt
git restore --theirs file.txt

使用合并策略选项

bash
# 合并时优先使用当前分支
git merge -X ours feature

# 合并时优先使用被合并分支
git merge -X theirs feature

# 注意:这只会自动解决冲突,其他修改仍会合并

批量解决

bash
# 批量使用当前分支版本
git checkout --ours .
git add .

# 批量使用被合并分支版本
git checkout --theirs .
git add .

中止合并

取消合并

bash
# 中止合并,恢复到合并前状态
git merge --abort

# 如果已经解决部分冲突,--abort 会撤销所有更改

重置到合并前

bash
# 使用 reset 回退
git reset --hard HEAD

# 或使用 ORIG_HEAD
git reset --hard ORIG_HEAD

冲突预防

定期同步

bash
# 在功能分支上定期合并主分支
git checkout feature
git fetch origin
git merge origin/main

# 或使用变基
git rebase origin/main

小步提交

bash
# 频繁提交小的、原子性的更改
# 减少单次提交的修改范围
# 降低冲突概率

团队协调

bash
# 避免多人同时修改同一文件
# 使用代码所有权规则
# 进行充分的代码审查

使用 .gitattributes

bash
# .gitattributes 文件
# 定义合并策略

# 总是使用当前分支版本
config.lock merge=ours

# 定义自定义合并驱动
*.js merge=js-merge

总结

  • 冲突是团队协作的正常现象
  • 理解冲突标记是解决冲突的基础
  • 使用合并工具提高效率
  • 定期同步可以减少冲突
  • 小步提交降低冲突影响

下一步

学习完解决冲突后,建议继续学习: