Skip to content

Git 代码冲突处理完全指南 (Git Clash)

在团队协作开发中,Git 冲突(Conflict)是几乎无法避免的“家常便饭”。当两个开发者同时修改了同一个文件的同一行代码,或者一个删除了文件而另一个修改了它时,Git 无法自动决定使用哪个版本,这就产生了冲突 (Clash)

本文将深入浅出地介绍冲突的成因、识别方法、解决步骤以及如何利用 VS Code 等现代工具高效处理冲突。

1. 为什么会发生冲突?

Git 的自动合并策略(Auto-merge)非常强大,能处理大多数并行修改。但在以下场景中,Git 需要人工干预:

  • 同一行修改:分支 A 和分支 B 都修改了 index.html 的第 10 行。
  • 文件删除与修改:分支 A 删除了 config.js,而分支 B 修改了 config.js 的内容。
  • 变基 (Rebase):在使用 git rebase 整理提交历史时,旧的基底与新的更改发生重叠。

2. 识别冲突

当你执行 git mergegit pullgit rebase 时,如果发生冲突,终端会提示:

bash
Auto-merging src/App.js
CONFLICT (content): Merge conflict in src/App.js
Automatic merge failed; fix conflicts and then commit the result.

此时,使用 git status 可以查看具体哪些文件处于冲突状态:

bash
$ git status
On branch feature-login
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   src/App.js

3. 读懂冲突标记

打开冲突文件,你会看到 Git 插入的特殊标记,它们将冲突内容分成了三个部分:

javascript
<<<<<<< HEAD
const apiUrl = 'https://dev.api.com';  // 当前分支的修改 (Current Change)
=======
const apiUrl = 'https://prod.api.com'; // 传入分支的修改 (Incoming Change)
>>>>>>> feature/new-api
  • <<<<<<< HEAD:冲突开始,下方是当前所在分支(或 Rebase 时的目标基底)的内容。
  • =======:分隔符,上方是当前分支内容,下方是准备合并进来的分支内容。
  • >>>>>>> feature/new-api:冲突结束,标记了来源分支的名称或 Commit ID。

4. 实战:解决冲突的步骤

这是最基础的通用解决流程,适用于所有编辑器。

第一步:手动编辑

打开冲突文件,根据需求决定代码的去留。你有三种选择:

  1. 保留当前更改:删除 ======= 下方的内容。
  2. 保留传入更改:删除 ======= 上方的内容。
  3. 两者保留/混合:手动重写代码,将两者的逻辑结合。

处理后(确保存留的代码本身语法正确,并删除了 <<<, ===, >>> 标记):

javascript
// 假设我们需要根据环境动态判断
const apiUrl = process.env.NODE_ENV === 'production' 
  ? 'https://prod.api.com' 
  : 'https://dev.api.com';

第二步:标记解决

保存文件后,在终端运行:

bash
git add src/App.js

这告诉 Git:“我已经处理好这个文件的冲突了”。

第三步:完成合并

  • 如果是 Merge 造成的冲突:
    bash
    git commit -m "Fix merge conflict in App.js"
  • 如果是 Rebase 造成的冲突:
    bash
    git rebase --continue
    (注意:Rebase 时通常不需要通过 git commit 生成新提交,只需 continue)

5. 使用 VS Code 高效解决 (推荐)

Visual Studio Code 对 Git 冲突提供了极佳的图形化支持,极大降低了心智负担。

1. Code Lens 快捷操作

当 VS Code 检测到冲突标记时,会在代码上方显示一行灰色的快捷操作:

Accept Current Change | Accept Incoming Change | Accept Both Changes | Compare Changes

  • Accept Current Change (采用当前更改):保留绿色高亮部分。
  • Accept Incoming Change (采用传入更改):保留蓝色/紫色高亮部分。
  • Accept Both Changes (保留双方):自动移除标记,保留两段代码。
  • Compare Changes:开启左右分屏对比视图。

2. 合并编辑器 (Merge Editor)

在 VS Code 底部或文件右键菜单中,选择 "Open in Merge Editor" (在合并编辑器中打开)。这会展示一个三栏视图:

  • 左侧 (Incoming):显示来源分支的代码。
  • 右侧 (Current):显示当前分支的代码。
  • 底部 (Result):显示最终合并后的预览结果。

你可以在底部窗格直接编辑,实时查看合并后的最终形态,非常适合复杂的逻辑合并。

6. 特殊场景处理

场景一:放弃合并 (后悔药)

如果你在解决冲突的过程中把代码改乱了,或者发现现在不适合合并,可以随时终止操作,回退到合并前的状态:

bash
# 终止合并
git merge --abort

# 终止变基
git rebase --abort

场景二:二进制文件冲突

对于图片 (.png)、文档 (.docx) 等二进制文件,无法像文本代码那样合并。你需要明确指定使用哪一个版本:

bash
# 强制使用当前分支的版本 (保留我的)
git checkout --ours images/logo.png

# 强制使用传入分支的版本 (使用它的)
git checkout --theirs images/logo.png

# 标记为已解决
git add images/logo.png

场景三:只想要“他们”的版本

有时你很确定自己的修改是错误的,或者只想完全覆盖:

bash
# 慎用:将当前目录所有冲突文件都重置为传入版本
git checkout --theirs .
git add .

7. 最佳实践:如何减少冲突?

虽然冲突不可避免,但良好的习惯可以大幅降低其频率和解决难度:

  1. 频繁拉取 (Pull Frequently):每天开始工作前先 git pull,开发过程中也定期拉取主分支代码,尽早发现由于基底落后产生的问题。
  2. 细粒度提交 (Small Commits):避免一个 Commit 修改几十个文件。小而原子化的提交让冲突范围更集中,更容易排查。
  3. 分支策略 (Branching):团队成员尽量避免同时修改同一个核心文件。
  4. 及时沟通:如果要重构底层代码或目录结构,提前在群里吼一声,避免同事在旧结构上通过大量工作。
  5. 开启自动变基:建议配置 git pull 默认使用 rebase 模式,保持提交历史线性的同时,强迫你在拉取时就处理冲突,而不是积压到最后合并时。
    bash
    git config --global pull.rebase true

总结

遇到冲突不要慌。Git 冲突本质上是 Git 对你的保护——它不敢擅自做主,所以把决定权交给了你。深呼吸,看清 CurrentIncoming,你一定能理清代码的逻辑。