一个成功的Git分支模型
本作品原发布账号为【白鸽子中文网】,现转至当前账号【飞翔中文网】。
反思备录(2020/3/5)
这个模型构思于2010年,现已过去10余年,(2010年)那时正处于Git诞生后不久。在这10年间,git-flow(本文中提到的分支模型) 在许多软件队伍里特别流行,以至于人们已经把它当成某种软件标准——但不幸的是它也被视为教条或灵丹妙药。
在这10年里,Git本身已经席卷了世界,而使用Git开发的最受欢迎的软件类型正在更多地转向网络应用程序,至少在我的过滤器泡沫(认知)中是这样。Web应用程序通常是连续交付的,而不是回滚的,而且您不必支持在非主体系中(?)运行的多个版本的软件。
这不是我10年前写博客时想的那种软件。如果您的团队正在持续交付软件,我建议您采用更简单的工作流(如GitHub流),而不是试图将git-flow塞进您的团队。
然而,如果您正在构建明确版本化的软件,或者如果您需要在非主体系中(?)支持多个版本的软件,那么git-flow可能仍然像过去10年一样适合您的团队。在这种情况下,请继续阅读。
总之,永远记住,灵丹妙药是不存在的。充分考虑你自己的背景——不要讨厌自己决定。
正文
在这篇文章中,我介绍了大约一年前我为我的一些项目(包括工作和私人项目)介绍的开发模式,结果证明它非常成功。我想写这篇文章已经有一段时间了,但直到现在,我还没有真正找到时间写得这么彻底。我不谈任何项目的细节,只谈分支策略和发布管理。

