← 返回面试总览

🔧 代码冲突解决方案面试题

掌握冲突处理技巧,保证团队协作顺畅

📚 代码冲突解决核心知识

1. 什么是 Git 冲突?为什么会产生冲突?简单

冲突定义

当两个分支对同一文件的同一部分进行了不同的修改,Git 无法自动合并时就会产生冲突。

产生冲突的场景

  • merge 冲突:合并分支时,两个分支修改了同一行代码
  • rebase 冲突:rebase 过程中,提交与目标分支有冲突
  • cherry-pick 冲突:挑选的提交与当前分支有冲突
  • pull 冲突:本地和远程对同一文件有不同修改

冲突标记

<<<<<<< HEAD
// 当前分支的代码
console.log("Hello from main");
=======
// 要合并的分支的代码
console.log("Hello from feature");
>>>>>>> feature/new-feature

冲突标记说明

  • <<<<<<< HEAD:当前分支的内容开始
  • =======:分隔符
  • >>>>>>> branch-name:要合并的分支内容结束
2. 如何查看冲突文件和冲突内容?简单

查看冲突文件

# 查看冲突状态
git status

# 输出示例:
# Unmerged paths:
#   both modified:   src/main.js
#   both modified:   src/utils.js

# 查看冲突详情
git diff

# 只查看冲突部分
git diff --name-only --diff-filter=U

# 查看三方对比(base、ours、theirs)
git diff --base 
git diff --ours 
git diff --theirs 

使用图形化工具

# 使用 Git 自带的合并工具
git mergetool

# 配置 VS Code 作为合并工具
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# 配置 Beyond Compare
git config --global merge.tool bc
git config --global mergetool.bc.path "C:/Program Files/Beyond Compare 4/bcomp.exe"

查看冲突历史

# 查看导致冲突的提交
git log --merge

# 查看文件修改历史
git log --oneline --all --graph -- 

# 查看谁修改了这一行
git blame 
3. 如何手动解决 merge 冲突?中等

解决步骤

# 1. 尝试合并
git merge feature/new-feature

# 2. 查看冲突文件
git status

# 3. 打开冲突文件,手动编辑
# 删除冲突标记,保留需要的代码

# 4. 标记冲突已解决
git add 

# 5. 完成合并
git commit -m "Merge feature/new-feature"

# 或者放弃合并
git merge --abort

冲突解决示例

// 冲突前的文件
<<<<<<< HEAD
function calculate(a, b) {
    return a + b;
}
=======
function calculate(x, y) {
    return x * y;
}
>>>>>>> feature/new-feature

// 解决后的文件(保留两个功能)
function add(a, b) {
    return a + b;
}

function multiply(x, y) {
    return x * y;
}

使用策略选项

# 完全采用当前分支的版本
git checkout --ours 

# 完全采用要合并分支的版本
git checkout --theirs 

# 然后标记为已解决
git add 
4. 如何解决 rebase 过程中的冲突?中等

rebase 冲突特点

rebase 是逐个应用提交,可能需要多次解决冲突。

解决步骤

# 1. 开始 rebase
git rebase main

# 2. 遇到冲突,Git 会停下来
# CONFLICT (content): Merge conflict in file.js

# 3. 查看冲突
git status

# 4. 手动解决冲突,编辑文件

# 5. 标记冲突已解决
git add 

# 6. 继续 rebase
git rebase --continue

# 7. 如果还有冲突,重复步骤 3-6

# 放弃 rebase
git rebase --abort

# 跳过当前提交
git rebase --skip

实战示例

# 场景:feature 分支 rebase 到 main
git checkout feature/user-profile
git rebase main

# 第一个冲突
# 解决冲突...
git add .
git rebase --continue

# 第二个冲突
# 解决冲突...
git add .
git rebase --continue

# 完成 rebase
# Successfully rebased and updated refs/heads/feature/user-profile

使用策略

# rebase 时优先采用当前分支的版本
git rebase -X ours main

# rebase 时优先采用目标分支的版本
git rebase -X theirs main
5. 如何使用 git rerere 自动解决重复冲突?中等

rerere 定义

rerere(reuse recorded resolution)记录冲突解决方案,自动应用到相同冲突。

启用 rerere

# 全局启用
git config --global rerere.enabled true

# 查看配置
git config --get rerere.enabled

# 自动暂存 rerere 解决的文件
git config --global rerere.autoupdate true

使用流程

# 第一次遇到冲突
git merge feature/branch1
# 手动解决冲突
git add .
git commit -m "Merge branch1"

# 第二次遇到相同冲突
git merge feature/branch2
# rerere 自动应用之前的解决方案
# Resolved 'file.js' using previous resolution.

# 查看 rerere 记录
git rerere status

