git作为当下最流行的版本控制工具,它存在于互联网的各个位置。

当进行个人开发,或项目规模不大时,只需要工作在master分支下,代码提交和变动都在主分支便足够了。

而在多组员、多项目的环境下时,如果没有比较好的协作流程,会存在许多问题,比如较多的版本冲突、版本不好控制等等。GitFlow作为最早被广泛采用的一种工作流程,便是为了解决这些问题。它最早发表于A successful Git branching model

GitFlow之后,还有Github FlowGitlab Flow,它们都是对前者的改进,以适应不同的开发环境,本文主要阐述的是GitFlow

GitFlow特点

GitFlow存在5个分支:

  • 长期分支:masterdevelop
  • 短期分支:featurereleasehotfix

masterdevelop是需要同时维护的两个长期分支。master主要用于发布版本,从master拿到的,都是稳定的发布版。develop主要用于开发,存放的是最新的开发版。

feature是功能分支,每个新功能都应该创建一个feature分支,从develop派生出来,当feature完成时,要合并回develop分支,同时结束它的生命周期。

release是发布分支,当功能开发完善,准备发布一个新版本时,从develop分支派生出一个release分支,在这个分支上,不再添加功能,只修复测试出现的bug。当发布时机到了,release要合并到masterdevelop分支,同时打上版本标签。

hotfix是热修复分支,当线上环境出现bug时,需要从对应master分支派生hotfix分支,进行快速修复,修复完成后,合并回master,打上版本标签,同时合并到develop分支,以确保修复的问题不会在后续的版本出现。

三个短期分支在完成了它们自身的任务后,都没有了存在的必要,所以在合并回masterdevelop之后,都可以删除。

实践

从上面的特点我们知道,GitFlow工作流的主要特点在于要维护5个分支,分支间的切换十分频繁而且繁琐,庆幸的是,开源社区已经有贡献者提供了插件使用,在终端下,可以安装gitflow插件。

下面的操作都建立在安装完插件的基础上完成。

插件概览

安装完插件后,我们执行下git flow命令,可以看到下面的使用提示:

1
2
3
4
5
6
7
8
9
10
11
12
 ~/project/test/gitflow/ git flow
usage: git flow <subcommand>

Available subcommands are:
init Initialize a new git repo with support for the branching model.
feature Manage your feature branches.
release Manage your release branches.
hotfix Manage your hotfix branches.
support Manage your support branches.
version Shows version information.

Try 'git flow <subcommand> help' for details.
  • init:将项目初始化成一个支持gitflow模型的仓库
  • feature:管理feature功能分支
  • release:管理release发布分支
  • hotfix:管理hotfix修复分支
  • support:管理support分支

初始化gitflow仓库

首先,我们要有一个git项目,然后进入到项目目录,执行git flow init操作,接下来会询问对各特性分支的命名,如果不需要特别命名,那么按照默认就好。

1
2
3
4
5
6
7
8
9
10
11
 ~/project/test/gitflow/ [master] 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? []

这样我们就建好了gitflow模型的仓库。查看.git/config文件我们可以得到gitflow分支的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[gitflow "branch"]
master = master
develop = develop
[gitflow "prefix"]
feature = feature/
release = release/
hotfix = hotfix/
support = support/
versiontag =

新建功能分支(Feature)

通过命令git flow feature start feature-helloworld新建一个名字叫feature-helloworld的功能分支:

1
2
3
4
5
6
7
8
9
10
 ~/project/test/gitflow/ [develop] git flow feature start feature-helloworld
Switched to a new branch 'feature/feature-helloworld'

Summary of actions:
- A new branch 'feature/feature-helloworld' was created, based on 'develop'
- You are now on branch 'feature/feature-helloworld'

Now, start committing on your feature. When done, use:

git flow feature finish feature-helloworld

接下来我们新建一个文件index.js,程序打印出hello gitflow

1
2
3
 ~/project/test/gitflow/ [feature/feature-helloworld] echo "console.log('hello gitflow.');" > index.js
 ~/project/test/gitflow/ [feature/feature-helloworld] node index.js
hello gitflow.

然后commit到版本库中,通过git flow feature finish完成feature分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 ~/project/test/gitflow/ [feature/feature-helloworld] git add .
 ~/project/test/gitflow/ [feature/feature-helloworld+] git commit -m 'hello gitflow'
