【Git#3】分支管理下的分支策略_git 分支策略
📃个人主页:island1314
⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞
- 生活总是不会一帆风顺,前进的道路也不会永远一马平川,如何面对挫折影响人生走向 – 《人民日报》
🔥 目录
一、分支管理策略
通常在合并分支时,一般情况下Git会采用 Fast forward
模式。还记得如果我们采用 Fast forward
模式之后,形成的合并结果是什么呢?
在这种 Fast forward
模式下,删除分支后,查看分支历史时,会丢掉分支信息,看不出来最新提交到底是 merge 进来的还是正常提交的。
但在合并冲突部分,我们也看到通过解决冲突问题,会再进行一次新的提交,得到的最终状态为
那么这就不是 Fast forward
模式了,这样的好处是,从分支历史上就可以看出分支信息。
- 例如:上面已经删除了在合并冲突部分创建的
dev1
分支,但依旧能看到 master 其实是由其他分支合并得到,如下:
lighthouse@VM-8-10-ubuntu:gitcode$ git log --graph --pretty=oneline --abbrev-commit* cb7ce27 (HEAD -> master) merge book|\\ | * da3c3b1 modify book* | fc26892 modify book|/
Git 支持我们强制禁用 Fast forward
模式,那么就会在 merge 时生成一个新的 commit
,这样从分支历史上就可以看出分支信息。
下面我们实战一下 --no-ff
方式的 git merge
。首先,创建新的分支 dev2
,并切换至新的分支
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout -b dev2Switched to a new branch \'dev2\'
修改 book
文件,并且提交一个新的 commit
lighthouse@VM-8-10-ubuntu:gitcode$ cat bookHello Island1314Hello Worldhello version1hello version2hello version3write bbb for new brancha,b,c,dlighthouse@VM-8-10-ubuntu:gitcode$ git add .lighthouse@VM-8-10-ubuntu:gitcode$ git commit -m \"modify Readme\"[dev2 2bd7b8b] modify Readme 1 file changed, 1 insertion(+)
切换 master
分支,进行合并
lighthouse@VM-8-10-ubuntu:gitcode$ git checkout masterSwitched to branch \'master\'lighthouse@VM-8-10-ubuntu:gitcode$ git merge --no-ff -m \"merge with no-ff\" dev2Merge made by the \'ort\' strategy. book | 1 + 1 file changed, 1 insertion(+)lighthouse@VM-8-10-ubuntu:gitcode$ cat bookHello Island1314Hello Worldhello version1hello version2hello version3write bbb for new brancha,b,c,d
请注意
--no-ff
参数,表示禁用Fast forward
模式。禁用Fast forward
模式后合并会创建一个新的 commit,所以加上-m 参数,把描述写进去。
合并后,查看分支历史:
lighthouse@VM-8-10-ubuntu:gitcode$ git log --graph --pretty=oneline --abbrev-commit* ac37cfb (HEAD -> master) merge with no-ff|\\ | * 2bd7b8b (dev2) modify Readme|/ * cb7ce27 merge book|\\ | * da3c3b1 modify book* | fc26892 modify book|/ * 33a5368 modify book
可以看到,不使用 Fast forward
模式,merge 就像如下:
所以在合并分支时,加上 --no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而 fast forward
合并就看不出来曾经做过合并。
二、分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
- 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
- 那在哪干活呢?干活都在dev分支上
- 也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在
master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了
所以,团队合作的分支看起来就像如下这样:
三、六种合并策略
1. 合并策略全景图
#mermaid-svg-ALf8J0pwNcOMCuUH {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ALf8J0pwNcOMCuUH .error-icon{fill:#552222;}#mermaid-svg-ALf8J0pwNcOMCuUH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ALf8J0pwNcOMCuUH .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-ALf8J0pwNcOMCuUH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ALf8J0pwNcOMCuUH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ALf8J0pwNcOMCuUH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ALf8J0pwNcOMCuUH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ALf8J0pwNcOMCuUH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ALf8J0pwNcOMCuUH .marker.cross{stroke:#333333;}#mermaid-svg-ALf8J0pwNcOMCuUH svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ALf8J0pwNcOMCuUH .pieCircle{stroke:black;stroke-width:2px;opacity:0.7;}#mermaid-svg-ALf8J0pwNcOMCuUH .pieTitleText{text-anchor:middle;font-size:25px;fill:black;font-family:\"trebuchet ms\",verdana,arial,sans-serif;}#mermaid-svg-ALf8J0pwNcOMCuUH .slice{font-family:\"trebuchet ms\",verdana,arial,sans-serif;fill:#333;font-size:17px;}#mermaid-svg-ALf8J0pwNcOMCuUH .legend text{fill:black;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:17px;}#mermaid-svg-ALf8J0pwNcOMCuUH :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 45% 30% 10% 8% 5% 2% Git 合并策略使用场景分析 Recursive (复杂合并) Fast Forward (快速同步) Ours/Theirs (特殊处理) Octopus (多分支集成) Subtree (模块化开发) Resolve (遗留系统)
2. 六大合并策略详解
① Fast Forward(快进合并)
- 触发条件:目标分支没有新提交
命令示例:
示例1:
git merge --ff-only dev # 强制仅快进合并
示例2:
# 强制创建合并提交(即使可以快进)git merge --no-ff feature/login# 查看合并后的线性历史git log --graph --oneline --decorate
示例3:错误演示
# 错误:在已提交的主分支使用快进合并导致历史混乱git checkout maingit merge --ff-only hotfix # 若main有新提交则会失败
-
图示:
-
特点:
- 线性历史
- 无合并提交
- 适用于临时特性分支
② Recursive(递归合并)
- 触发条件:存在分叉提交且需三方合并
命令示例:
git merge -s recursive -X patience dev # 使用特定差异算法
-
图示:
-
特点:
- 自动处理多个共同祖先
- 产生合并提交
- 默认策略(占 Git 合并的 70%+)
③ Ours/Theirs(选择性合并)
- 触发条件:需要强制保留特定分支内容
命令示例:
示例1:使用规范
# 正确流程示例(架构迁移场景):git checkout new-archgit merge -s ours old-arch # 保留新架构git checkout old-archgit merge new-arch # 同步代码但保留旧架构
示例2:危险操作告警
# 错误示例:直接覆盖生产分支git checkout productiongit merge -X theirs staging # 可能导致生产数据丢失!
- 图示:
-
特点:
- 人工指定冲突解决方案
- 常用于保留分支架构
- 高风险操作(可能覆盖代码)
④ Octopus(章鱼合并)
-
触发条件:同时合并多个分支
-
命令示例:
git merge dev feat hotfix # 一次性合并三个分支
-
图示:
-
特点:
- 仅限无冲突合并
- 常用于 CI/CD 流水线
- 历史记录最简洁
⑤ Subtree(子树合并)
-
触发条件:合并外部仓库到子目录
-
命令示例:
git merge -s subtree --allow-unrelated-histories external-repo
-
图示:
-
特点:
- 保留外部仓库独立历史
- 自动处理路径冲突
- 适合组件化开发
⑥ Resolve(解决合并)
-
触发条件:简单分叉合并(Git 早期版本)
-
命令示例:
git merge -s resolve legacy-branch
-
图示:
-
特点:
- 仅处理单共同祖先
- 比 recursive 更快
- 逐渐被淘汰
3. 策略对比矩阵
4. 实战场景推荐
- 功能开发:
Recursive
(默认) +--no-ff
保留合并痕迹 - 紧急修复:
Fast Forward
快速上线 - 架构迁移:
Ours
策略保留新架构 - 多特性发布:
Octopus
一次合并多个已验证分支 - 组件更新:
Subtree
管理子仓库 - 遗留系统:
Resolve
处理简单合并
5. 高级调试技巧
# 查看可用的合并策略git merge -s help# 显示合并细节(调试用)git merge --verbose --stat -m \"合并日志\"# 分析合并基准git merge-base --all master dev# 可视化合并过程git mergetool -t meld
分支治理规范:
- 黄金法则:main分支必须使用
--no-ff
合并 - 权限控制:保护分支禁止使用theirs策略
- 合并检查清单:
- ✅ 代码评审完成
- ✅ CI流水线通过
- ✅ 文档同步更新
- ✅ 兼容性测试报告
合并策略决策树
#mermaid-svg-Ipe9nAlvEQkz6XkX {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .error-icon{fill:#552222;}#mermaid-svg-Ipe9nAlvEQkz6XkX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ipe9nAlvEQkz6XkX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .marker.cross{stroke:#333333;}#mermaid-svg-Ipe9nAlvEQkz6XkX svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ipe9nAlvEQkz6XkX .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .cluster-label text{fill:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .cluster-label span{color:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .label text,#mermaid-svg-Ipe9nAlvEQkz6XkX span{fill:#333;color:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .node rect,#mermaid-svg-Ipe9nAlvEQkz6XkX .node circle,#mermaid-svg-Ipe9nAlvEQkz6XkX .node ellipse,#mermaid-svg-Ipe9nAlvEQkz6XkX .node polygon,#mermaid-svg-Ipe9nAlvEQkz6XkX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ipe9nAlvEQkz6XkX .node .label{text-align:center;}#mermaid-svg-Ipe9nAlvEQkz6XkX .node.clickable{cursor:pointer;}#mermaid-svg-Ipe9nAlvEQkz6XkX .arrowheadPath{fill:#333333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ipe9nAlvEQkz6XkX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Ipe9nAlvEQkz6XkX .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Ipe9nAlvEQkz6XkX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ipe9nAlvEQkz6XkX .cluster text{fill:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX .cluster span{color:#333;}#mermaid-svg-Ipe9nAlvEQkz6XkX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ipe9nAlvEQkz6XkX :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 是 否 是 否 是 否 是 否 是 否 开始合并 目标分支是否落后? Fast Forward 有多个共同祖先? Recursive with patience 需要强制覆盖? Ours/Theirs 合并多个分支? Octopus 合并子目录? Subtree Resolve
🔥实际开发中需要根据项目需求选择最合适的合并方式。建议在团队中制定《合并策略规范》,例如长期分支强制使用 --no-ff
,第三方库必须用 subtree
等,以保持代码库的健康度。