更适合年轻人体质的 git 工作流

关于如何使用 git,相信大家都见过下面这张图:

很多人都学习过这张图上的流程并应用在实际工作中,但是慢慢就发现,用起来好像有点不对劲:令人困惑的合并冲突,每次发版前都需要找哪些 commit 需要发布等等。然后突然发现,诶这套流程好像用起来也不太爽,不知道有没有更好的流程可以用。本篇文章就来聊一聊这个问题。

现有 git flow 存在的问题

首先我们来分析一下上面这个流程中都存在哪些问题:

feature 分支要从 dev 分支创建,怎么保证代码是干净的?

举个例子,你要开发一个新功能,从 dev 切出一个新分支后之后发现怎么都跑不起来,群里问了一圈发现有人提交到 dev 的代码有问题,于是你就等到他重新提交了一个 commit 之后,你拉了下代码,这才开始正常开发。

发版时需要从 dev 分支创建 release 分支,怎么保证代码都是干净的?

再举个例子,本轮迭代共提交了 20 个 commit,其中 16 个 commit 需要发布,剩下 4 个 commit 因为还没测试完、bug 没改完不能发。这时候你能准确的把要发布的 commit 检出来么?

如果可以的话,咱们更进一步,本轮迭代由五位同事提交了 40 个 commit,在发版的时候其中两个请假了,这时候你能准确的知道哪些 commit 是要发布的,并准确将其检出来么?

如果还可以的话,那就更更进一步,你检出来之后,发布到 uat 环境,发现代码跑不起来了,结果发现,有个同事偷懒了,某个 commit 因为功能没开发完所以没有检进来,但是恰好这个 commit 里又包含了一些非常关键的代码,没有就跑不起来,这时候你会怎么做?

需要保持 dev 分支和 master 分支的同步,不同步的话可能导致合并冲突

回忆一下,你之前有没有处理过这种合并冲突:冲突的两方代码是完全一样的,但就是冲突了。

这种就是使用了 rebase(非 fast-forward)或者 cherry-pick 后导致的,因为这两种方法会产生代码完全一样,但是 id 不同的新 commit。就导致了 git 产生了混乱。

一个常见场景就是 hotfix 分支的 commit,合并到 master 之后又 cherry-pick 到了 dev 分支。这样下次再从 dev 往 master 合的时候就会出现这种问题。


不知道你什么感受,反正我是已经开始汗流浃背了,那么有没有更简单、更高效、心智负担更低的 git 工作流能解决这些问题呢?当然是有的。

正式介绍一下新的 git flow

首先我们还是以流程图的形式展示一下新的 flow:

和原本 git flow 的区别在于:

  • feature 分支不再从 dev 创建,而是从最稳定的 master 分支创建
  • dev 分支的代码不再向 release 分支合并,由 feature 直接发起到 release 的合并。
  • 当 release 分支测试完成要发版的时候,直接 fast-forward 到 master
  • 定期删除 dev 和 release,然后从 master 创建新的(例如每轮迭代结束之后)

那么这套工作流能解决刚才提到的问题么?答案是肯定的,老的工作流中存在的问题主要就是:

dev 分支过于重要

dev 需要接受来自多个 feat 以及 hotfix、master 的合并,并合并到 release 分支,这就会导致 dev 分支出现冲突的概率是成倍增加的。开发人员越多,其中存在的脏代码就越多,分支就越不稳定,冲突的情况就越多。

而这套新流程中 dev 的职责被弱化了,变得更加纯粹,即只对接测试环境的发布,其他的工作一概不管。也就是说 dev 本身就是合并路径的终点,从而消除了合并 commit 的回环,干掉了很多可能会产生迷惑冲突的场景。

从普通开发人员的视角看一下

现在我们从头开始,以普通开发的身份来走一遍这套流程,看会有什么效果:

  • 昨天版本发布了,master 代码上有了新的 commit,于是你执行了 git fetch会把远程的代码都同步到本地,比如远程的 master 分支同步到本地的 origin/master
  • 早上开会的时候给你安排了功能 a 和功能 b,你决定先做 a,于是你执行了 git checkout -b feat/a origin/master从刚才拉下来的 origin/master 分支创建了一个新分支
  • 你开始开发,随着开发进度的增加,中间可能执行了多次 git addgit commit
  • 几个小时后终于把功能做好了,自测也没问题,你决定发到测试环境让 QA 同事看一下,于是你执行了 git push 并且在远程仓库里提交了 feat/a 到 dev 分支的 pr,合并完成后流水线自动把代码发布到了测试环境。
  • 通知了 QA 之后,你决定开始开发 b 功能,于是你执行了 git checkout -b feat/b origin/master,然后开始开发。
  • 突然 QA 通知你功能 a 有 bug 需要修复,于是你执行了 git stash 把当前手头的工作暂存了起来,然后 git checkout feat/a 开始解决 bug。
  • 解决完了之后,你重新 git commitgit push 到了 dev 分支,QA 开始继续测试,你也切回了 feat/b 分支并 git stash pop 开始继续开发。
  • 过了一会,QA 通知你功能 a 测试没问题了,于是你在远程仓库里找到 feat/a 分支,并直接发起了一个到 release 分支的 pr。此时 release 分支触发了流水线,将功能 a 的代码更新到了预发环境。
  • 搞完之后,你切回 feat/b 分支继续开始功能 b 的开发...