# 查看 rerere 差异
git rerere diff

# 清除 rerere 缓存
git rerere clear
git rerere forget 

典型场景

  • 长期维护的功能分支需要频繁 rebase
  • 多个分支有相似的修改
  • 回滚后重新合并
6. 如何处理二进制文件冲突?困难

二进制文件冲突特点

Git 无法合并二进制文件(图片、视频、Office 文档等),只能选择一个版本。

查看冲突

# 查看冲突的二进制文件
git status

# 输出示例:
# both modified:   images/logo.png
# both modified:   docs/report.docx

解决方案

# 方案1:采用当前分支的版本
git checkout --ours images/logo.png
git add images/logo.png

# 方案2:采用要合并分支的版本
git checkout --theirs images/logo.png
git add images/logo.png

# 方案3:手动选择外部文件
# 1. 导出两个版本
git show :2:images/logo.png > logo_ours.png
git show :3:images/logo.png > logo_theirs.png

# 2. 手动选择或合并
# 3. 复制选择的版本
cp logo_final.png images/logo.png
git add images/logo.png

预防措施

  • 使用 Git LFS 管理大型二进制文件
  • 约定二进制文件由专人维护
  • 使用版本号命名:logo_v1.pnglogo_v2.png
  • 将生成的二进制文件加入 .gitignore

配置 Git LFS

# 安装 Git LFS
git lfs install

# 跟踪大文件
git lfs track "*.psd"
git lfs track "*.mp4"

# 查看跟踪的文件
git lfs ls-files
7. 如何处理文件重命名导致的冲突?中等

重命名冲突场景

  • 一个分支重命名文件,另一个分支修改了该文件
  • 两个分支将同一文件重命名为不同名称
  • 一个分支删除文件,另一个分支修改了该文件

场景1:重命名 + 修改

# main 分支:user.js 重命名为 user-model.js
git mv user.js user-model.js
git commit -m "refactor: rename user.js"

# feature 分支:修改了 user.js
# 修改内容...
git commit -m "feat: add user validation"

# 合并时 Git 通常能自动处理
git checkout main
git merge feature/user-validation

# 如果 Git 无法自动处理
# 1. 手动将修改应用到新文件名
git checkout --theirs user.js
git mv user.js user-model.js
# 2. 手动合并修改
# 3. 删除旧文件
git rm user.js
git add user-model.js
git commit

场景2:双重重命名

# main: user.js → user-model.js
# feature: user.js → user-service.js

# 合并后需要手动决定
# 1. 选择一个名称
git mv user-model.js user-service.js
# 2. 删除另一个
git rm user-model.js
# 3. 提交
git add .
git commit -m "resolve: use user-service.js"

场景3:删除 + 修改

# main: 删除了 old-api.js
# feature: 修改了 old-api.js

git merge feature/update-api
# CONFLICT (modify/delete): old-api.js

# 决策:
# 1. 如果确实要删除
git rm old-api.js
# 2. 如果要保留修改
git add old-api.js

git commit
8. 如何使用 git merge 策略选项?中等

合并策略

策略说明使用场景
ours完全采用当前分支废弃另一分支的修改
theirs完全采用要合并的分支完全接受新分支
recursive默认策略,三方合并大多数情况
octopus合并多个分支同时合并多个分支

使用 -X 选项

# 冲突时优先采用当前分支
git merge -X ours feature/branch

# 冲突时优先采用要合并的分支
git merge -X theirs feature/branch

# 忽略空白字符差异
git merge -X ignore-space-change feature/branch
git merge -X ignore-all-space feature/branch

# 重命名检测阈值
git merge -X rename-threshold=50 feature/branch

使用 -s 选项

# 使用 ours 策略(完全忽略另一分支)
git merge -s ours feature/old-feature

# 使用 octopus 策略(合并多个分支)
git merge -s octopus feature/a feature/b feature/c

# 使用 subtree 策略
git merge -s subtree feature/library

实战示例

# 场景:合并时完全采用 main 的配置文件
git merge -X ours feature/new-config

# 场景:合并时完全采用 feature 的代码
git merge -X theirs feature/refactor

# 场景:废弃旧分支但保留合并记录
git merge -s ours feature/deprecated
9. 如何处理大规模冲突(几十个文件)?困难

分析冲突

# 统计冲突文件数量
git diff --name-only --diff-filter=U | wc -l

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

# 按目录分组
git diff --name-only --diff-filter=U | cut -d/ -f1 | sort | uniq -c

# 查看冲突严重程度
git diff --stat

批量处理策略

# 1. 对于配置文件,统一采用一方
git checkout --ours config/
git add config/

