现代软件开发过程中要实现高效的团队协作,需要使用代码分支管理工具实现代码的共享、追溯、回滚及维护等功能。目前流行的代码管理工具,包括CVS,SVN,Git,Mercurial等。相比CVS和SVN的集中管理,Git具有非常明显的优势。
Git的优势
- Git 可以在本地进行提交以支持离线工作;
- Git 可以在本地创建分支并且没有命名空间冲突的问题;
- Git 可以让提交通过 Pull Request 的方式进行,不需要所有的开发者都有主仓库的写权限;
- Git 在优化性能时选择了合并分支作为主要的性能衡量指标,将合并分支变成了成本非常低的操作以鼓励分支的使用;
- Git 通过 SHA-1 哈希来保证仓库中数据的可靠性,通过 SHA-1 就可以对数据进行校验,抵御了来自攻击者的恶意篡改;
版本管理的挑战
大家工作在同一个仓库上,那么彼此的代码协作必然带来很多问题和挑战,如下:
- 如何开始一个Feature的开发,而不影响别的Feature?
- 由于很容易创建新分支,分支多了如何管理,时间久了,如何知道每个分支是干什么的?
- 哪些分支已经合并回了主干?
- 如何进行Release的管理?开始一个Release的时候如何冻结Feature, 如何在Prepare Release的时候,开发人员可以继续开发新的功能?
- 线上代码出Bug了,如何快速修复?而且修复的代码要包含到开发人员的分支以及下一个Release?
关于工作流
什么是工作流
工作流(workflow),简单点就是工作的流程,复杂点则有很多层面的定义:
- 工作流是对工作流程及其各操作步骤之间业务规则的抽象、概括描述;
- 工作流是指一类能够完全自动执行的经营过程,根据一系列过程规则,将文档、信息或任务在不同的执行者之间进行传递与执行。
为什么工作流很重要
因为影响效率,尤其对于团队协作。
协作必须有一个规范的工作流程,使得项目井井有条地发展下去。好的团队协作“工作流”应该像水流一样顺畅自然地向前流动,在不断汇集各支流的过程中不发生冲击对撞,持续保持主干方向的正确,最终奔向大海。
理想中的行云流水往往只在完全自动化的工作流中发生,但只要有“人”的参与,现实则会相当骨感。因为只要“流”动必然就有信息的传递,而人之间的信息传递容易带来“冲击对撞”。
以下就是一个典型的“冲击对撞”:
【预设的工作流】妻子要做午饭,叮嘱丈夫:“去买2个鸡蛋,如果有西红柿,则买1个。”
【团队执行】丈夫出门了,回来时买了1个鸡蛋。
【无实际产出的冲击对撞】妻子:“怎么只有1个鸡蛋”
【无实际产出的冲击对撞】丈夫:“你说有西红柿则买1个,所以我买了1个鸡蛋。”
【无实际产出的冲击对撞】妻子:“...我的意思是去买2个鸡蛋,如果有西红柿,则在买2个鸡蛋的基础上再买1个西红柿”
【无实际产出的冲击对撞】丈夫:“...”
影响效率的一面是丈夫需要再去买一次,更糟的是事儿没办成说不定还得吵一架。
在你开始阅读之前,请记住:这些Git工作流程应被视作为指导方针,而非“铁律”。我们只是想告诉你可能的做法。因此,如果有必要的话,你可以组合使用不同的流程 。
常见的三种分支策略:GitFlow、GitHubFlow和GitLabFlow
这三种工作流程,有一个共同点:都采用”功能驱动式开发”(Feature-driven development,简称FDD)。
它指的是,需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch)。完成开发后,该分支就合并到主分支,然后被删除。
GitFlow
Git Flow 是一种基于 Git 的分支管理策略,旨在优化软件开发的效率和质量。该策略最初由 Vincent Driessen 在 2010 年提出,并得到了广泛的应用和推广。
特点
它最主要的特点有两个。
项目存在两个长期分支:
- 主分支master 【用于版本发布,主分支的每个版本都是质量稳定和功能齐全的发布版。】
- 开发分支develop【用于日常开发工作,存放最新的开发版代码。当开发分支的代码达到稳定状态并可以发布版本时,代码需要被合并到 master 分支,然后标记上对应的版本标签(tag)。】
Master分支上存放的是最稳定的正式版本,并且该分支的代码应该是随时可在生产环境中使用的代码。任何时候在这个分支获取到的都是稳定的已发布的版本。当一个版本开发完毕后,产生了一份新的稳定的可供发布的代码时,主要分支上的代码要被更新。同时,每一次更新,都需要在主要分支上打上对应的版本号。上图里的v0.1和v0.2就是tag。
任何人不允许在主要分支上进行代码的直接提交,只接受其他分支的合入。原则上主要分支上的代码必须是合并自经过多轮测试及已经发布一段时间且线上稳定的预发分支。
Develop分支是主开发分支,其上更新的代码始终反映着下一个发布版本需要交付的新功能。当开发分支到达一个稳定的点并准备好发布时,应该从该点拉取一个预发分支并附上发布版本号。也有人称开发分支为集成分支,因为会基于该分支和持续集成工具做自动化的构建。
开发分支接受其他辅助分支的合入,最常见的就是功能分支,开发一个新功能时拉取新的功能分支,开发完成后再并入开发分支。需要注意的是,合入开发的分支必须保证功能完整,不影响开发分支的正常运行。
如果需要开发新的功能或者解决代码中的问题,则创建辅助分支来解决问题,辅助分支常用于:
其次,项目存在三种短期分支。
- 功能分支(feature branch)
- 补丁分支(hotfix branch)
- 预发分支(release branch)
一旦完成开发,它们就会被合并进develop或master,将被删除。
Feature分支目的是开发新模块或新功能以满足客户需求,从develop分支创建,开发结束后只需要合并回develop分支,并不需要合并回master主分支。
Feature分支起源于develop分支,最终也会归于develop分支。
功能分支一般命名为 Feature/xxx,用于开发即将发布版本或未来版本的新功能或者探索新功能。该分支通常存在于开发人员的本地代码库而不要求提交到远程代码库上,除非几个人合作在同一个功能分支开发。
Feature最终的结局必然只有两个,其一是合并入“develop”分支,其二是被抛弃。
简而言之,就是每一个特性(feature)的开发并不直接在主干上开发,而是在分支上开发,分支开发完毕后再合并到主干上。
这样做的好处是:
- 还处于半成品状态的feature不会影响到主干
- 各个开发人员之间做自己的分支,互不干扰
- 主干永远处于可编译、可运行的状态
功能分支要求足够细粒度以避免成为长期存在的功能分支,应当小步合并而不是一次合并大量代码。
Release分支
是用于准备发布的版本分支,从develop分支创建,预发分支一般命名为 Release/1.2(后面是版本号),创建时已经包含了发布所需要的所有功能,所以在这个分支上不再开发新功能,仅对这个预发布版本进行修复问题,(记住:一旦打了Release分支之后不要从Develop分支上合并新的改动到Release分支)创建文档及其他与发布相关的工作,一切就绪后将Release分支合并回master主分支并打上相应的版本号标签(Tag),同时也合并回develop分支。
Release分支通常负责“短期的发布前准备工作”、“小bug的修复工作”、“版本号等元信息的准备工作”。与此同时,develop分支又可以承接下一个新功能的开发工作了。
Release分支产生新提交的最好时机是develop分支已经基本到达预期的状态,在一段短时间内,在Release分支上,我们可以继续修复bug。在此阶段,严禁新功能的并入,新功能应该是被合并到develop分支的。
Release分支,起源于develop分支,最终归于develop或master分支。
预发分支需要提交到服务器上,交由测试工程师进行测试,并由开发工程师修复Bug。同时根据该分支的特性我们可以部署自动化测试以及生产环境代码的自动化更新和部署。
经过若干bug修复后,Release分支上的代码已经达到可发布状态,此时,需要完成三个动作:第一是将Release分支合并到master分支,第二是一定要为master上的这个新提交打TAG(记录里程碑),第三是要将Release分支合并回develop分支。
我们用这个分支干所有和发布有关的事情,比如:
- 把这个分支打包给测试人员测试
- 在这个分支里修复bug
- 编写发布文档
所以,在这个分支里面绝对不会添加新的特性。
单独搞一个release分支的好处是,当一个团队在做发布相关的工作时,另一个团队则可以接着开发下一版本的东西。
Hotfix分支
通常用于紧急修复当前发布的版本中出现的严重问题,从发布版本的标签或master主分支创建,热修复分支一般命名为Hotfix/1.2.1(后面是版本号),问题修复后合并回master主分支并打上新的版本号标签(Tag),同时也合并回develop分支或者正在进行中的Release分支。创建单独的Hotfix分支可以避免打断正在进行中的各项开发工作,客户也不需要等到下一个发布周期才能拿到修复。
Hotfix分支源于master,归于develop或master。
创建Hotfix分支的好处是不会打断正在进行的开发分支的开发工作,能够让团队中负责功能开发的人与负责代码修复的人并行、独立的开展工作。
评价
优点:
- Git flow的优点在于流程清晰,分支管理严格,适用于发布周期比较长的“版本发布”,发布周期可能是几周,几个月,甚至更长时间。由于保持两个长期分支同步的开销较大,所以Git flow并不适用于快速的“持续发布”。
- 功能相互独立,在每个发布的新版本中可以挑选想要发布的功能,同时可以支持我们持续发布新的功能。
缺点:
- 相对复杂,需要同时维护两个长期分支。在实际应用中,很多开发者会忘记合并回 develop 或者 master,并且各辅助分支之间互相独立,如果从master上pull代码不够及时,一方面可能造成某个分支长期使用已经过时或者错误的代码,另一方面如果与master相隔较远,合并分支时可能会有大量代码冲突,往往需要花费很多时间来消除代码冲突,并且非常容易出错,影响项目的持续集成。
- 更大问题在于,这个模式是基于”版本发布”的,目标是一段时间以后产出一个新版本。但是,很多网站项目是”持续发布”,代码一有变动,就部署一次。这时,master分支和develop分支的差别不大,没必要维护两个长期分支。
GitHubFlow
GitHub flow是由Scott Chacon于2011年提出的代码分支管理模型,这是GitHub官方推荐的开发流程,以快速部署为目标,目前大部分开源项目都遵循这一流程。
特点
Github flow最大的特点是只有一个长期分支,即主分支master,且主分支始终保持可发布状态。
从master上创建新分支进行功能开发、问题修复等,这些分支通过pull request将代码合并到master。为了保证主分支的代码质量,master的权限只开放给一部分人。Pull request是请求别人pull你的代码库(repository),也就是把开发分支的代码经过代码评审并通过测试后,让有权限的管理员合并回master。
不过在实际情况中,代码评审不可能检查出提交的代码中的所有问题,所以对于每次提交的代码进行自动化测试,主分支代码的自动化部署尤其重要,自动化测试能在产品部署前及时发现一部分问题,如果产品部署之后发现严重问题,自动化部署可以在最短时间内把产品回滚到上一个版本。
官方推荐的流程如下:
其中的主要流程为:
- 新建分支(Create a branch);
- 提交修改(Add commits);
- 创建PR(Open a Pull Request);
- 代码评审(Discuss and review your code);
- 部署(Deploy);
- 合并(Merge);
GitHub flow 最大的亮点在于部署(Deploy)发生在合并(Merge)之前,这就是 GitHub flow 的核心,非阻塞式集成 —— 在产生任何副作用之前得知当前修改的所有集成效果,达到真正的持续集成。
操作步骤
Step 1: 创建分支(Create a branch)ch)
当你操作一个项目的时候,无论其他协作者做什么,你都可以在特定的分支上实现自己的想法。也就是说,分支的存在是帮助你管理这些工作流。
在你创建了一个项目的分支的时候,你也就创建了一个可以尝试你的新想法的环境。在分支上做的更改不会影响master分支,所以你可以自由地进行实验和提交更改,这些操作都是安全的。当然,只有你完成的代码被协作人审阅并通过的时候,才可以被合并。
Step 2:增加提交(Add commit)
只要你创建了分支,就说明你要对它进行修改啦!无论添加、修改、还是删除文件,你都必须进行提交,将它们同步到你的分支上。当你在分支上工作的时候,这些提交操作可以跟踪你的工作进度。
提交操作也建立一个关于你工作的历史,通过查看这些提交记录,其他人可以知道你做了什么和为什么这么做。每个提交操作都有一个相关的提交信息(Commit messages),用于描述你做出的修改。此外, 每一个提交操作都被视为一个“修改单元”。如果发现了 bug 或者决定走不同的开发方向,你也可以通过这些“修改单元”进行回滚操作。
Step 3:提出pull请求(Open a pull request)
Pull 请求开启了一个关于你的提交内容的讨论。因为他们与底层 Git 仓库紧密集成,所以如果他们接受你的请求,任何人都可以准确地看到合并的变化。
Pull 请求对于促进开源项目和管理共享库的更改非常有用。如果你使用Fork & Pull Model,Pull 请求提供一种方式来通知工程维护人员关于你希望他们考虑的变化。如果使用Shared Repository Model,则在将它们合并到主分支前,Pull 请求帮助启动代码审查和有关更改建议的会话。
Step 4:讨论和评估你的代码(Discuss and review your code)
当你提出 Pull 请求的时候,审查你的更改内容的人或团队可能有一些问题或者意见。也许你的编码风格与项目规范不符,或者缺少单元测试,也有可能所有的东西看起来都很棒,条理清晰。Pull 请求的目的就是鼓励和捕捉这种类型的对话。
你也可以在大家讨论和给出关于你提交内容的反馈时,继续 Push 你的分支。如果有人评论说你什么没有做,或者代码中有 bug,你也可以及时把它修复,然后 Push 这些修改。GitHub 将会给你展示出最新评论和反馈,你也可以在 Pull 请求的视图中统一接收这些消息。
Step 5. 部署(Deploy)
只要你的 Pull 请求被审查并且通过了你的测试,你就可以部署这些修改,在生产环境中验证她们。如果分支发生了问题,你也可以回滚到之前的状态。
Step 6. 合并(Merge)
现在, 你修改的内容已经在生产环境中验证了,是时候将你的代码合并到master分支啦!合并之后,Pull 请求就保存了一份关于你修改代码的历史记录。因为它们是可搜索的,所有任何人都可以通过搜索了解你为什么这么修改以及如何修改的。
如何开始使用 GitHub flow?
使用 GitHub flow 的基本要求有:
- 具备一个代码版本控制环境;
- 具备一个持续集成环境;
- (可选)具备 CI 环境的管理员权限;
- 能够创建一个有权限访问 VCS 平台的机器人帐号;
- 能够自由使用 VCS 平台的 WebHook API;
- 能够自由使用 CI 平台的 Trigger API;
- (可选)能够自由使用 CI 平台的状态查询 API;
- 能够创建一个高可用的内部服务器用于机器人帐号的运行;
- 能够决定开发团队的工作流程;
- 能够投入成本改善基础设施;
评价
优点:
- GitHub flow 的核心优势在于其流程带来的自动化可能性,能够做到其它流程无法实现的检查过程,并极大简化开发团队的体力劳动,真正发挥自身的价值。
- Github flow的优点在于流程简单灵活,不需要考虑及管理太多的分支,适用于需要快速集成及“持续发布”的项目,这类项目可能需要每天发布一个版本,甚至一天发布多个版本。但是对于应用场景比较复杂的情况,例如:多个环境下的产品部署,多个版本的发布或问题修复,只有一个master便会显得力不从心。
缺点:
问题在于它的假设:master分支的更新与产品的发布是一致的。也就是说,master分支的最新代码,默认就是当前的线上代码。
可是,有些时候并非如此,代码合并进入master分支,并不代表它就能立刻发布。比如,苹果商店的APP提交审核以后,等一段时间才能上架。这时,如果还有新的代码提交,master分支就会与刚发布的版本不一致。另一个例子是,有些公司有发布窗口,只有指定时间才能发布,这也会导致线上版本落后于master分支。
上面这种情况,只有master一个主分支就不够用了。通常,你不得不在master分支以外,另外新建一个production分支跟踪线上版本。
github flow这种方式,要保证高质量,对于贡献者的素质要求很高,换句话说,如果代码贡献者素质不那么高,质量就无法得到保证。
GitLabFlow
GitLab flow是由GitLab 的 CEO Sytse Sijbrandij 于 2014 年正式发布的代码分支管理模型,属于GitHub flow的演进版本。GitLab Flow版本发布工作流适用于有明确的版本规划、并且需要支持多个版本的项目。
上游优先
Gitlab flow 的最大原则叫做"上游优先"(upsteam first),即只存在一个主分支master,它是所有其他分支的"上游"。只有上游分支采纳的代码变化,才能应用到其他分支。只有紧急情况,才允许跳过上游,直接合并到下游分支。
与GitHub不同之处是GitLab flow考虑多环境部署等现实因素,增加production产品分支用于在不同的环境下部署产品,或者从master的稳定版本创建release发布分支用于发布软件。
如果在产品分支或者发布分支发现问题,就从对应版本分支创建修复分支,修复完成之后,GitLab flow遵循 “上游优先” 的合并策略,先合并到 master,再合并到下游的production或release分支。
和Github flow类似,master的修改权限只开放给部分人,开发分支的工作完成后,代码通过merge request(类似于GitHub flow中的pull request)请求有权限的管理员把代码合并(merge)回主分支
Gitlab flow 分成两种情况,适应不同的开发流程:
持续发布
对于”持续发布”的项目,它建议在master分支以外,再建立不同的环境分支。比如,”开发环境”的分支是master,”预发环境”的分支是pre-production,”生产环境”的分支是production。
- 开发分支 master 用于发布到测试环境,该分支为受保护的分支
- 预发分支 pre-production用于发布到预发环境,上游分支为 master
- 正式分支 production用于发布到正式环境,上游分支为 pre-production
只有紧急情况,才允许跳过上游,直接合并到下游分支。
版本发布
对于”版本发布”的项目,建议的做法是每一个稳定版本,都要从master分支拉出一个分支,比如2-3-stable、2-4-stable等等。
以后,只有修补bug,才允许将代码合并到这些分支,并且此时要更新小版本号。
功能分支工作流步骤
- feature分支(依据issues产生)上编写代码、提交、推送到该远程仓库;
- 发起feature分支到master分支的合并请求;
- 开发经理或代码评审委员会成员对合并的代码进行评审;
- 提出评审建议,通过或不通过?通过继续下一步流程;不通过返回feature分支进行修改重复以上流程(此时之前的合并请求如果没有关闭,不用重新提交合并申请)。
- feature分支代码合并到master分支;
- master自动测试或手动测试;
- 产生issues 和 bug featrue
- 重复第一步
- 通过后,创建从master分支到pre-production分支的合并请求;
- 创建从pre-production分支到production分支的合并请求来上线发布。
另外3种分支策略
TBD flow
TBD (Trunk-based Development) flow是由Paul Hammant于2013年提出的模型。
TBD flow最大的特点是所有开发都基于主干trunk,不再有长期的开发分支,要求所有的提交尽快回到主干,这样可以共享最新的代码,并且能尽可能地减少合并冲突。如果需要发布版本,可以基于trunk直接生成新的分支用于发布。
TBD flow没有pull或者push request,要求开发人员尽快把代码提交到主干分支,但是TBD flow缺乏严格的流程来保证每一份提交代码的质量,如果一些项目开发人员众多且水平不一,同时工作在主分支上可能会在产品发布时才发现不可预知的问题,
所以TBD flow更适用于需要快速迭代的产品,如果在主干分支上发现问题,可以快速进行修复再合并回主干分支。
TBD++ flow
TBD++ flow是Arrus公司软件研发部门从2015年开始一直沿用的Git分支管理模型,产品项目的客户主要是电信级服务运营商,每个国家或地区的需求在主要功能上一致,但在客户定制化方面又存在不少差异,同时项目开发周期较长,整个周期一般在3个月到2年之间,软件产品在项目前期需要有快速的迭代,在项目后期需要有稳定的发布版本。
TBD++ flow以敏捷开发为核心,同时吸收了以上几个主流模型的优点,主要特点如下:
1. 基于功能的主分支
只存在一个长期的独立分支,即主分支master,主分支上功能齐全,通过自动测试保证基本功能运行正常,因为自动测试覆盖不全面或者手动测试不及时,所以无法保证主分支的每个版本都是质量稳定的发布版,但是可以根据功能的完成程度直接从主分支上创建迭代版本用于针对不同客户或者不同时期的功能演示。基于master开发功能或者修复问题需要用到以下两个辅助分支:
- Feature分支:为了开发新模块或新功能以满足客户需求,从主分支上创建Feature分支,但是并不要求Feature分支上所有的提交尽快回到主分支,开发完成并且通过代码评审及功能测试后才能合并回master主分支。为了共用主分支上的最新代码以及避免合并代码时解决代码冲突引起的额外开销,在功能开发过程中Feature分支需要始终与master保持同步。
- Bugfix分支:基于主分支创建Bugfix分支修复主分支上发现的问题,修复完成并且通过代码评审后代码合并回master主分支。
基于主分支的Feature分支和Bugfix分支完成后,开发者直接将代码合并回主分支master,不需要像GitLab或GitHub那样依赖于少数几个有权限的管理员,这是因为如果一个项目中开发人员比较多的话,管理员没办法做到对每部分代码都熟悉或掌握,所以代码质量交由代码评审和功能测试来掌控,合并代码回主分支的操作由开发者自己完成。
主分支上的新代码有时候可能因为评审或测试不全面而带来新问题,例如破坏其他功能模块,甚至影响整体功能。为了能尽早发现问题,主分支上的每次提交都会触发系统级自动化测试,并且周期性地对主分支进行人工测试。一旦发现问题,主分支的专职配置管理员(Software Configuration Manager,SCM)将根据问题的严重性和紧迫性决定是否需要直接回退引起问题的提交,或者基于master创建bugfix分支进行问题修复。
2. 基于发布的Release分支
Release分支负责对外发布软件产品,每个Release分支也会配备专职版本配置管理员SCM,SCM具有对Release分支的最高管理权限。当master上已经包含了某个发布所需要的所有功能,并且没有已知的严重问题,此时由SCM从主分支上创建Release分支准备系统集成测试,和Git flow相同,在此分支上不再进行新功能的开发,仅在这个分支上进行修复问题,创建文档,客户参数配置及其他与发布相关的工作,这些代码同时也需要合并回master以确保主分支功能的完整性。
在每个Release分支正式发布前可能还需要将主分支上的一部分关键问题的修复选择性地同步(Cherry-pick)到Release分支,这个操作也是由SCM完成。Release分支上的工作一切就绪并通过系统集成测试后,SCM在Release分支上打上相应的版本号标签(Tag)进行发布,这点和Git flow在主分支上进行发布不同。
软件产品发布之后,如果发现紧急或者严重的问题,此时需要基于版本发布时的Release分支标签创建Hotfix分支来修复此类问题,问题修复后合并回该Release分支以及其他同样需要此修复的Release分支,并打上新的版本号标签(Tag)用于发布,同时代码也需要合并回master以确保主分支的健壮性。
Forking Workflow
Forking Workflow与以上讨论的工作流很不同,一个很重要的区别就是它不只是多个开发共享一个远程仓库(central repository),而是每个开发者都拥有一个独立的服务端仓库。也就是说每个contributor都有两个仓库:本地私有的仓库和远程共享的仓库。
Forking Workflow这种工作流主要好处就是每个开发者都拥有自己的远程仓库,可以将提交的commits推送到自己的远程仓库,但只有工程维护者才有权限push提交的commits到官方的仓库,其他开发者在没有授权的情况下不能push。能为大型、自发性的团队(包括了不受信的第三方)提供灵活的方式来安全的协作。 也让这个工作流成为开源项目的理想工作流。Github很多开源项目都是采用Forking Workflow工作流。
流程步骤:
- 在官方公共的仓库开发者fork官方仓库来创建它的拷贝
- 当开发者准备好发布本地的commit时,他们push commit到他们自己的公共仓库
- 在自己的公共仓库发送一个pull request到官方仓库
- 维护者pull贡献者的commit到他自己的本地仓库
- 审查代码确保它不会破坏工程,合并它到本地仓库的master分支
- push master分支到服务器上的官方仓库
- 其他开发者应该同步官方仓库。
选择合适的分支模型
首先是项目的版本发布周期。如果发布周期较长,则 Git-Flow 是最好的选择。Git-Flow 可以很好地解决新功能开发、版本发布、生产系统维护等问题;如果发布周期较短,则 TBD 和 GitHub-Flow 都是不错的选择。GitHub-flow 的特色在于集成了 pull request 和代码审查。如果项目已经使用 GitHub,则 GitHub-Flow 是最佳的选择。GitHub-Flow 和 TBD 对持续集成和自动化测试等基础设施有比较高的要求。如果相关的基础设施不完善,则不建议使用。
参考资料:
A successful Git branching model
Understanding the Git Workflow
What are GitLab Flow best practices?
文章地址:www.yuque.com/fengjutian/… 《Git工作流介绍》