翻译:一个成功的Git分支模型
原文链接:http://nvie.com/posts/a-successful-git-branching-model/
在这篇博客里,我将展示一个开发模型。这个模型在我之前的几个项目里引入,这个模型被证明是非常成功的。我早就想写一篇文章介绍它,但是直到现在才有时间干这个事情。我不会讲项目的具体细节,我主要讲讲分支策略和发布管理。
它专注于用Git作为我们源码版本控制的工具
为什么用Git
关于Git和其他中心化的源码控制系统的比较,你可以通过这个链接来看到一个比较完整的讨论。关于这个比较,有太多的唇枪舌战了。作为一个开发者,比起其他工具,我更倾向于Git。Git真的是改变了开发者思考合并及分支的想法。在典型的CVS/Subversion世界中,合并/分支已经被认为是有点可怕(“小心合并冲突,他会咬你”),并且是你唯一时而要做的事情。
但是有了Git,这个行为非常简单,这些工作会被认为是你日常工作的核心部分之一。例如,在 CVS/Subversion 书里,分支和合并是在后面几章第一个被讨论的事情,但是在每一个Git书里,这些知识都在基础章节介绍。
这种简单和重复的天性导致了分支和合并不在是某种可怕的事情。版本控制工具被认为是分支/合并的助攻而不是其他东西。
写了太多工具的工具,让我们直接开始讲开发模型吧。我要讲的这个模型不外乎一系列团队成员为了管理开发进程而要遵守的一系列过程。
去重新化&&中心化
我们平常用到的工作仓库是一个”中心“仓库。注意这个仓库只是被认为是中心化仓库(因为Git是去中心化的,在技术层面是没有一个中央仓库的)。我们用origin引用这个仓库,这个名字对每一个Git用户都很熟悉
每一个用户从origin拉去和推送代码。但是除了这种这种中心化的推送拉去模型,每一个开发者同样可以从其他开发者那里拉去代码形成一个子团队。例如,当两个或者多个开发者工作在同一个比较大的功能模块开发上,比起过早的推送到origin,这种子团队模式就非常有用。在上面这个图里,Alice和Bob、Alice和David、Clair和David之间都有子团队存在。
从技术角度上,这无非就是Alice定义了一个远端叫做bob,指向Bob的仓库,反之亦然。
主要分支
这个开发模型从现有的模型上汲取了很多灵感,中央仓库保持两个主要的分支,这两个分支永久存在。
- master
- develop
每一个Git用户都应该非常熟悉origin上的master分支。和master分支一样,另一个存在的分支叫develop
我们认为origin/master应该是HEAD反应一个生产环境下准备好的状态的一个分支
我们认为origin/develop应该是HEAD总是反应一个状态,这个状态下有下个发布版本最新的开发变动。
当develop分支下的源码到达稳定状态的时候并且准备发布的时候,所有的改变应该合并到master分支,然后打上发布号码的标签。做到这点的具体方法后面会讨论。
所以,每次有变动合并到master分支,这就是一个版本发布。我们对这个要求很严格,所以理论上,我们可以用一些Git脚本做到服务器的自动构建和发布。
支持分支
除了master和develop分支以外,我们的开发模型使用了一些支持分支来辅助团队成员之间的并行开发,这便于跟踪功能、准备下次版本发布或者是修复一些生产环境下的问题。不想主要分支,这些支持分支生存时间有限,并且最终会被移除。
我们会用到的几种分支
- 功能分支(Feature Branches)
- 发布分支(Release Branches)
- 修复分支(Hotfix Branches)
每一个分支都有一个特定的目的和对应的严格规则,以及每一个分支都有一个来源和一个merge对象。我们一会一一介绍他们。
但从技术角度上看,这些分支一点不特殊。他们的分类依据是我们使用他们的目的。他们都是纯粹的git分支
功能分支(Feature Branches)
可能从如下分支来:
develop
必须合并到:
master
分支命名管理
任何名字除了 master,develop,release-*, hotfix-*
功能分支(有时候也叫主题分支topic branches)是为了开发将来需要用到的或者很久的将来才会用到的功能。当开始开发一个功能时,这个功能将要合并到的目标发布版本有可能还是未知的。功能分支和这个功能开发时长一样,最终被合并到develop分支或者被抛弃(在测试不理想的情况下)
功能分支通常存在开发者仓库里,不在origin
创建一个分支
当开始着手一个新的功能时,从develop分支新增
git checkout -b myfeature develop
//Switched to a new branch ‘myfeature’
合并一个开发好的功能到develop
get checkout develop
// switched to branch 'develop'
git merge --no-ff myfeature
// updating ea1b82a..05e9557
git branch -d myfeature
// delete branch myfeature
git push origin develop
—no-ff 标志会造成合并操作总是创建一个新的Commit对象,即使这个合并可能是向前合并的。这避免丢失一些管理功能分支的历史信息,并且可以把这个功能所有的改动一起添加。
上图中后者,想从Git历史中看出哪些commit记录实现了这个功能几乎是不可能的,你必须得手动读所有的日志信息。在后者的情况下,把一个功能移除将会是非常头疼的事情。但是有—no-ff 标志就很简单。
他将会创建一些空的提交记录,但是这样做好处大于坏处。
发布分支
可能从如下分支来:
develop
必须合并到:
develop 和 master
分支命名管理
release-*
发布分支是为一个新的发布版本做准备。他允许你做一些修枝减叶,也可以做一些bug修复和准备发布的元数据(例如版本号,发布日期等)。通过把这些工作放到发布分支上,develop分支可以保持整洁,接受下一次分支需要的功能合并。
从develop分支切出来一个release分支最佳的时候是develop分支最反映发布状态的时候。起码这个发布分支所有的功能分支已经合并到了develop分支。未来发布分支的功能不能合并到develop分支,他们必须等到当前这个发布版本新建之后。
当一个发布分支建好之后,这个版本就应该分配版本号,不能再早了。到这个时候,develop分支开始反应下个发布版本,下个发布版本的版本号还不清楚,直到下个发布分支建立。版本号在发布版本建立的时候,根据项目规格定。
创建一个发布分支
发布分支从develop分支创建。例如我们假设1.1.5是当前的发布版本,我们将要发布一个大版本。目前develop准备好了下一次版本发布,我们决定下个版本叫1.2。所以我们新建分支然后给这个发布分支一个能反应新的版本号的名字
git checkout -b release-1.2 develop
./bump-version.sh 1.2
git commit -a -m 'Bumped version number to 1.2'
创建一个新的分支然后切换到这个分支,我们改变版本号。这里bump-version.sh 是一个功能脚本,它可以改变反映版本号的文件,然后提交这个改变。
这个新的分支可能存在一段时间,直到这个分支推出。在这段时间内,bug修复都在这个分支上(而不是在develop分支)。添加大的功能是被禁止的。大的功能应该添加到develop分支,等到下次版本再发布。
完成一个发布分支
当发布分支的状态准备变成一个真的发布时,我们需要做一些事情。第一,把发布分支merge到master分支(这样每一个在master分支上的提交都是一次发布)。然后,在master上的提交应该被打上标签,以便将来我们可以引用这个历史版本。最后,在发布分支上改的东西需要被重新merge到develop分支,这样将来的分布将会包括这次的bug修复。
开始的两部操作
git checkout master
git merge --no-ff release-1.2
git tag -a 1.2
发布完成,并且打上了标签,将来我们可以引用他
git checkout develop
git merge --no-ff release-1.2
这一步也可能导致冲突,如果冲突,修复它
现在我们就完工了,这个发布版本就要被移除了,因为我们不再需要他了
git branch -d release-1.2
热修复分支
可能从如下分支来:
master
必须合并到:
develop 和 master
分支命名管理
hotfix-*
热修复分支非常像发布分支,因为他们都是为了准备下次发布,尽管这次是计划之外的。他由于当前生产环境一个意外状态引起。当生产环境下的重大bug必须被马上修复的时候,一个热修复分支就从master分支创建出来。
本质上,其他成员的开发不受影响,负责的人着手bug修复
创建一个热修复分支
热修复分支从master分支创建出来。例如,我们假设1.2是目前的发布版本,这个版本因为几个bug存在严重问题。但是目前在develop分支的提交还不稳定。我们就需要新建一个热修复分支然后修复这个问题
git checkout -b hotfix-1.2.1 master
./bump-version.sh 1.2.1
git commit -a -m 'Bumped version number to 1.2.1'
新建分支后,不要忘记修改版本号
然后,修复通过一个或者多个commit修复bug
git commit -m 'Fixed servere production problem'
完成一个热修复分支
当完成的时候,这个变动需要merge回master,同时需要merge到develop分支,为了保障这个bug不会出现在下个发布版本里。这跟发布分支很类似。
首先,更新master分支然后给这个发布打上标签
git checkout master
git merge --no-ff hotfix-1.2.1
git tag -a 1.2.1
Edit你可能会用-s 和-u 标志来加密你的标签
接下来,把这些改动融合到develop分支
git checkout develop
git merge --no-ff hotfix-1.2.1
这个规则的例外是,当目前一个发布版本存在的时候,热修复的改动应该merge到发布分支而不是develop分支。发布分支的改动最终还是会merge到devleop。
最后,移除这个临时分支
git branch -d hotfix-1.2.1
总结
这个模型并没有什么特别触目惊心的地方,博文开始的大图在我们的项目里被证明非常有用。他规范了一个优雅的精神上的模型,这个模型很好理解,允许团队成员开发一个共享的、标准的分支发布过程。
我们还提供了一个高质量的PDF版本的图片,请查看附件,你可以在任何时候引用他。
gitflow.pdf