# 2. 对于生成文件,重新生成
git checkout --theirs package-lock.json
npm install
git add package-lock.json

# 3. 对于代码文件,分批处理
# 先处理核心模块
git checkout --ours src/core/
git add src/core/

# 再处理其他模块
# 逐个文件手动解决...

使用脚本辅助

#!/bin/bash
# resolve-conflicts.sh

# 获取所有冲突文件
conflicts=$(git diff --name-only --diff-filter=U)

for file in $conflicts; do
    echo "Processing $file..."
    
    # 根据文件类型选择策略
    if [[ $file == *.json ]]; then
        # JSON 文件采用 theirs
        git checkout --theirs "$file"
    elif [[ $file == config/* ]]; then
        # 配置文件采用 ours
        git checkout --ours "$file"
    else
        # 其他文件需要手动处理
        echo "Manual resolution needed for $file"
        continue
    fi
    
    git add "$file"
done

echo "Batch resolution complete. Manual files remaining:"
git diff --name-only --diff-filter=U

预防措施

  • 频繁同步主分支,避免分支长期分离
  • 模块化开发,减少文件重叠
  • 使用代码格式化工具统一风格
  • 大型重构分阶段进行
10. 如何撤销已经解决的冲突?中等

撤销场景

  • 解决冲突后发现方案不对
  • 想重新开始解决冲突
  • 误操作需要回退

撤销方法

# 场景1:还未 commit,撤销单个文件
git checkout --conflict=merge 
# 文件恢复到冲突状态

# 场景2:还未 commit,撤销所有文件
git merge --abort
git rebase --abort

# 场景3:已经 commit,回退到合并前
git reset --hard ORIG_HEAD

# 场景4:已经 push,创建反向提交
git revert -m 1 
# -m 1 表示保留第一个父提交(通常是 main)

重新解决冲突

# 1. 撤销错误的解决
git checkout --conflict=merge src/main.js

# 2. 重新编辑文件
vim src/main.js

# 3. 标记为已解决
git add src/main.js

# 4. 继续合并
git merge --continue
# 或
git rebase --continue

查看合并前的状态

# 查看合并前的 HEAD
git reflog
# 找到合并前的提交

# 查看 ORIG_HEAD(合并前的 HEAD)
git show ORIG_HEAD

# 对比合并前后
git diff ORIG_HEAD HEAD
11. 如何预防代码冲突?中等

开发习惯

  • 小步提交:频繁提交,每次提交只做一件事
  • 频繁同步:每天至少一次 pull/rebase 主分支
  • 短期分支:功能分支不要存活太久
  • 及时合并:功能完成后尽快合并到主分支

团队协作

# 1. 开始工作前先同步
git checkout main
git pull origin main
git checkout feature/my-feature
git rebase main

# 2. 开发过程中定期同步
git fetch origin
git rebase origin/main

# 3. 提交前最后一次同步
git fetch origin
git rebase origin/main
git push origin feature/my-feature

代码组织

  • 模块化:不同功能放在不同文件
  • 接口隔离:通过接口而非直接修改共享代码
  • 配置分离:配置文件和代码分离
  • 代码格式化:使用统一的格式化工具

工具配置

# 配置自动 rebase
git config --global pull.rebase true

# 配置 Prettier
npm install --save-dev prettier
echo "*.js" > .prettierignore
npx prettier --write "src/**/*.js"

# 配置 ESLint
npm install --save-dev eslint
npx eslint --init
npx eslint --fix "src/**/*.js"

沟通机制

  • 大型重构提前通知团队
  • 修改公共文件前先沟通
  • 使用 Code Review 发现潜在冲突
  • 定期同步开发进度
12. 如何处理 package-lock.json 或 yarn.lock 冲突?困难

锁文件冲突特点

锁文件是自动生成的,手动解决容易出错,应该重新生成。

npm 项目

# 方法1:采用一方后重新安装
git checkout --theirs package-lock.json
npm install
git add package-lock.json

# 方法2:删除后重新生成
git checkout --ours package.json
rm package-lock.json
npm install
git add package-lock.json

# 方法3:合并 package.json 后重新生成
# 1. 手动解决 package.json 冲突
git add package.json
# 2. 删除锁文件
rm package-lock.json
# 3. 重新安装
npm install
# 4. 提交
git add package-lock.json
git commit

yarn 项目

# 采用一方后重新安装
git checkout --theirs yarn.lock
yarn install
git add yarn.lock

# 或删除后重新生成
rm yarn.lock
yarn install
git add yarn.lock

pnpm 项目

# 重新生成锁文件
rm pnpm-lock.yaml
pnpm install
git add pnpm-lock.yaml

最佳实践

  • 永远不要手动编辑锁文件
  • 先解决 package.json 冲突
  • 删除锁文件后重新安装
  • 提交前验证项目能正常运行

预防措施

# 配置 .gitattributes
echo "package-lock.json merge=npm" >> .gitattributes
echo "yarn.lock merge=yarn" >> .gitattributes

# 配置自定义合并驱动
git config merge.npm.driver "npm install"
git config merge.yarn.driver "yarn install"
13. 如何使用 git diff3 查看三方对比?中等

diff3 定义

显示三个版本的对比:共同祖先(base)、当前分支(ours)、要合并的分支(theirs)。

启用 diff3

# 全局启用
git config --global merge.conflictstyle diff3

# 仅当前仓库启用
git config merge.conflictstyle diff3

# 查看配置
git config --get merge.conflictstyle

diff3 格式

<<<<<<< HEAD (ours)
function calculate(a, b) {
    return a + b;
}
||||||| base (共同祖先)
function calculate(a, b) {
    return a - b;
}
=======
function calculate(x, y) {
    return x * y;
}
>>>>>>> feature/new-feature (theirs)

优势

  • 看到原始代码,理解修改意图
  • 判断哪个分支的修改更合理
  • 避免误删重要代码

实战示例

// 场景:两个分支都修改了同一函数
<<<<<<< HEAD
function getUserName(user) {
    return user.firstName + ' ' + user.lastName;
}
||||||| base
function getUserName(user) {
    return user.name;
}
=======
function getUserName(user) {
    return user.fullName;
}
>>>>>>> feature/user-refactor

// 分析:
// base: 使用 user.name
// ours: 改为拼接 firstName + lastName
// theirs: 改为使用 fullName
// 解决:需要确认数据结构,选择正确的实现
14. 如何处理已经 push 的错误合并?困难

场景分析

错误的合并已经推送到远程,其他人可能已经基于这个合并继续开发。

方案1:使用 revert(推荐)

# 1. 找到错误的合并提交
git log --oneline --graph

# 2. revert 合并提交
git revert -m 1 
# -m 1 表示保留第一个父提交(main)

# 3. 推送
git push origin main

# 4. 如果后续需要重新合并该分支
# 先 revert 这个 revert
git revert 
# 然后再合并
git merge feature/branch

方案2:reset + force push(危险)

# ⚠️ 仅在确保没有其他人基于此提交开发时使用

# 1. reset 到合并前
git reset --hard 

# 2. 强制推送
git push --force origin main

# 3. 通知团队成员
# 其他人需要执行:
git fetch origin
git reset --hard origin/main

方案3:创建修复分支

# 1. 从错误合并前创建新分支
git checkout -b hotfix/fix-merge 

# 2. 重新正确合并
git merge --no-ff feature/branch
# 手动解决冲突

# 3. 推送新分支
git push origin hotfix/fix-merge

# 4. 创建 PR 合并到 main
# 5. 通知团队切换到新分支

团队协作注意事项

  • 立即通知团队成员
  • 说明问题和解决方案
  • 提供详细的操作步骤
  • 确认所有人都已同步
15. 在面试中如何回答冲突解决相关问题?困难

回答框架

1. 理解冲突原因

  • 说明什么是冲突
  • 解释为什么会产生冲突
  • 举例常见冲突场景

2. 解决步骤

# 标准流程
1. git status 查看冲突文件
2. 打开文件,查看冲突标记
3. 分析冲突原因,理解双方修改意图
4. 手动编辑,保留正确的代码
5. 删除冲突标记
6. git add 标记为已解决
7. git commit 完成合并
8. 测试验证

3. 工具和技巧

  • 使用 git mergetool 图形化工具
  • 使用 --ours / --theirs 快速选择
  • 使用 rerere 自动解决重复冲突
  • 使用 diff3 查看三方对比

4. 实战经验

面试示例回答:

"在我之前的项目中,遇到过一次大规模冲突。当时我们有两个
并行开发的功能分支,都对核心模块进行了重构。合并时产生了
50+ 个文件的冲突。

我的处理方式是:
1. 先分析冲突文件,按模块分类
2. 对于配置文件,统一采用主分支的版本
3. 对于核心业务代码,逐个文件仔细对比
4. 使用 VS Code 的三方对比工具辅助
5. 解决后进行完整的回归测试
6. 总结经验,制定了团队的冲突预防规范

这次经历让我深刻理解了频繁同步和模块化开发的重要性。"

5. 预防措施

  • 频繁同步主分支
  • 小步提交,短期分支
  • 模块化开发
  • 使用代码格式化工具
  • 团队沟通机制

加分项

  • 提到具体的工具(VS Code、Beyond Compare)
  • 说明团队协作经验
  • 展示对 Git 原理的理解
  • 提出改进建议