为什么使用Git?
有关Git与集中式源代码控制系统的优缺点的详细讨论,请参阅web(http://git.or.cz/gitwiki/GitSvnComparsion)。那里正在进行大量的唇枪舌战。作为一名开发人员,我更喜欢Git,而不是现在的所有其它工具。Git确实改变了开发人员对合并和分支的看法。从我所处的经典CVS/Subversion体系来看,分离(分支)/合并(分支)一直被认为有点可怕(“小心合并冲突,它们会反噬你!”)而你只是偶尔做一次。
但是使用Git,这些操作非常便宜和简单,它们被认为是日常工作流程的核心部分之一。例如,在CVS/Subversion书籍中,分离和合并首先在后面的章节中讨论(对于高级用户),而在每一本Git书籍中,它已经在第3章(基础知识)中讨论过了。
由于其自身的简单性和重复性,分离和合并不再是令人害怕的事情。版本控制工具应该比其他任何工具更能帮助分离/合并。
关于工具的内容已经足够了,让我们继续讨论开发模型。我将在这里介绍的模型基本上只不过是一系列的操作,每个团队成员都必须遵循这些操作才能进入托管软件开发过程。
去中心化又中心化
我们使用的存储仓库设置,它与这个分支模型很好地配合使用,是一个中央“真实”仓库。请注意,该仓库仅被(人为)视为中央仓库(由于Git是一个DVCS[Distributed Version Control System,分布式版本控制系统],因此在技术层面上不存在中央仓库)。我们将此仓库称为原始仓库origin,因为所有Git用户都熟悉这个名称。

每个开发人员都将其代码分离于且提交到原始仓库origin。但除了统一的“推送-拉取”关系之外,每个开发人员还可以从其他同行那里获取更改,以组成子团队。例如,在过早地将正在进行的工作推到原始仓库origin之前,与两个或两个以上的开发人员合作开发一个大的新特性可能会很有用。在上图中,有爱丽丝(alice)和鲍勃(bob)、爱丽丝与大卫(david)、克莱尔(clair)和大卫的子组。
从技术上讲,这意味着Alice定义了一个名为bob的Git远程,指向bob的存储库,反之亦然(bob也定义了一个名为alice的远程仓库并指向)。
核心分支
最核心的是,开发模型受到了现有模型的极大启发。中央仓库持有两个寿命无限(伴随软件开发的整个周期)的核心分支:
master
develop
每个Git用户都应该熟悉原始仓库origin的主分支master。与主分支master并行的还有另一个分支,称为开发分支develop。
我们认为origin/master是核心分支,是因为其HEAD源代码总是反映生产就绪状态。
我们认为origin/develop是核心分支,是因为其HEAD源代码总是反映下一个版本中最新交付的开发更改的状态。有人会称之为“集成分支”。这是构成任何自动夜间构建的基础。
当开发分支develop中的源代码达到稳定点并准备发布时,所有更改都应该以某种方式合并回主分支master,然后用发布号标记。这将在后面详细讨论。
因此,每次将更改合合并到主分支master时,这都是一个新的生产版本。我们倾向于对此非常严格,因此理论上,我们可以使用Git钩子脚本在每次主服务器上提交代码到主分支master时自动构建软件,并将其推出到我们的生产服务器。
支撑分支
除了主分支master和开发分支develop之外,我们的开发模型还使用各种支持分支来帮助团队成员之间的并行开发,简化功能跟踪,为生产发布做准备,并帮助快速解决实时生产问题。与核心分支不同,这些分支的寿命总是有限的,因为它们最终会被移除(不必伴随软件开发的整个周期)。
我们可能(“可能”->言外之意也可以自己定义)使用的不同类型的分支有:
Feature branches
Release branches
Hotfix branches
这些分支每个都有特定的目的,并受严格的规则约束,即哪些分支可以是其原始分支,哪些分支必须是其合并目标。我们将在一分钟内彻底了解它们。
从技术角度来看,这些分支绝非“特殊”。分支类型根据我们使用它们的方式进行分类。它们当然是普通的旧Git分支。
特性分支Feature branches
可以来自于(特定分支):
develop
必须合并到(特定分支):
develop
分支命名约定:
除了master,develop,release-*,或hotfix-*之外的所有命名均可。
特性分支Feature branches(或有时称为主题分支)用于为即将发布或将来发布的版本开发新功能。当开始开发一个特性时,这个特性将被纳入的目标版本在那个时候可能是未知的。特性分支的本质是,只要特性处于开发阶段,它就一直存在,但最终会被合并回开发分支develop(以明确地将新特性添加到即将发布的版本中)或被丢弃(以防出现令人失望的经历)。
特性分支Feature branches通常只存在于开发者存储库中,而不存在于原始仓库origin中。
创建功能分支Creating a feature branch
在开始新特性的工作时,从开发分支develop拉出分支。
$ git checkout -b myfeature develop
Switched to a new branch "myfeature"
在开发过程中合并已完成的功能Incorporating a finished feature on develop
完成的功能可以合并到开发分支develop中,以明确地将它们添加到即将发布的版本中:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop
--no ff标志使合并始终创建一个新的提交对象,即使(不使用的话)合并可以快速执行。这避免了丢失关于特性分支的历史存在的信息,并将一起添加特性的所有提交分组在一起。比较:

在后一种情况下,(合并以后)不可能从Git历史中看到哪些提交对象一起实现了一个特性,您必须手动读取所有日志消息。恢复整个特性(例如一组提交)是一个真正令人头痛的问题,而如果使用了--no ff标志,则很容易完成。
是的,它将创建更多(空)提交对象,但收益远大于成本。
发布分支Release branches
可以来自于(特定分支):
develop
必须合并到(特定分支):
develop和master
分支命名约定:
release-*
发布分支为新的生产发布提供支持。它们允许在最后一刻打点和提交……(原文是:They allow for last-minute dotting of i’s and crossing t’s,此句不明?)。此外,它们还允许小的错误修复和为发布准备元数据(版本号、构建日期等)。通过在发布分支上完成所有这些工作,开发分支develop可以接收下一个大型发布的特性。
从开发分支develop拉出新发布分支的关键时刻是开发分支(几乎)反映了新发布的期望状态。至少此刻所有针对要构建的版本的特性都必须合并在开发分支develop中。所有面向未来版本的功能(可能都不是),它们必须等到发布分支被分离后。
正是从发布分支出现开始,即将发布的版本被分配了一个版本号——而不是更早的时候。直到那一刻(指发布分支出现),开发分支develop反映了“下一次发布”的变化,但在发布分支开始之前,尚不清楚“下一版本”最终会变成0.3还是1.0。这个决定是在发布分支开始时做出的,并由项目关于版本号变更的规则执行。
创建发布分支Creating a release branch
发布分支是从开发分支develop创建的。例如,假设1.1.5版本是当前的生产版本,并且我们即将发布一个大版本。开发分支develop的状态已经为“下一个版本”做好了准备,我们已经决定这将成为1.2版(而不是1.1.6或2.0版)。所以我们拉出分支并给为其起一个反映新版本号的名称:
$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)
在创建一个新分支并切换到该分支后,我们添加版本号。这里,bump-version.sh是一个虚构的shell脚本,它更改了工作副本中的一些文件以反映新版本。(当然,这可以是手动更改,因为某些文件会更改。)然后,提交更改的版本号。
这个新的分支可能会在那里存在一段时间,直到发行版确认推出。在此期间,bug修复可能会应用于此分支(而不是开发分支develop)。严禁在此处添加大型新功能。它们必须合并到开发分支develop中,并且,等待下一个大型版本。
完成发布分支Finishing a release branch
当发布分支的状态准备成为真正的发布时,需要执行一些操作。首先,发布分支被合并到主分支master中(因为主分支master上的每个提交根据定义都是一个新的发布,请记住这点)。接下来,必须标记在主分支master上的提交,以便于将来参考此历史版本。最后,需要将发布分支上所做的更改合并回开发分支develop中,以便将来的发布也包含这些错误修复。
在Git中的前两个步骤:
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2
该版本现已完成,并已标记以供将来参考。
您还可以使用-s或-u<key>标志对标记进行加密签名。
为了保持发布分支中所做的更改,我们需要将这些更改合并回开发分支develop中。在Git中:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
这一步很可能会导致合并冲突(很有可能,因为我们已经更改了版本号)。如果是,请修复并提交。
现在我们真的完成了,发布分支可能会被删除,因为我们不再需要它了:
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
修补程序分支hotfix
可以来自于(特定分支):
master
必须合并到(特定分支):
develop和master
分支命名约定:
hotfix-*
修补程序分支与发布分支非常相似,因为它们也旨在为新的生产发布做准备,尽管是计划外的。它们产生于必须立即对现场制作版本的不期望状态采取行动。当必须立即解决生产版本中的关键bug时,可以从标记生产版本的主分支上的相应标记分出一个修补程序分支。
本质是团队成员(在开发分支develop上)的工作可以继续,而另一个人正在准备快速的生产修复。
创建修补程序分支Creating the hotfix branch
修补程序分支是从主分支master创建的。例如,假设1.2版是当前正在运行的生产版本,并由于严重的bug而导致问题。但开发分支develop中的变更仍然不稳定。然后,我们可以分离出一个修补程序分支并开始解决问题:
$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)
分离后别忘了添加版本号!
然后,修复bug并在一次或多次单独提交中提交修复。
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)
完成修补程序分支Finishing a hotfix branch
完成后,bugfix(hotfix)需要合并回主分支master,但也需要合并回开发分支develop,以确保bugfix(hotfix)也包含在下一个版本中。这完全类似于发布分支的完成方式。
首先,更新主分支master并标记发布。
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1
您还可以使用-s或-u<key>标志对标记进行加密签名。
接下来,在开发分支develop中也加入错误修复:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
这里的规则有一个例外,当发布分支当前存在时,需要将修补程序更改合并到该发布分支中,而不是开发分支develop。当发布分支完成时,也会将合并到发布分支中的错误修复程序合并到开发分支develop中。(如果开发分支develop中的工作立即需要此错误修复,并且无法等待发布分支完成,那么您也可以安全地将错误修复合并到开发分支develop中。)
最后,删除临时分支:
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
总结
虽然这个分支模型并没有什么真正令人震惊的新东西,但这篇文章开头的“大图”表现出在我们的项目中非常有用。它形成了一个优雅的心理模型,易于理解,并允许团队成员对分支和发布过程形成共同的理解。
本文翻译自网络:https://nvie.com/posts/a-successful-git-branching-model
本文由飞翔中文网「feixiang.net」翻译
相关文章:
一个成功的Git分支模型
本作品原发布账号为【白鸽子中文网】,现转至当前账号【飞翔中文网】。 反思备录(2020/3/5) 这个模型构思于2010年,现已过去10余年,(2010年)那时正处于Git诞生后不久。在这10年间,git-flow(本文中提到的分支模型) 在许多软件队伍里…...
Kafka可视化工具KafkaTool工具的使用
Kafka Tool工具 介绍 使用Kafka的小伙伴,有没有为无法直观地查看 Kafka 的 Topic 里的内容而发过愁呢?下面推荐给大家一款带有可视化页面的Kafka工具:Kafka Tool (目前最新版本是 3.0.2) 注意:以前叫Kafk…...
【嵌入式Linux】基于ArmLinux的智能垃圾分类系统项目
目录 1. 功能需求2. Python基础2.1 特点2.2 Python基础知识2.3 dict嵌套简单说明 3. C语言调用Python3.1 搭建编译环境3.2 直接调用python语句3.3 调用无参python函数3.4 调用有参python函数 4. 阿里云垃圾识别方案4.1 接入阿里云4.2 C语言调用阿里云Python接口 5. 香橙派使用摄…...
同等学力申硕-计算机专业-数学基础-历年真题和答案解析
同等学力申请硕士学位考试是比较适合在职人员的提升学位方式,了解过的人应该都知道,现在社会的竞争压力越来越大,为了提高职业生存能力,提升学位在所难免。 为了通过同等学力申请硕士学位考试,对于计算机专业的人来说…...
网络安全漏洞与修复 网络安全软件漏洞
文章目录 一、软件漏洞的概念 1、信息安全漏洞简述2、软件漏洞3、软件漏洞概念4、软件漏洞的成因分析 二、软件漏洞标准化管理 1、软件漏洞分类2、软件漏洞分级3、安全漏洞管理规范 一、软件漏洞的概念 1、信息安全漏洞简述 信息安全漏洞是信息安风险的主要根源之一&…...
STM32:Default_Handler问题
记录代码进入Default_Handler错误的解决办法 一、 问题表述 在一次调试代码的时候,发现代码卡死在启动文件 startup_at32f423xx_.s 的367行,即 B. 处B.是汇编代码,B:跳转到一个标号,这里跳转到一个‘.’,…...
iwebsec-SQL数字型注入
1.判断是否存在漏洞 添加and 11发现正常显示,添加and 12无回显条目,则存在sql注入漏洞 2.因为有回显,尝试union联合注入,使用order by判断出有3个字段 3.使用union联合注入查看回显位,发现3三个字段均有回显ÿ…...
基于Spring Boot的冷链物流系统的设计与实现的设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
LLM(6):理解词嵌入
深度神经网络模型,包括 LLM,无法直接处理原始文本。由于文本是分类的,它与用于实现和训练神经网络的数学操作不兼容。因此,我们需要一种方法来将词语表示为连续值向量。 注意:如果读者对向量和张量不太了解,…...
SQLMesh系列教程:利用date_spine宏构建日期序列实践指南
引言:为什么需要日期维度表? 在数据分析和报表开发中,日期维度表是不可或缺的基础结构,其中包括一定日期范围的日期序列,每个序列包括对应日期属性,如年季月日、是否周末等。无论是计算日粒度销售额、分析…...
sqlite mmap
https://www.sqlite.org/mmap.html 1. 内存映射 I/O 的基本原理 默认机制(传统 I/O) SQLite 默认通过 xRead() 和 xWrite() 方法(对应 read()/write() 系统调用)访问数据库文件。这些方法需要将数据从内核缓冲区复制到用户空间&am…...
Java 大视界 -- 企业数字化转型中的 Java 大数据战略与实践(93)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
Unity Enlighten与Progressive GPU Lightmapper对比分析
一、技术背景与核心差异 1. 算法原理 Enlighten 基于辐射度算法(Radiosity),通过将场景分解为Systems(光照关联单元)和Clusters(计算单元),预计算光照环境中的间接光传输。其核心是…...
linux:环境变量,进程地址空间
一.命令行参数 main的参数:int argc,char*argv[],char*env[] 1.参数意义: argc是命令行调用次程序时传递的参数 例: ls -l -a 传递了三个参数,“ls" "-l" "-a"三个字符串 argv是传递的参…...
mybatis集合映射association与collection
官方文档:MyBatis的一对多关联关系 一、用途 一对一:association 一对多:collection 二、association 比较容易理解,可参考官方文档 三、collection <?xml version"1.0" encoding"UTF-8"?> &l…...
【AIGC】Win10系统极速部署Docker+Ragflow+Dify
【AIGC】WIN10仅3步部署DockerRagflowDify 一、 Docker快速部署1.F2进入bios界面,按F7设置开启VMX虚拟化技术。保存并退出。2.打开控制面板配置开启服务3.到官网下载docker安装包,一键安装(全部默认勾选) 二、 RagFlow快速部署1.确…...
全局上下文网络GCNet:创新架构提升视觉识别性能
摘要:本文介绍了全局上下文网络(GCNet),通过深入分析非局部网络(NLNet),发现其在重要视觉识别任务中学习的全局上下文与查询位置无关。基于此,提出简化的非局部模块、全局上下文建模…...
鸿蒙NEXT项目实战-百得知识库03
代码仓地址,大家记得点个star IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点: 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三…...
Linux上位机开发实战(qt编译之谜)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 很多同学都喜欢用IDE,也能理解。因为不管是visual studio qt插件,还是qt creator其实都帮我们做了很多额外的工作。这里面最…...
【人工智能】【Python】在Scikit-Learn中使用网格搜索对决策树调参
这次实践课最大收获非网格搜索莫属。 # 导入包 import matplotlib.pyplot as plt import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split, GridSearchCV # 网格搜索 from sklearn.tree import DecisionTreeClassi…...
用Python代码生成批量下单json
需求 根据以下json体,生成230OrderList对象生成10位有序的数字字母随机数赋值给OrderDetailList.ApiOrderId 和 OrderDetailList.Traceid生成的Json文件 保存在项目JSON目录中 {"UAccount": "xxxx","Password": "","…...
笔记:代码随想录算法训练营day56:图论理论基础、深搜理论基础、98. 所有可达路径、广搜理论基础
学习资料:代码随想录 连通图是给无向图的定义,强连通图是给有向图的定义 朴素存储:二维数组 邻接矩阵 邻接表:list基础知识:C 容器类 <list> | 菜鸟教程 深搜是沿着一个方向搜到头再不断回溯,转…...
Elasticsearch8.17 集群常见问题排查与解决
一、磁盘水位错误(Flood-Stage Watermark) 当数据节点磁盘空间极度不足并达到洪水阶段水位时,系统会记录以下错误: Error: disk usage exceeded flood-stage watermark, index has read-only-allow-delete block。 为防止磁盘写满,Elasticsearch 会阻止对受影响节点上分片…...
TCP、UDP协议的应用、ServerSocket和Socket、DatagramSocket和DatagramPacket
DAY13.1 Java核心基础 TCP协议 TCP 协议是面向连接的运算层协议,比较复杂,应用程序在使用TCP协议之前必须建立连接,才能传输数据,数据传输完毕之后需要释放连接 就好比现实生活中的打电话,首先确保电话打通了才能进…...
配置VMware Workstation中Ubuntu虚拟机与Windows主机的剪贴板共享功能
步骤1:安装或更新VMware Tools组件 卸载旧版本工具(可选) 若已安装旧版工具,建议先卸载: sudo apt-get autoremove open-vm-tools安装必需组件 sudo apt-get updatesudo apt-get install open-vm-tools o…...
深入理解Python闭包与递归:原理、应用与实践
目录 闭包 什么是闭包: 闭包的基本结构: 实现闭包的条件: 1.嵌套函数 2.内函数引用外部函数的变量 3.外部函数返回内部函数 4.外部函数已经执行完毕 递归函数 什么是递归函数: 递归函数条件 1.必须有个明确的结束条…...
第7章:Docker容器网络模型深度剖析
第7章:Docker容器网络模型深度剖析 作者:DogDog_Shuai 阅读时间:约30分钟 难度:高级 目录 1. 引言2. Docker网络架构3. Docker网络模式详解4. Docker网络配置5. Docker网络故障排查6. 总结1. 引言 Do...
SeaCMS代码审计
漏洞描述 漏洞分析 根据漏洞描述定位漏洞代码 当actionsaveCus或者save时,可以进行一个文件写入,不过文件类型被进行了限制,只有html,htm,js,txt,css 虽然这里并不能写入php文件,但是当actionadd或者custom时,这里进行…...
好看的网络安全登录页面 vue http网络安全
一、http协议 http协议是一种网络传输协议,规定了浏览器和服务器之间的通信方式。位于网络模型中的应用层。(盗图小灰。ヾ(◍∇◍)ノ゙) 但是,它的信息传输全部是以明文方式,不够安全,…...
springmvc中如何自定义入参注解并自动注入值
在Spring中,HandlerMethodArgumentResolver 是一个非常强大的接口,用于自定义控制器方法参数的解析逻辑。以下是一个完整的示例,展示如何使用 HandlerMethodArgumentResolver 并结合自定义注解来实现特定的参数解析逻辑。 ### **1. 定义自定…...
