Appearance
GitFlow工作流
先放一张经典的图:
GitFlow工作流定义了一个严格的分支模型,它依赖版本演进,为管理大型醒目也提供了一套规范的流程。和前面提到的工作流相比,它给不同的分支指定了特定的角色,定义它们应该如何、什么时候协作。除了功能分支之外,它还为准备发布、维护发布、记录发布分别使用了单独的分支。当然,你还能享受到功能分支工作流带来的所有好处:Pull Request、隔离实验和更高效的协作。因此,在团队开发中被广泛使用。
GitFlow工作流同样以远程仓库为沟通中心,开发者需要将分支推送到远程仓库,唯一区别就是项目的分支结构。
master分支
和单独的master
分支不同,这里的master
分支只用来储存官方发布历史。同时,我们也会在master
分支上面提交打上的版本标签号。
develop分支
此为源自master
分支的开发分支,是我们用来整合各个功能的分支,类似于集中式工作流里面的master
,也是开发者打交道最多的分支。
feature分支
该分支叫功能分支,每个新功能都放在自己的分支中,并以develop分支作为父分支,当一个功能完成后,它将被合会develop分支,再由develop分支合并回master分支,而不应该直接与master分支交互。命名规范我们遵循feature-*
或者feature/*
。
release分支
一旦develop分支功能开发完成,或者预定的日期来临需要发布,我们可以从develop分支创建一个release分支,或者说是发布分支,这个分支的创建开始了下个发布周期,只有和发布相关的任务才在这个分支上进行,如修复bug,生成文档等等。我们在发布分支上面完成的需要合并进develop分支,一旦准备好了发布,发布分支将会合并进master,打上版本号的标签,同时合并回develop。
使用一个专门的分支来准备发布确保一个团队完善当前的发布,其他团队可以继续开发下一个发布的功能。它还建立了清晰的开发阶段(比如说,「这周我们准备 2.0 版本的发布」,而我们在仓库的结构中也能看到这个阶段),命名规范我们遵循release-*
或者release/*
。
hotfix分支
紧急修复分支用来快速给产品打上补丁,这是唯一一个可以从master
分支上面fork的分支,一旦修复完成了,它应该被合并入master
和develop
分支中,其中master
应该被打上更新的版本号的标签。
有一个专门的 bug 修复开发线使得团队能够及时处理 issues,而不打断其他工作流或是要等到下一个发布周期。你可以将维护分支看作在 master
分支上工作的临时发布分支。
接下来举例来说明一下这个工作流是如何工作的:
1. 初始化中央仓库
首先是我们的项目负责人在服务器上创建中央仓库,如果这是一个新项目,你可以初始化一个空的仓库,不然,你需要导入一个已经存在的Git或者SVN项目。
中央仓库应该永远是裸仓库(没有工作目录),命令如下:
ssh user@host
git init --bare /path/to/repo.git
注意这里使用的SSH用户名user
和host
服务器域名或者IP地址、储存仓库的地址/path/to/repo.git
都是有效的。
针对GitHub或者GitLab用户还可以使用图形界面来代替上面的操作。
2. 创建开发分支
负责人小王首先在本地创建一个空的develop分支,并将它最后推送到远程仓库里面。
git branch develop
git push -u origin develop
其中这个分支包含了项目中的所有历史,其他开发者小张和小李将远程仓库克隆到本地,并创建一个分支来追踪远程分支。
git clone ssh://user@host/path/to/repo.git
git checkout -b develop origin/develop
现在所有人本地都有一个历史分支的副版本。
3. 小张和小李分别开始开发新功能A和新功能B
首先,他们都需要为自己的功能创建单独的分支,此分支是基于develop,而不是master:
git checkout -b feature-A develop
git checkout -b feature-B develop
然后同样用我们之前的Git标准流程进行编辑、暂存、提交
git status
git add <some-file>
# git add .
git commit -m "提交说明"
4. 小张完成了功能A
小张添加了一些提交,然后确定功能完成了,如果我们使用GitHub或者Gitlab这类平台的话,这时候就可以发起Pull Request了,请求将他的功能合并入develop分支,如果没有使用的话,我们可以通过下面的方式来请求将它的本地develop分支推送到远程仓库:
git pull origin develop
git checkout develop
git merge feature-A
git push
git branch -d feature-A
第一条命令是确保功能分支并入之前,我们的develop分支是最新,这里注意的是一定不要并入master分支。
5. 小李完成了功能B
小李以同样的方式将功能B合并入develop分支,并将其push到远程仓库。这里需要注意的是小李需要先将小张的提交更新到本地,如果这中间出现冲突的话,解决方式与之前说的一样。
6. 小王准备发布版本
当小张和小李完成了他们各自的功能并入develop分支后,小王开始着手准备发布版本,和开发功能一样,他先从develop分支新建发布分支:
git checkout -b release-1.0.0 develop
git push -u origin release-1.0.0
这个分支用来修复bug,整理需求,更新文档等等,为即将到来的发布做准备,来完善我们的功能分支。理论上,一旦创建了这个分支,推送到远程仓库,不在develop分支中的功能都将被推迟到下个发布周期。
7. 小张和小李完善发布分支
小张和小李分别完善自己的功能,修复bug,编辑、暂存、提交,最后push到远程仓库:
git checkout -b release-1.0.0 release-1.0.0
git status
git add <some-file>
# git add .
git commit -m "提交说明"
# 经过多次commit 最后提交
git push
8. 小王完成发布
一旦所有功能完善,这时候就需要将release分支并入master和develop分支,然后删除发布分支,注意这里的合并回两个分支。
git checkout master
git merge release-1.0.0
git push
git checkout develop
git merge release-1.0.0
git push
git branch -d release-1.0.0
发布分支是功能开发develop和公开发布master分支之间的过渡阶段,记得不论什么时候提交到master的分支都需要打上方便引用的标签:
git checkout master
git tag -a 1.0.0 -m "initial release 1.0.0" master
git push origin --tags
Git提供了许多钩子,即仓库中特定时间发生时被执行的脚本,当向仓库推送master分支或者标签的时候,可以配置在这里一个钩子来做自动糊构建。
9. 终端用户发现BUG
正式发布之后,小张和小李紧接着下一个版本的开发功能,这时,终端用户发现一个问题说当前项目存在一个bug,为了解决这个BUG,负责出现与bug相关功能的那个人创建一个维护分支hotfix,用几个提交解决了这个BUG,然后合并回master和develop。
git checkout -b bug-#001 master
git status
git add <some-file>
# git add .
git commit -m "提交说明"
git checkout master
git merge bug-#001
git push origin master
git checkout develop
git merge bug-#001
git push origin master
git branch -d bug-#001
这样一来整个流程就算说完了,这是算相对复杂的一个,不过如果整个条例理清楚了,我们就可以灵活的掌握Git的使用。
关于GitFlow这里推荐提出者nvie写的命令行工具,简化了上面我们模拟的工作流程,由于此项目太过久远没有更新,后来petervanderdoes在此基础上加入了bugfix修复分支:gitflow-avh,具体安装可以参考该链接,里面有具体的安装方法。
首先初始化仓库:
git flow init
紧接着输入一些配置信息:
No branches exist yet. Base branches must be created now.
Branch name for production releases: [master]
Branch name for "next release" development: [develop]
How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []
这里建议都用默认配置,不用更改,完成后自动创建了master和develop分支,并且当前所在分支即为develop分支。
管理feature
开始创建功能分支:
git flow feature start <feature-name>
如果有多人协作时,也可以同步到远程仓库:
git flow feature publish <feature-name>
其他人可以同步下来:
git flow feature track <feature-name>
然后其他人通过push推送代码
git push origin feature/<feature-name>
创建人通过pull同步其他人写的代码:
git pull origin feature/<feature-name>
同理创建人同步自己写的功能也用同样方式,最后完成功能后:
git flow feature finish <feature-name>
这里finish操作会完成以下操作:
- 它会把feature分支整合进develop分支
- 删除当下的feature分支,并且切换到develop分支
管理bugfix
功能完成时我们开始修复bug,并创建bugfix分支
git flow bugfix start <bugfix-name>
可以通过publish同步到远程仓库:
git flow bugfix publish <feature-name>
其他人可以同步下来:
git flow bugfix track <feature-name>
然后其他人通过push推送代码
git push origin bugfix/<feature-name>
创建人通过pull同步其他人写的代码:
git pull origin bugfix/<feature-name>
同理创建人同步自己写的功能也用同样方式,最后完成功能后:
git flow bugfix finish <feature-name>
该finish操作会执行以下动作:
- 它会把bugfix分支整合进develop分支
- 删除当下的bugfix分支,并且切换到develop分支
管理release
当我们的develop分支是一个成熟的release版本时,第一,它包括所有新的功能和必要的修复;第二,它已经被彻底的测试过了。如果上述两点都满足,那就是时候开始生成一个新的 release 了。
git flow release start v1.0.0
请注意,release 分支是使用版本号命名的。完成一些上线的准备工作,比如文档版本说明等等,然后就执行finish。
git flow release finish v1.0.0
这个命令会完成如下一系列的操作:
- 首先,git-flow 会拉取远程仓库,以确保目前是最新的版本。
- 然后,release 的内容会被合并到 “master” 和 “develop” 两个分支中去,这样不仅产品代码为最新的版本,而且新的功能分支也将基于最新代码。
- 为便于识别和做历史参考,release 提交会被标记上这个 release 的名字,在这里为v1.0.0
- 清理操作,版本分支会被删除,并且回到 “develop”。
从 Git 的角度来看,release 版本现在已经完成。如果我们做了CI,对 “master” 的提交就是在这里触发了定义的部署流程,当然也可以通过手动部署。
这个过程非常类似于发布一个 release 版本:
- 完成的改动会被合并到 “master” 中,同样也会合并到 “develop” 分支中,这样就可以确保这个错误不会再次出现在下一个 release 中。
- 这个 hotfix 程序将被标记起来以便于参考。
- 这个 hotfix 分支将被删除,然后切换到 “develop” 分支上去。