Git中文社区

合并与冲突解决

分支开发完成后,需要把修改合并到主分支。本章介绍 git merge 的两种合并方式,以及遇到冲突时该怎么办。

基本合并

先切换到接收修改的分支,再合并另一个分支:

git switch main
git merge feature-login

快进合并(Fast-forward)

如果 main 在创建 feature 之后没有新的提交,合并时 Git 只需要把 main 指针向前移动即可,这就是快进合并:

合并前:
A ── B ── C          (main)
           \
            D ── E   (feature)

合并后:
A ── B ── C ── D ── E   (main, feature)

快进合并不会产生新的提交,历史保持一条直线。如果希望即使可以快进也保留一个合并提交(以便看出这里曾有一个分支),可以使用:

git merge --no-ff feature-login

三方合并

如果两个分支都有了新提交,Git 会找到它们的共同祖先,将两个分支末端和共同祖先进行三方合并,并自动创建一个新的合并提交:

合并前:
A ── B ── C ── F        (main)
           \
            D ── E      (feature)

合并后:
A ── B ── C ── F ── M   (main)
           \       /
            D ── E      (feature)

合并提交 M 有两个父提交。

合并冲突

如果两个分支修改了同一个文件的同一部分,Git 就无法自动合并,这就是冲突:

git merge feature-login
# Auto-merging index.html
# CONFLICT (content): Merge conflict in index.html
# Automatic merge failed; fix conflicts and then commit the result.

此时合并暂停,git status 会列出冲突的文件:

git status
# Unmerged paths:
#   both modified:   index.html

打开冲突文件,可以看到 Git 插入的冲突标记:

<<<<<<< HEAD
<footer>联系我们:[email protected]</footer>
=======
<footer>请发送邮件至 [email protected]</footer>
>>>>>>> feature-login
  • <<<<<<< HEAD 到 ======= 之间是当前分支(main)的内容;
  • ======= 到 >>>>>>> 之间是被合并分支(feature-login)的内容。

解决冲突的步骤

  1. 编辑文件,保留需要的内容,删除所有冲突标记;
  2. 使用 git add 将文件标记为已解决;
  3. 执行 git commit 完成合并。
# 编辑 index.html 解决冲突后
git add index.html
git commit

放弃合并

如果冲突太多,想回到合并之前的状态:

git merge --abort

整个文件采用某一方的版本

# 采用当前分支的版本
git checkout --ours index.html

# 采用被合并分支的版本
git checkout --theirs index.html

git add index.html

使用工具解决冲突

VS Code、JetBrains IDE 都内置了可视化的冲突解决界面,点击"采用当前更改 / 采用传入更改 / 保留双方"即可。也可以配置外部合并工具:

git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
git mergetool

提示

设置 git config --global merge.conflictStyle zdiff3 后,冲突标记中还会显示共同祖先的原始内容,更容易判断双方各改了什么。

减少冲突的建议

  • 经常把主分支的更新合并到自己的分支,不要让分支"活"得太久;
  • 每次提交保持小而专注;
  • 团队统一代码格式化工具,避免因格式差异产生冲突;
  • 大规模重构前与团队沟通。

压缩合并(Squash)

如果功能分支有很多零碎的提交,而你只想在主分支上留下一个整洁的提交:

git merge --squash feature-login
git commit -m "新增用户登录功能"

--squash 会把分支上的所有修改合并到暂存区,但不会创建合并提交,需要你手动提交一次。