故事到这里就结束了,你可能会好奇:版本发布的时候呢?不需要执行什么操作?

是的不需要。这套流程中发布生产环境极其简单。因为功能测试完成后会直接推到 release 分支。也就是说,只要和 release 分支绑定的环境(例如 uat)测试没问题,那么发布的时候只需要把 release 合并到 master 就行了。不会出现之前那种要在发版前检查很久要发布哪些 commit 的情况。

一些疑问解答

在实践过程中也有很多同事对这套流程产生了或多或少的疑问,这里就记录一下,希望对大家有帮助:

1、代码提交到 release 分支后出现 bug 怎么办?

切换到对应的分支(例如 feat/c),提交新的 commit 之后从 feat/c 合并到 dev,dev 测试没问题后从 feat/c 合并到 release 分支。

2、feat 分支合并到 dev 分支的时候代码冲突了怎么办?

首先,代码冲突很正常,没有任何一个工作流能完全避免代码冲突。我们应该尽力避免因工作流本身的问题产生的"令人困惑"的代码冲突。

比较正规的做法是:从最新的 dev 创建一个新分支,例如 dev-feat/a,然后把你的 feat/a 本地合并到 dev-feat/a 并解决冲突,然后 git push dev-feat/a 并在远程仓库发起 dev-feat/a 到 dev 的 pr。

比较随性的做法是:本地切到 dev 分支,git pull --rebase 拉取最新代码,然后直接 git rebase feat/a 解决冲突后直接 git push 到远程仓库的 dev 分支。

有些人可能会有疑问:"直接 push 到这种环境分支没问题么,之前我们这种分支都是写保护的,只能接受 pr"。

确实,老的工作流对环境分支的保护都是比较严格的,但是这一套工作流没有这些限制,因为最遭的情况也就是你把 dev 分支搞崩了。那直接把远程 dev 分支删掉再从 master 或者 release 分支拉一个就完事了嘛,反正大家的功能都在各自的 feat 分支上。再极端一点,只要你不搞坏其他人的代码,你就算直接 git push --force 强制推送到 dev 分支都没问题。

3、同事 A 和 B 的新功能要基于同事 C 的新代码,这时候怎么办?

假设同事 C 开发的功能在 feat/c,那么同事 A 和 B 的分支就应该从 feat/c 创建并继续开发。而不是等同事 C 合并到 dev 之后再从 dev 创建。

4、既然是 feat 直接合并到指定分支,那么为什么最后一步不是 feat 分支合并到 master 分支呢?

因为这套流程里,最重要的就是保证 master 分支的稳定性。所以 master 分支上的代码必须是经过严格验证的。

并且如果 feat 直接合到 master 的话还会导致一些其他的问题:

  • 有一个同事比较粗心,在提交 pr 的时候本来该合到 dev 分支,结果一不小心点到了 master,审核的人有不注意直接点了同意,这时候 master 就被污染了。
  • 合并到 dev 时如果出现合并冲突的话,那么合并到 release 分支大概率也会再出现一遍,你总不会想合到 master 的时候去解决第三遍吧,而且也无法保证冲突的解决一定是不会出问题的。

所以说,最稳妥,最省心的做法就是直接把 release 分支的代码合并到 master。

5、hotfix master 怎么办?

git flow 里 hotfix 分支中的 commit 一方面要合并到 master,另一方面要同步到 dev。但是由于后续 dev 也要再次更新到 master,这个 hotfix 的 commit 就可能会导致困惑冲突。

但是这套新流程里就不会出现冲突,因为 dev 分支自己就已经是终点了,不会合并到其他分支。所以 hotfix 里的提交无论怎么合并到 dev,不管是 merge、rebase 还是 cherry-pick,都是可以的。甚至不用管也没关系,因为只要是新 feat 合并到 dev,这个 hotfix commit 就被自动携带过来了。

总结

其实这一套工作流其实是 gitlab flow + git flow 的一个调优,使其在保证效率的同时更贴近 git 新手的心理认知。总结一下就是 dev 分支并不会"晋升"到 release 分支。而是由 feat 分支发起到 release 分支的合并,同时 master 只接受来自 release 的合并,由此减少了很多需要遵守的规则和发生冲突的情况。

参考

相关推荐
柚个朵朵2 小时前
IDEA中使用Git
java·git·spring
我是哈哈hh2 小时前
【Git】初始Git及入门命令行
git·gitee·github·版本控制器
lifejump2 小时前
Git命令(Gitee)
git·gitee
极小狐2 小时前
如何创建并使用极狐GitLab 部署令牌?
运维·git·ssh·gitlab·github
Kusunoki_D3 小时前
Win11 配置 Git 绑定 Github 账号的方法与问题汇总
git·github
Justice link5 小时前
Git和Gitlab的部署和操作
git
极小狐5 小时前
极狐GitLab 的合并请求部件能干什么?
运维·git·安全·gitlab·极狐gitlab
vortex55 小时前
Git常用命令简明教程
git
YoungHong199211 小时前
Git Bisect 使用指南:高效定位引入 Bug 的提交
git·bug
极小狐14 小时前
极狐GitLab 的压缩和合并是什么?
运维·git·ssh·gitlab·github