Git详解之⼗分⽀管理最佳实践
是⽬前最流⾏的源代码管理⼯具。⼤量的软件项⽬由 GitHub、Bitbucket 和 GitLab 这样的云服务平台或是私有的仓库来管理。在使⽤ Git 时通常会遇到的⼀个问题是采⽤何种分⽀管理实践,即如何管理仓库中作⽤不同的各类分⽀。和软件开发中的其他实践⼀样,Git 分⽀管理并没有普遍适⽤的最佳做法,⽽只有对每个团队和项⽬⽽⾔最适合的做法。简单来说,在项⽬开发中使⽤多个分⽀会带来额外的管理和维护开销,但是多个分⽀对于项⽬的团队合作、新功能开发和发布管理都是有⼀定好处的。不同的团队可以根据团队⼈员组成和意愿、项⽬的发布周期等因素选择最适合的策略,到最适合团队的管理⽅式。本⽂将介绍三种常见的 Git 分⽀管理⽅式。
单主⼲
单主⼲的分⽀实践(Trunk-based development,TBD)在 SVN 中⽐较流⾏。和都使⽤这种⽅式。trunk 是 SVN 中主⼲分⽀的名称,对应到 Git 中则是 master 分⽀。TBD 的特点是所有团队成员都在单个主⼲分⽀上进⾏开发。当需要发布时,先考虑使⽤标签(tag),即 tag 某个 commit 来作为发布的版本。如果仅靠 tag 不能满⾜要求,则从主⼲分⽀创建发布分⽀。bug 修复在主⼲分⽀中进⾏,再 cherry-pick 到发布分⽀。图 1 是 TBD 中分⽀流程的⽰意图。
图 1. TBD 中的分⽀流程的⽰意图
图 1. TBD 中的分⽀流程的⽰意图
由于所有开发⼈员都在同⼀个分⽀上⼯作,团队需要合理的分⼯和充分的沟通来保证不同开发⼈员的代码尽可能少的发⽣冲突。持续集成和⾃动化是必要的,⽤来及时发现主⼲分⽀中的 bug。因为主⼲分⽀是所有开发⼈员公⽤的,⼀个开发⼈员引⼊的 bug 可能对其他很多⼈造成影响。不过好处是由于分⽀所带来的额外开销⾮常⼩。开发⼈员不需要频繁在不同的分⽀之间切换。
GitHub flow
是 GitHub 所使⽤的⼀种简单的流程。该流程只使⽤两类分⽀,并依托于 GitHub 的 pull request 功能。在 GitHub flow 中,master 分⽀中包含稳定的代码。该分⽀已经或即将被部署到⽣产环境。master 分⽀的作⽤是提供⼀个稳定可靠的代码基础。任何开发⼈员都不允许把未测试或未审查的代码直接提交到 master 分⽀。
对代码的任何修改,包括 bug 修复、hotfix、新功能开发等都在单独的分⽀中进⾏。不管是⼀⾏代码的⼩改动,还是需要⼏个星期开发的新功能,都采⽤同样的⽅式来管理。当需要进⾏修改时,从 master 分⽀创建⼀个新的分⽀。新分⽀的名称应该简单清晰地描述该分⽀的作⽤。所有相关的代码修改都在新分⽀中进⾏。开发⼈员可以⾃由地提交代码和 push 到远程仓库。
当新分⽀中的代码全部完成之后,通过 GitHub 提交⼀个新的 pull request。团队中的其他⼈员会对代码进⾏审查,提出相关的修改意见。由持续集成服务器(如 Jenkins)对新分⽀进⾏⾃动化测试。当代码通过⾃动化测试和代码审查之后,该分⽀的代码被合并到 master 分⽀。再从 master 分⽀部署到⽣产环境。图 2 是 GitHub flow 分⽀流程的⽰意图。
图 2. Github flow 中的分⽀流程的⽰意图
图 2. Github flow 中的分⽀流程的⽰意图
GitHub flow 的好处在于⾮常简单实⽤。开发⼈员需要注意的事项⾮常少,很容易形成习惯。当需要进⾏任何修改时,总是从 master 分⽀创建新分⽀。完成之后通过 pull request 和相关的代码审查来合并回 master 分⽀。GitHub flow 要求项⽬有完善的⾃动化测试、持续集成和部署等相关的基础设施。每个新分⽀都需要测试和部署,如果这些不能⾃动化进⾏,会增加开发⼈员的⼯作量,导致⽆法有效地实施该流程。这种分⽀实践也要求团队有代码审查的相应流程。
git-flow
应该是⽬前流传最⼴的 Git 分⽀管理实践。git-flow 围绕的核⼼概念是版本发布(release)。因此 git-flow 适⽤于有较长版本发布周期的项⽬。虽然⽬前推崇的做法是持续集成和随时发布。有的项⽬甚⾄可
以⼀天发布很多次。随时发布对于 SaaS 服务类的项⽬来说是很适合的。不过仍然有很⼤数量的项⽬的发布周期是⼏个星期甚⾄⼏个⽉。较长的发布周期可能是由于⾮技术相关的因素造成的,⽐如⼈员限制、管理层决策和市场营销策略等。
git-flow 流程中包含 5 类分⽀,分别是 master、develop、新功能分⽀(feature)、发布分⽀(release)和 hotfix。这些分⽀的作⽤和⽣命周期各不相同。master 分⽀中包含的是可以部署到⽣产环境中的代码,这⼀点和 GitHub flow 是相同的。develop 分⽀中包含的是下个版本需要发布的内容。从某种意义上来说,develop 是⼀个进⾏代码集成的分⽀。当 develop 分⽀集成了⾜够的新功能和 bug 修复代码之后,通过⼀个发布流程来完成新版本的发布。发布完成之后,develop 分⽀的代码会被合并到 master 分⽀中。
其余三类分⽀的描述如所⽰。这三类分⽀只在需要时从 develop 或 master 分⽀创建。在完成之后合并到 develop 或 master 分⽀。合并完成之后该分⽀被删除。这⼏类分⽀的名称应该遵循⼀定的命名规范,以⽅便开发⼈员识别。
表 1. git-flow 分⽀类型
分⽀类型命名规范创建⾃合并到说明
feature feature/*develop develop新功能
release release/*develop develop 和 master⼀次新版本的发布
hotfix hotfix/*master develop 和 master⽣产环境中发现的紧急 bug 的修复
对于开发过程中的不同任务,需要在对应的分⽀上进⾏⼯作并正确地进⾏合并。每个任务开始前需要按照指定的步骤完成分⽀的创建。例如当需要开发⼀个新的功能时,基本的流程如下:
从 develop 分⽀创建⼀个新的 feature 分⽀,如 feature/my-awesome-feature。
在该 feature 分⽀上进⾏开发,提交代码,push 到远端仓库。
当代码完成之后,合并到 develop 分⽀并删除当前 feature 分⽀。
在进⾏版本发布和 hotfix 时也有类似的流程。当需要发布新版本时,采⽤的是如下的流程:
从 develop 分⽀创建⼀个新的 release 分⽀,如 release/1.4。
把 release 分⽀部署到持续集成服务器上进⾏测试。测试包括⾃动化集成测试和⼿动的⽤户接受测试。
对于测试中发现的问题,直接在 release 分⽀上提交修改。完成修改之后再次部署和测试。
当 release 分⽀中的代码通过测试之后,把 release 分⽀合并到 develop 和 master 分⽀,并在 master 分⽀上添加相应的 tag。
因为 git-flow 相关的流程⽐较繁琐和难以记忆,在实践中⼀般使⽤来完成相关的⼯作。⽐如同样的开发新功能的任务,可以使⽤ git flow feature start my-awesome-feature 来完成新分⽀的创建,使⽤ git flow feature finish my-awesome-feature 来结束 feature 分⽀。辅助脚本会完成正确的分⽀创建、切换和合并等⼯作。
Maven JGit-Flow
对于使⽤ Apache Maven 的项⽬来说,Atlassian 的是⼀个更好的 git-flow 实现。JGit-Flow 是⼀个基于的纯实现的 git-flow,并不需要安装额外的脚本,只需要作为 Maven 的插件添加到 Maven 项⽬中即可。JGit-Flow 同时可以替代来进⾏发布管理。JGit-Flow 会负责正确的设置不同分⽀中的 Maven 项⽬的 POM ⽂件中的版本,这对于 Maven 项⽬的构建和发布是很重要的。
在 Maven 项⽬的 l ⽂件中添加中的插件声明就可以使⽤ JGit-Flow。<configuration>中包含的是 JGit-Flow 不同任务的配置。
清单 1. JGit-Flow 的 Maven 设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18< build >
< plugins >
< plugin >
< groupId >external.atlassian.jgitflow</ groupId >
< artifactId >jgitflow-maven-plugin</ artifactId >
< version >1.0-m5.1</ version >
< configuration >
< flowInitContext >
< versionTagPrefix >release-</ versionTagPrefix >
</ flowInitContext >
< releaseBranchVersionSuffix >RC</ releaseBranchVersionSuffix >                < noDeploy >true</ noDeploy >
< allowSnapshots >true</ allowSnapshots >
< allowUntracked >true</ allowUntracked >
</ configuration >
</ plugin >
</ plugins >
</ build >
JGit-Flow 提供了很多配置选项,可以在 POM ⽂件中声明。这些配置项可以对不同的任务⽣效。常⽤的配置项如所⽰。表 2. JGit-Flow 的配置项
名称描述适⽤任务flowInitContext配置不同类型的分⽀的名称全局
allowSnapshots是否允许存在 SNAPSHOT 类型的依赖
allowUntracked是否允许本地 Git 中存在未提交的内容
scmCommentPrefix 和scmCommentSuffix JGit-Flow 会进⾏代码合并⼯作。通过这两个配置项来设置 JGit-Flow 进⾏代码
提交时的消息的前缀和后缀
全局
username 和 password进⾏ Git 认证时的⽤户名和密码,适⽤于 HTTPS 仓库全局releaseBranchVersionSuffix release 分⽀中项⽬的 POM ⽂件版本号的后缀
developmentVersion下⼀个开发版本的版本号
releaseVersion发布版本的版本号
pushReleases是否把 release 分⽀ push 到远程仓库
goals当部署发布版本到 Maven 仓库时执⾏的⽬标release-finish,hotfix-finish keepBranch当发布完成之后是否保留相应的分⽀release-finish,hotfix-finish noDeploy是否启⽤部署到 Maven 仓库的功能release-finish,hotfix-finish noReleaseBuild在完成发布时是否禁⽤ Maven 构建release-finish noReleaseMerge在完成发布时是否把 release 分⽀合并回 develop 和 master 分⽀release-finish
noTag在完成发布时是否添加标签release-finish,hotfix-finish squash在进⾏分⽀合并时,是否把多个 commit 合并成⼀个release-finish,hotfix-finish
featureName新特性分⽀的名称feature-start,feature-finish,feature-deploy
pushFeatures是否把特性分⽀ push 到远程仓库feature-start,feature-finish noFeatureBuild在完成特性时是否禁⽤ Maven 构建feature-finish
noFeatureMerge在完成特性时是否把特性分⽀合并回 develop feature-finish
pushHotfixes在完成 hotfix 时是否把分⽀合并回 master hotfix-finish
noHotfixBuild在完成 hotfix 时是否禁⽤ Maven 构建hotfix-finish
名称描述适⽤任务
其余的配置项可以参考插件不同任务的。
在启⽤了 JGit-Flow 之后,可以通过 mvn 运⾏ jgitflow:feature-start 和 jgitflow:feature-finish 来开始和结束新特性的开发。与版本发布和hotfix 相关的命令分别是 jgitflow:release-start 和 jgitflow:release-finish 以及 jgitflow:hotfix-start 和 jgitflow:hotfix-finish。在运⾏命令之
后,JGit-Flow 会完成相关的 Git 分⽀创建、合并、删除、添加 tag 等操作,不需要开发⼈员⼿动完成。
每个分⽀的 l 中的版本号的格式并不相同。如 master 分⽀的版本号是标准的发布版本号,如 1.2.3;develop 分⽀中则是SNAPSHOT 版本,⽐ master 分⽀的版本号要⾼,如 1.2.4-SNAPSHOT;release 分⽀可以通过<releaseBranchVersionSuffix>配置来指定版本号的后缀,如 1.2.3-RC-SNAPSHOT;feature 分⽀可以通过<enableFeatureVersions>配置来把 feature 分⽀名称作为后缀添加到版本号中,如 1.2.3-my-awesome-feature-SNAPSHOT;hotfix 分⽀的版本号基于 master 分⽀的版本号,如 1.2.3.1 是对于 1.2.3 版本的第⼀个hotfix。当使⽤ jgitflow:release-finish 完成⼀个 release 分⽀时,develop 分⽀的版本号会被⾃动更新成下⼀个⼩版本,如从 1.2.3 到 1.2.4。当需要⼿动修改版本号时,可以使⽤,如 mvn versions:set -DnewVersion=2.0.0-SNAPSHOT。
持续集成
由于 JGit-Flow 是纯 Java 的 Maven 插件实现,可以很容易的与常⽤的持续集成服务器进⾏集成。不过在与 Atlassian 的 Bamboo 集成时,有⼏个细节需要注意。⾸先是 Bamboo 在进⾏构建的时候,使⽤的是⼀个虚拟的 Git 仓库,其仓库地址是⼀个不存在的⽂件系统路径。因此需要在 JGit-Flow 的配置中⼿动设置 Git 仓库的地址,保证 Git 操作可以正确执⾏,如所⽰。
清单 2. ⼿动设置 JGit-Flow 的 Git 仓库地址
1 2 3 4< configuration >
< defaultOriginUrl >[Git url]</ defaultOriginUrl >
< alwaysUpdateOrigin >true</ alwaysUpdateOrigin > </ configuration >
另外在开始新的 release 之前,需要确保前⼀个发布分⽀已经被删除。JGit-Flow 在默认情况下会⾃动在发布完成之后,删除对应的 Git 分⽀。但是可能本地仓库中还保留有之前的发布分⽀,这会导致新的 release-start 任务执⾏失败。⼀种解决⽅式是每次都重新 checkout 新的仓库,这样可以保证不会出现已经在远程被删除的分⽀。不过可能会增加构建的时间。另外⼀种解决⽅式是通过 Git 命令来删除本地分⽀,如所⽰。
清单 3. 删除 Git 本地分⽀
1 2 3${bamboo.capability.utable} fetch --prune --verbose ${bamboo.capability.utable} branch -vv | awk '/: gone]/{print $1}' |
4 5 6 7 8                      xargs ${bamboo.capability.utable} branch -d 2> /dev/null echo 'stale branches deleted'
Git 分⽀合并冲突处理
当把发布分⽀合并到 develop 时,可能会出现冲突。因为在发布分⽀中有与 bug fix 相关的改动,在 develop 分⽀中有可能修改相同的⽂件。当有冲突时,直接运⾏ JGit-Flow 的 release-finish 任务会出错。这个时候需要开发⼈员⼿动把发布分⽀合并到 develop 分⽀,并解决相应的冲突。然后再次运⾏ release-finish 任务即可。
选择合适的实践
每个开发团队都应该根据团队⾃⾝和项⽬的特点来选择最适合的分⽀实践。⾸先是项⽬的版本发布周期。如果发布周期较长,则 git-flow 是最好的选择。git-flow 可以很好地解决新功能开发、版本发布、⽣产系统维护等问题;如果发布周期较短,则 TBD 和 GitHub flow 都是不错的选择。GitHub flow 的特⾊在于集成了 pull request 和代码审查。如果项⽬已经使⽤ GitHub,则 GitHub flow 是最佳的选择。GitHub flow 和 TBD 对持续集成和⾃动化测试等基础设施有⽐较⾼的要求。如果相关的基础设施不完善,则不建议使⽤。
⼩结
Git 作为⽬前最流⾏的源代码管理⼯具,已经被很多开发⼈员所熟悉和使⽤。在基于 Git 的团队开发中,Git 分⽀的作⽤⾮常重要,可以让团队的不同成员同时在多个相对独⽴的特性上⼯作。本⽂对⽬前流⾏的 3 种 Git 分⽀管理实践做了介绍,并着重介绍了 git-flow 以及与之相关的 Maven JGit-Flow 插件。
相关主题
了解和的单主⼲实践。
了解的更多内容。
了解的更多内容。
git使用详解了解 Maven  插件的更多内容。
:这⾥有数百篇关于 Java 编程各个⽅⾯的⽂章。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。