高效研发:硅谷研发效能方法与实践
上QQ阅读APP看书,第一时间看更新

10.2 工作流二:使用多个分支完成所有需求的开发

在这种开发工作流下,每个需求都拥有独立的分支。同样的,与单分支实现提交原子性的方式一样,这些分支都是本地分支,并非主代码仓上的功能分支。

需要注意的是,在下面的分析中,我只描述每个分支上只有一个提交的简单形式,至于每个分支上使用多个提交的情况,操作流程与单分支提交链中的描述一样,这里不再累述。

多分支工作流的具体步骤,大致包括以下4步。

1)切换到某一个分支对某需求进行开发,产生提交。

2)提交完成后,将其发送到Phabricator上进行质量检查。在等待质量检查结果的同时,切换到其他分支,继续其他需求的开发。

3)如果第2步发送到Phabricator的提交没有通过质量检查,则切换回这个提交所在分支,对提交进行修改,修改之后返回第2步。

4)如果第2步发送到Phabricator的提交通过了质量检查,则切换回这个提交所在分支,把这个提交推送到远端代码仓中,然后回到第1步进行其他需求的开发。

下面,我们看一个开发C和D两个需求的场景。在这个场景中,我首先开发需求C,并把它的提交C1发送到Phabricator;然后开发需求D,等到C1通过质量检查之后,立即将其推送到远程共享代码仓中去。

阶段1:开发需求C

需求C是一个简单的重构,把index.js中所有的var都改成const。首先,使用git checkout -b feature-c origin/master产生本地分支feature-c,并跟踪origin/master。

> git checkout -b feature-c origin/master
Branch 'feature-c' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'feature-c'

然后,开发需求C,生成提交C1,并把提交发送到Phabricator进行检查。

## 修改代码,产生提交
> vim index.js
> git diff
diff --git a/index.js b/index.js
index cc92a42..e5908f0 100644
--- a/index.js
+++ b/index.js
@@ -1,6 +1,6 @@
-var port = 3000
-var express = require('express')
-var app = express()
+const port = 3000
+const express = require('express')
+const app = express() app.get('/timestamp', function (req, res) {
    res.send('' + Date.now())
20:54:10 (feature-c) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo
> git add .
20:54:16 (feature-c) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo
> git commit

## 填写详细提交说明
Refactor to use const instead of var
Summary: const provides more info about a variable. Use it when possible.Test: ran `node index.js` and verified it by visiting localhost:3000.
Endpoints still work.
## 以下是Commit Message保存并退出后,git commit的输出结果
[feature-c 2122faa] Refactor to use const instead of var
 1 file changed, 3 insertions(+), 3 deletions(-)

## 使用arc命令把当前提交发送给Phabricator进行检查
> arc diff

## 查看提交链
* 2122faa (HEAD -> feature-c, multi-branch-step-1) Refactor to use const instead of var
* 5055c14 (origin/master) Add documentation for getRandom endpoint
...

这时,origin/master上只有feature-c一个分支,上面有C1一个提交,如图10-15所示。

110-01

图10-15 生成C1提交(多分支工作流状态1)

阶段2:开发需求D

将C1发到Phabricator进行质量检查后,开始开发需求D。需求D是在README.md中添加所有endpoint的文档。首先,使用git checkout -b命令产生一个分支feature-d并跟踪origin/master。

> git checkout -b feature-d origin/master
Branch 'feature-d' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'feature-d'
Your branch is up to date with 'origin/master'.

然后,开发需求D,生成提交D1,并把D1发送到Phabricator进行检查。

## 进行修改
> vim README.md
## 添加,产生修改,输入提交说明
> git add README.md
> git commit
## 查看修改
> git show
commit 97047a33071420dce3b95b89f6d516e5c5b59ec9 (HEAD -> feature-d, multi-branch-step-2)
Author: Jason Ge <gejun_1978@yahoo.com>
Date:   Tue Oct 15 21:12:54 2019    Add spec for all endpoints
    Summary: We are missing the spec for the endpoints. Adding them.    Test: none
diff --git a/README.md b/README.md
index 983cb1e..cbefdc3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,8 @@
  # This project is for demoing atomic commit in git-You can visit endpoint getRandom to get a random real number.
-The end endpoint is `/getRandom`.
+## endpoints
+
+* /getRandom: get a random real number.
+* /timestamp: get the current timestamp.
+* /: get a "hello world" message.

## 将提交发送到Phabricator进行检查
> arc diff

## 查看提交历史
> git log --oneline --graph feature-c feature-d
* 97047a3 (HEAD -> feature-d Add spec for all endpoints
| * 2122faa (feature-c) Refactor to use const instead of var
|/
* 5055c14 (origin/master) Add documentation for getRandom endpoint

这时,origin/master上有feature-c和feature-d两个分支,分别有C1和D1两个提交,如图10-16所示。

111-01

图10-16 分支上有C1、D1两个提交(多分支工作流状态2)

阶段3:推送提交C1到远端代码仓共享分支

这时,我收到Phabricator发来的C1通过检查的通知,可以推送C1了!首先,使用git checkout把分支切换回分支feature-c:

> git checkout feature-c
Switched to branch 'feature-c'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

然后,运行git fetch; git rebase origin/master,确保分支上有最新的远程共享分支代码:

> git fetch
> git rebase origin/master
Current branch feature-c is up to date.

接下来,运行git push推送C1:

> git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 460 bytes | 460.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:jungejason/git-atomic-demo.git
   5055c14..2122faa  feature-c -> master

## 查看提交状态
* 97047a3 (feature-d) Add spec for all endpoints
| * 2122faa (HEAD -> feature-c, origin/master, multi-branch-step-1) Refactor to use
    const instead of var
|/
* 5055c14 Add documentation for getRandom endpoint
...

这时,origin/master指向C1。分支feature-d从origin/master的父提交上分叉,上面只有D1一个提交,如图10-17所示。

112-01

图10-17 推送C1到远程代码仓(多分支工作流状态3)

阶段4:继续开发D1

完成C1的推送后,继续开发D1。首先,用git checkout命令切换回分支feature-d;然后,运行git fetch和git rebase,确保当前代码D1包含了远程代码仓的最新代码,以减少将来合并代码产生冲突的可能性。

> git checkout feature-d
Switched to branch 'feature-d'
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
21:38:22 (feature-d) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo> git fetch
> git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: Add spec for all endpoints## 查看提交状态
> git log --oneline --graph feature-c feature-d
* a8f92f5 (HEAD -> feature-d) Add spec for all endpoints
* 2122faa (origin/master,) Refactor to use const instead of var
...

这时,当前分支为feature-d,上面有唯一一个提交D1',而且D1'已经变基到了origin/master上,如图10-18所示。

113-01

图10-18 继续开发D1(多分支工作流状态4)

需要注意的是,因为使用的是git rebase,没有使用git merge产生合并提交,所以提交历史是线性的。在后面的章节中我们会详细讨论线性的提交历史对CI自动化的重大意义。

至此,我们完成了在两个分支上同时开发C、D两个需求,并尽早把完成的提交推送到远端代码仓中的全过程。

虽然在这个例子中,我简化了这两个需求开发的过程,每个需求只有一个提交并且一次就通过了质量检查,但结合在一个分支上完成所有需求开发的流程,相信你也可以推导出每个需求有多个提交,以及质量检查没有通过时的处理方法。