合并与冲突解决
分支开发完成后,需要把修改合并到主分支。本章介绍 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)的内容。
解决冲突的步骤
- 编辑文件,保留需要的内容,删除所有冲突标记;
- 使用
git add将文件标记为已解决; - 执行
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 会把分支上的所有修改合并到暂存区,但不会创建合并提交,需要你手动提交一次。