你是否遇到过这种场景:功能开发顺利完成,合并分支时却踩坑无数?
你是否对 --no-ff
、--squash
的选择一头雾水?
今天我们就聊聊 Git Merge的用法,从原理到命令、从冲突到应对,一次讲清楚。
合并原理
合并的本质是寻找两个分支的共同祖先,然后通过差异对比生成新版本。
为了直观的理解合并原理,我们来看一个例子:
C---D---E 你的分支 (feature)
/
A---B---F---G 主分支 (main)
当执行git merge feature
,即合并时,Git会找到共同祖先B,然后比较B到E和B到G的差异,如果没有冲突,那就进行合并,如果有冲突,那就需要解决冲突后进行合并。
合并策略
1. 普通合并(Fast-Forward)
适用场景:主分支没有新提交,功能分支是直线开发
初始状态:
A---B---C ← main
\
D---E ← feature
执行命令
git checkout main
git merge feature # 直接把main指针移到feature末端
合并后:
A---B---C---D---E ← main, feature
这种合并不会生成合并提交,适合小功能开发
2. 保留历史的合并(–no-ff)
适用场景:需要明确看到合并历史的中大型功能
初始状态:
A---B---C ← main
\
D---E ← feature
执行命令:
git merge --no-ff feature # 强制生成合并节点
执行时会打开一个窗口用于填写提交信息,填写后保存退出即可
合并后:
A---B---C-------M ← main
\ /
D---E ← feature
优势:
- 保留完整开发历史
- 方便追溯功能开发周期
3. 压缩合并(Squash)
适用场景:功能分支提交太零碎,需要整理历史
初始状态:
A---B---C ← main
\
D---E---F ← feature
执行命令:
git merge --squash feature
git commit -m "完整功能X实现" # 把feature分支的所有提交压成一个
合并后:
A---B---C---S ← main
\
D---E---F ← feature
这会丢失原始提交记录,适合个人开发分支合并到主分支
合并冲突
典型冲突现场
假设你的主分支与feature分支的状态如下所示:
C---D ← main
/
A---B
\
E---F ← feature
如果两个分支修改了同一个文件的同一区域,那么在进行合并的时候就会遇到类型下面的冲突:
$ git merge feature
Auto-merging src/main.cc
CONFLICT (content): Merge conflict in src/main.cc
Automatic merge failed; fix conflicts and then commit the result.
这个时候我们查看冲突文件src/main.cc
,会发现冲突部分被标记了出来:
<<<<<<< HEAD
std::string version = '1.0';
=======
std::string version = '2.0';
>>>>>>> feature
解决方法
- 定位冲突文件:
git status
查看冲突列表 - 人工决策:用编辑器修改冲突内容(选择适当的内容,并删除标记)
- 标记解决:
git add 已解决的文件
- 完成合并:
git commit -m "合并说明"
合并不是结束,而是质量保障的新起点。遇到冲突别慌,那是Git在提醒你:“这里需要人类智慧介入啦!”
合并建议
1. 预演合并
git merge --no-commit --no-ff feature # 先看看合并结果
git merge --abort # 发现不对劲随时取消
2. 分析合并日志
git log --oneline --graph --all # 图形化查看合并历史
3. 合并前更新本地分支
# 先同步最新代码
git checkout main
git pull origin main # 确保本地main是最新的
# 合并
git merge feature
4. 定期合并
结语
通过掌握不同的合并策略与应对技巧,你不仅能避免常见的坑,还能提升团队协作效率。
下次合并就去试试 git merge --no-commit --no-ff
吧!
本文首发于微信公众号《Linux在秋名山》,欢迎大家关注~