[feature/feature-helloworld 3e9f1f0] hello gitflow
2 files changed, 2 insertions(+)
create mode 100644 README.md
create mode 100644 index.js
 ~/project/test/gitflow/ [feature/feature-helloworld] git flow feature finish feature-helloworld
Switched to branch 'develop'
Updating bed51b3..3e9f1f0
Fast-forward
README.md | 1 +
index.js | 1 +
2 files changed, 2 insertions(+)
create mode 100644 README.md
create mode 100644 index.js
Deleted branch feature/feature-helloworld (was 3e9f1f0).

Summary of actions:
- The feature branch 'feature/feature-helloworld' was merged into 'develop'
- Feature branch 'feature/feature-helloworld' has been removed
- You are now on branch 'develop'

feature合并回develop分支使用的是git merge --no-ff,在develop分支下,尽管feature对应分支已经被我们删除,日志信息依旧存在。

发布版本(Release)

假设我们现在已经开发完成功能了,可以通过realse发布一个版本,操作起来跟feature相似。

1
2
3
4
5
6
7
8
9
10
11
12
13
 ~/project/test/gitflow/ [develop] git flow release start release-v0.1.0
Switched to a new branch 'release/release-v0.1.0'

Summary of actions:
- A new branch 'release/release-v0.1.0' was created, based on 'develop'
- You are now on branch 'release/release-v0.1.0'

Follow-up actions:
- Bump the version number now!
- Start committing last-minute fixes in preparing your release
- When done, run:

git flow release finish 'release-v0.1.0'

通过上面的操作,我们建立了一个版本v0.1.0的release分支。现在这个版本可以进行测试,但是不再新增任何功能,只做bug修复。当我们完成修复后,通过finish发布,插件会帮我们完成几个操作,合并回masterdevelop,同时打上tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 ~/project/test/gitflow/ [release/release-v0.1.0] git flow release finish release-v0.1.0
Switched to branch 'master'
Merge made by the 'recursive' strategy.
README.md | 1 +
index.js | 1 +
2 files changed, 2 insertions(+)
create mode 100644 README.md
create mode 100644 index.js
Deleted branch release/release-v0.1.0 (was 3e9f1f0).

Summary of actions:
- Latest objects have been fetched from 'origin'
- Release branch has been merged into 'master'
- The release was tagged 'release-v0.1.0'
- Release branch has been back-merged into 'develop'
- Release branch 'release/release-v0.1.0' has been deleted

修复线上bug(Hotfix)

假设现在在线上出现了bug,有用户提出了issue#1,那么我们可以创建hotfix分支,操作依旧相似。我们创建一个名为release-v0.1.1的hotfix分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
 ~/project/test/gitflow/ [master] git flow hotfix start release-v0.1.1
Switched to a new branch 'hotfix/release-v0.1.1'

Summary of actions:
- A new branch 'hotfix/release-v0.1.1' was created, based on 'master'
- You are now on branch 'hotfix/release-v0.1.1'

Follow-up actions:
- Bump the version number now!
- Start committing your hot fixes
- When done, run:

git flow hotfix finish 'release-v0.1.1'

我们进行修复后commit到仓库,在commit的信息中,我们可以在内容加上fix #1,那么issue在对应的远程平台比如Github会被close掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 ~/project/test/gitflow/ [hotfix/release-v0.1.1*] git add .
 ~/project/test/gitflow/ [hotfix/release-v0.1.1+] git commit -m 'fix #1'
 ~/project/test/gitflow/ [hotfix/release-v0.1.1] git flow hotfix finish release-v0.1.1-1
Switched to branch 'master'
Merge made by the 'recursive' strategy.
index.js | 1 +
1 file changed, 1 insertion(+)
Switched to branch 'develop'
Merge made by the 'recursive' strategy.
index.js | 1 +
1 file changed, 1 insertion(+)
Deleted branch hotfix/release-v0.1.1 (was 6811c88).

Summary of actions:
- Latest objects have been fetched from 'origin'
- Hotfix branch has been merged into 'master'
- The hotfix was tagged 'release-v0.1.1'
- Hotfix branch has been back-merged into 'develop'
- Hotfix branch 'hotfix/release-v0.1.1' has been deleted

完成hotfix,代码会合并回masterdevelop,同时我们起的hotfix名字release-v0.1.1会被打成新的标签。

Sourcetree

如果你喜欢使用GUI工具,知名的有Sourcetree,它也内置了Gitflow工作流程的功能。

参考