玩儿转Devops —— 从用Git画一颗树开始(上)

必备知识

  1. git基础操作merge
  2. git基础操作rebase

如果你对git还不熟练就先来练习一下吧 https://learngitbranching.js.org/

我们的目标是:用git画出一颗树

还记得你手头上项目的第一行代码吗?不妨用下面的命令看看它现在的样子,你看到几条线?

git log --all --decorate --oneline --graph

如果你的*非常的密集,无论有多少条线,我要恭喜你,这表明你的项目状态良好

看起来不错

但是如果你的*非常散碎,commit记录之间出现大量的空白,这说明你的commit存在较多的merge,你很可能需要梳理一下版本库了,下图这样的车祸现场一样的,抢救就免了吧,可以直接拉到火葬场。

你已经死了

现在面试,面试官经常会一本正经的问“你有没有什么问题想要问我的?”,一般来说这就是个礼貌性提问,不过既然人家给了机会,咱也别浪费,对于不知道要问些什么的工友我教大家一个简单的,就用上面的命令看看团队主项目的git树。如果树“看起来不错”的亚子,这就是个值得加入的团队,如果树不幸长成了“你已经死了”的亚子,这更是一个值得加入的团队,因为你一显身手的时候到了!想想看吧,一手搭建团队不可或缺的devops流程,一步成为核心成员,你不牛逼谁牛逼?是不是有些小激动?醒醒吧,起来跟工头学习搬砖了!

年纪轻轻就该多搬砖

题外话:这东西对我有什么用

在学习任何东西之前,都应该明确这东西对我有什么用。相信大家都在大学都学习过高数,从泰勒展开到各种中值定理,如果你是数学相关专业的,最后会一路学到斯托克斯方程组,如今公式里的符号早已忘却,只剩下那个折磨过自己很长时间的名词还在脑海里挥之不去。工头在学习高数的时候不止一次怀疑过人生,完全想不通这东西除了能让我毕业还有什么用。直到工作过很长一段时间,有一次要设计一个老虎机算法,要求随机给玩家发1-100个金币,平均值为8。我才第一次用上积分方程,完美实现了需求,乐此不疲地玩儿了一个下午,不停想要刷新最高记录,而无论怎么刷,平均值始终稳定在8左右,这真是太有意思了。

解出来a、b来写个老虎机函数吧

至于斯托克斯方程组,我也是偶然得知游戏里的爆炸和烟雾效果,其实就是用它拟合出来的,可以说方程求解的越精准,你看到的效果就越逼真。在了解了方程物理意义的那一刻,方程里所有的符号看起来都不再抽象,干瘪的公式犹如被注入了灵魂变得生动和有趣起来。因为对数学产生了新的认识,工头又重新自学了一遍高数,探索了每个方程、公式的物理意义,看看它们能解决哪些实际问题,整个过程非常有趣且使人膨胀,甚至让我一度产生了应该可以把小孩儿辅导到大学毕业的错觉(大雾)

辅导功课被气到心梗

牛逼吹完了,我们来看看学习画一颗漂亮的git树有什么用。最直接的作用是方便追踪历史记录,通过git树你就可以清晰的看到每次发布的内容,如果线上出现任何问题,通过git树来回溯出现问题的时间并剔除出现问题的代码比直接打补丁的方式高效一些。要知道维护一颗干净漂亮的git树需要花费很多精力,这从一个侧面也反映出维护者对待项目的态度和精神面貌。从更长远的层面讲,干净整洁意味着低熵,而一切降低项目中熵的行为都值得称赞并一定会获得回报。

整洁的秘密 —— rebase

多人合作的项目有两个方式可以完成代码的合并,一个是merge,一个是rebase。merge的优点在于简洁高效,易于掌握。缺点也很明显,merge也会显示在git历史中,尤其是多人合作开发一个分支的时候,merge数量往往会比较多,让人看不清有效的commit,久而久之git树就会变得非常凌乱。工头掐指一算你的项目多半已经已经是车祸现场了,这倒不是因为工头是个神棍,只不过如果你还不知道rebase或者很少用rebase,git树最后就会变成那个样子,而大量使用rebase的项目就像维护者不停在修剪分支,始终保持了git树的简洁。

一个项目中的merge往往来自两个地方

  1. 在本分支拉取他人的代码 git pull 产生的自动merge
  2. 将分支合并到上游分支,比如从dev合并到master

其实这两个地方的merge都可以通过rebase来解决,在本分支拉取他人代码时添加--rebase参数

git pull origin feature-1 --rebase

将分支合并到上游之前

git rebase -i origin master

让我们来用一个小项目来看看merge和rebase对git树的影响。我们需要完成两个功能,分别创建分支1和分支2,同时在分支1上模拟多人协作开发的过程。

首先创建master和f1、f2两个分支

在f1上以user1提交a

在f1上以user2分别提交b和c,并修改a

在f1上以user1提交新的d之前,我们需要将user2在f1上新增的b、c以及对a的改动合并过来,此时你有两个选择

  1. git pull origin f1
  2. git pull origin f1 --rebase

前者会产生一次merge,而后者则不会。这里工头要详解一下pull中的merge操作,pull就是一次fetch + merge,一般情况下合并是fast-forwad的

一般情况下pull操作的合并都是fast-forward

但是也有特殊情况,如果你在合并user2的提交之前在f1上产生了新的提交(这在多人协作中很常见,彼此并不知道对方什么时候会提交),或者你手动添加了--no-ff,那么就会产生一个非fast-forward的合并。

我们先在f1上提交了b,此时产生了非fast-forward合并

所以这个时候你用git pull origin f1 --rebase实际上是做了一个git fetch && git rebase -i f1的操作,将本分支的提交rebase到了user2的提交之后。

使用--rebase,可以看到user1的修改rebase到了user2的提交之后

说完了pull,我们再来看看将分支合并到上游的情况。

在f2上以user1提交e和f,并合并到master

此时将f1合并到master之前,我们需要将master上的改动合并到f1,此时也有两个选择

  1. git merge master 直接将master上的改动合并到f1
  2. git rebase -i master 以交互形式进行rebase

毫无疑问,前者会多产生一次merge,我们来看看最终的效果,你就知道为什么我们要避免merge了

使用merge将master的修改合并到f1

使用rebase将f1 rebase到master的改动之后

最后在master上完成merge的操作,多人协作的项目上进行合并时建议还是使用-no-ff来合并,这样出现任何问题也可以及时将整个merge干掉完成回滚。

如果上面的图看得不过瘾,工头做了一个rebase操作的视频,遇到问题的时候可以反复观看此视频,对比rebase和merge --no-ff之后git树的变化。

视频加载中...

搜索rebase操作你会发现很多文章说它是一个“危险操作”,如果不知道自己在做什么最好不要轻易尝试。所谓的危险,其实也确实存在,主要是多人协作时,你checkout出来之前的那个分支,比如master已经合并了很多代码,这个时候如果有冲突,往往会解决多次冲突,原因是rebase操作要将分支里的每个commit都和上游比较并变基,这个时候用merge将上游的修改直接合并到本分支也是可以的,一个可以尝试的方法是用rebase压缩(squash)冲突开始之后的commit,再尝试rebase到master。

写在最后

一个良好的scm流程,是构建高效Devops的基础。工头将在下篇文章中以Devops的视角解读一个优秀的git流是如何工作的,以及维护一颗漂亮的git树和高效Devops之间的关系。

欢迎关注工头,带你玩儿转Devops

玩儿转Devops —— 部署的艺术

举报
评论 0