Git 分支管理最佳实践html
Git 是目前最流行的源代码管理工具。熟练使用 Git 已经成为开发人员的必修课之一。对于团队开发来讲,如何有效的使用 Git 的分支是一个重要的课题。须要在新功能开发,新版本发布和已有版本的维护等需求中达到一个良好的平衡。另外还须要与持续集成服务有良好的集成。本文对几种主流的 Git 分支管理实践进行了介绍,能够帮助开发团队选择本身最合适的方案。java
Git 是目前最流行的源代码管理工具。大量的软件项目由 GitHub、Bitbucket 和 GitLab 这样的云服务平台或是私有的 Git 仓库来管理。在使用 Git 时一般会遇到的一个问题是采用何种分支管理实践,即如何管理仓库中做用不一样的各种分支。和软件开发中的其余实践同样,Git 分支管理并无广泛适用的最佳作法,而只有对每一个团队和项目而言最适合的作法。简单来讲,在项目开发中使用多个分支会带来额外的管理和维护开销,可是多个分支对于项目的团队合做、新功能开发和发布管理都是有必定好处的。不一样的团队能够根据团队人员组成和意愿、项目的发布周期等因素选择最适合的策略,找到最适合团队的管理方式。本文将介绍三种常见的 Git 分支管理方式。git
单主干的分支实践(Trunk-based development,TBD)在 SVN 中比较流行。Google和 Facebook 都使用这种方式。trunk 是 SVN 中主干分支的名称,对应到 Git 中则是 master 分支。TBD的特色是全部团队成员都在单个主干分支上进行开发。当须要发布时,先考虑使用标签(tag),即 tag 某个 commit 来做为发布的版本。若是仅靠 tag 不能知足要求,则从主干分支建立发布分支。bug 修复在主干分支中进行,再 cherry-pick 到发布分支。图 1 是 TBD 中分支流程的示意图。github
因为全部开发人员都在同一个分支上工做,团队须要合理的分工和充分的沟通来保证不一样开发人员的代码尽量少的发生冲突。持续集成和自动化测试是必要的,用来及时发现主干分支中的 bug。由于主干分支是全部开发人员公用的,一个开发人员引入的 bug 可能对其余不少人形成影响。不过好处是因为分支所带来的额外开销很是小。开发人员不须要频繁在不一样的分支之间切换。apache
GitHubflow 是 GitHub 所使用的一种简单的流程。该流程只使用两类分支,并依托于 GitHub 的 pull request 功能。在 GitHub flow 中,master 分支中包含稳定的代码。该分支已经或即将被部署到生产环境。master 分支的做用是提供一个稳定可靠的代码基础。任何开发人员都不容许把未测试或未审查的代码直接提交到 master 分支。服务器
对代码的任何修改,包括 bug 修复、hotfix、新功能开发等都在单独的分支中进行。不论是一行代码的小改动,仍是须要几个星期开发的新功能,都采用一样的方式来管理。当须要进行修改时,从 master 分支建立一个新的分支。新分支的名称应该简单清晰地描述该分支的做用。全部相关的代码修改都在新分支中进行。开发人员能够自由地提交代码和 push 到远程仓库。eclipse
当新分支中的代码所有完成以后,经过 GitHub 提交一个新的pull request。团队中的其余人员会对代码进行审查,提出相关的修改意见。由持续集成服务器(如Jenkins)对新分支进行自动化测试。当代码经过自动化测试和代码审查以后,该分支的代码被合并到 master分支。再从 master 分支部署到生产环境。图 2 是 GitHub flow 分支流程的示意图。maven
GitHub flow 的好处在于很是简单实用。开发人员须要注意的事项很是少,很容易造成习惯。当须要进行任何修改时,老是从 master 分支建立新分支。完成以后经过 pull request 和相关的代码审查来合并回 master 分支。GitHub flow 要求项目有完善的自动化测试、持续集成和部署等相关的基础设施。每一个新分支都须要测试和部署,若是这些不能自动化进行,会增长开发人员的工做量,致使没法有效地实施该流程。这种分支实践也要求团队有代码审查的相应流程。工具
git-flow 应该是目前流传最广的 Git 分支管理实践。git-flow 围绕的核心概念是版本发布(release)。所以 git-flow 适用于有较长版本发布周期的项目。虽然目前推崇的作法是持续集成和随时发布。有的项目甚至能够一天发布不少次。随时发布对于 SaaS 服务类的项目来讲是很适合的。不过仍然有很大数量的项目的发布周期是几个星期甚至几个月。较长的发布周期多是因为非技术相关的因素形成的,好比人员限制、管理层决策和市场营销策略等。post
git-flow 流程中包含 5 类分支,分别是 master、develop、新功能分支(feature)、发布分支(release)和 hotfix。这些分支的做用和生命周期各不相同。master 分支中包含的是能够部署到生产环境中的代码,这一点和 GitHub flow 是相同的。develop 分支中包含的是下个版本须要发布的内容。从某种意义上来讲,develop 是一个进行代码集成的分支。当 develop 分支集成了足够的新功能和 bug 修复代码以后,经过一个发布流程来完成新版本的发布。发布完成以后,develop分支的代码会被合并到 master 分支中。
其他三类分支的描述如表 1所示。这三类分支只在须要时从 develop 或 master 分支建立。在完成以后合并到 develop 或 master 分支。合并完成以后该分支被删除。这几类分支的名称应该遵循必定的命名规范,以方便开发人员识别。
分支类型 |
命名规范 |
建立自 |
合并到 |
说明 |
feature |
feature/* |
develop |
develop |
新功能 |
release |
release/* |
develop |
develop 和 master |
一次新版本的发布 |
hotfix |
hotfix/* |
master |
develop 和 master |
生产环境中发现的紧急 bug 的修复 |
对于开发过程当中的不一样任务,须要在对应的分支上进行工做并正确地进行合并。每一个任务开始前须要按照指定的步骤完成分支的建立。例如当须要开发一个新的功能时,基本的流程以下:
在进行版本发布和 hotfix 时也有相似的流程。当须要发布新版本时,采用的是以下的流程:
由于 git-flow 相关的流程比较繁琐和难以记忆,在实践中通常使用辅助脚本来完成相关的工做。好比一样的开发新功能的任务,可使用 git flow feature start my-awesome-feature 来完成新分支的建立,使用 git flow feature finish my-awesome-feature 来结束 feature 分支。辅助脚本会完成正确的分支建立、切换和合并等工做。
对于使用 Apache Maven 的项目来讲,Atlassian 的JGit-Flow是一个更好的 git-flow 实现。JGit-Flow 是一个基于JGit 的纯 Java 实现的 git-flow,并不须要安装额外的脚本,只须要做为 Maven 的插件添加到 Maven 项目中便可。JGit-Flow 同时能够替代 Mavenrelease 插件来进行发布管理。JGit-Flow 会负责正确的设置不一样分支中的 Maven 项目的 POM 文件中的版本,这对于 Maven 项目的构建和发布是很重要的。
在 Maven 项目的 pom.xml 文件中添加代码清单1中的插件声明就可使用 JGit-Flow。<configuration>中包含的是 JGit-Flow 不一样任务的配置。
<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所示。
名称 |
描述 |
适用任务 |
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 等操做,不须要开发人员手动完成。
每一个分支的 pom.xml 中的版本号的格式并不相同。如 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。当须要手动修改版本号时,可使用 Versions 插件,如 mvnversions:set -DnewVersion=2.0.0-SNAPSHOT。
持续集成
因为 JGit-Flow 是纯 Java 的 Maven 插件实现,能够很容易的与经常使用的持续集成服务器进行集成。不过在与Atlassian 的 Bamboo 集成时,有几个细节须要注意。首先是 Bamboo 在进行构建的时候,使用的是一个虚拟的 Git 仓库,其仓库地址是一个不存在的文件系统路径。所以须要在 JGit-Flow 的配置中手动设置 Git 仓库的地址,保证 Git 操做能够正确执行,如代码清单2所示。
<configuration>
<defaultOriginUrl>[Git url]</defaultOriginUrl>
<alwaysUpdateOrigin>true</alwaysUpdateOrigin>
</configuration>
另外在开始新的 release 以前,须要确保前一个发布分支已经被删除。JGit-Flow 在默认状况下会自动在发布完成以后,删除对应的 Git 分支。可是可能本地仓库中还保留有以前的发布分支,这会致使新的 release-start 任务执行失败。一种解决方式是每次都从新 checkout 新的仓库,这样能够保证不会出现已经在远程被删除的分支。不过可能会增长构建的时间。另一种解决方式是经过 Git 命令来删除本地分支,如代码清单3所示。
${bamboo.capability.system.git.executable} fetch --prune --verbose
${bamboo.capability.system.git.executable} branch -vv | awk '/: gone]/{print $1}' |
xargs ${bamboo.capability.system.git.executable} 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 插件。