gitlab上代码回滚把自己坑了后, 陷入思考🤔"bug是谁"?

少年, 你点击过gitlab下图中的金灿灿按钮么? 本篇文章将告诉你如何正确使用这个按钮。

一、 背景

     原计划xx号下午3点我开发的功能要上线, 下午2点将开发分支合并入master分支准备跑上线流水线, 但是不巧这一天server同学遇到了点问题, 导致上线时间延期了, 与此同时前端还有其他分支今天要上线, 已经准备合并入master分支, 当时我要做的就是快速将master回滚。

     我提交的"mr"信息里面有"revert"按钮, 一看就知道这个按钮负责回滚代码, 鬼使神差的就点击了这个按钮, 当然啦代码被成功回滚并且也没有影响后续同学的上线, 但故事并没有这么简单。

     当n天后我负责的代码计划再次上线时, git merge branchmasetrmerge我的分支竟然失效了, 我的代码中新增的部分无法合并入master, 当时情况紧急眉头都皱成了一个"川"字, 焦急兽进化(🧬)钢铁焦急兽。

二、 强推灰飞烟灭

     当时现场求助了同组的其他同学, 将本地我的分支git merge master, 此时我的分支就是最新的, 再去gitlab上关闭master的保护机制, 强行将分支内容推到master上进行覆盖, 再将master的保护机制重新打开。

     这一套操作当然存在问题, 强推master可还行? 并且这种操作需要相关人员的审核, "费力+有风险"。

     当一切尘埃落定了也该开始思考了, 那么到底为什么会出现代码无法合并入master, 到底是哪一步出了问题, 当时我的gitlab使用的语言是中文, 按钮只显示"还原"两个字, 我赶快将语言切换至英语, 此时按钮的文案变成了"revert", 我就从嫌疑人"revert"开始调查吧, 看看他与不在场的"reset"有什么关联。

三、静下来学习reset与revert的区别

revert

     移除某个commit记录, 并且生成一条新的commit记录。

     假设当前的分支状态是下图:

     执行

git revert -n gt56th

git add .

git commit -m'feat: rm a'

reset

     移动HEAD到某个commit上, 这个commit之后的commit全部舍弃, 并且你本地的代码是没有变化的。

     假设当前的分支状态是下图

     执行git reset gt56th

四、解释为何代码合不进去

     通过对比我们就可以知道为什么, 那天下午我无法把代码合并入master分支的原因了。

     由上面的步骤图可知, revert后的代码里是明确记录了删除目标commit也就是commit idgt56th的这条数据, 那么我们再将含有commit idgt56th的提交mergemaster分支时, git的算法会判断出这个commit已被移除, 所以git会认为我分支的代码落后于master, master里面移除了这条commit的代码才是最新的。

     git这样判断是没问题的, 我们多人开发的时候, 假设'a'与'b'一起开发一个项目, 'a'删除了a.js文件并且mergemaster, 第二天'b'改动了其他地方也mergemaster, 此时就算b的代码上没有移除a.js, merge后也不会在master分支上增加a.js文件。

五、默认revert此事有蹊跷

     我们来聊聊既然gitlab默认使用revert功能来回退代码, 也就是说官方认为这种回滚方式是最棒的, 那么它棒在哪里?

第一: 连续性

     就算是回退操作, 也算是对master的正常操作, 但是直接reset会导致时间线的缺失, 让我们不知道中间发生了什么, 长期来看这样不利于解决问题。

第二: 中途有人拉代码

     'a'的代码push到了master上, 2个小时后a将master代码reset掉, 看似一切正常, 但是殊不知'b'刚刚pullmaster的代码到本地, 此时就埋下了隐患, 因为如果'b'进行master的合并操作, 会将'a'之前删掉的代码再次发布到master分支, 导致代码的错误上线。

第三: 方便回滚

     比如说我们的gitlab是默认merge完毕就删除源分支的, 此时我们可以直接拉取master的最新代码, 因为可以在git log里面找到所有的commit这样就不怕玩坏了分支代码找不到了。

六、reset 里的大学问

     讲了不少revert的好处与坏处改说说reset了:

     假设我当前项目里有 a.jsb.jsc.js三个文件, a.jsgit commit -m'', b.jsgit add, c.js没有被git监控:

git reset --hard CommitId (暴力删除)

     这个写法直接将HEAD回退到目标分支, 并且删除所有当前分支之后编写的代码, 也就是说会将你的代码清除哦。

     这个方法适合完全舍弃某些代码的场景, 因为你在git log命令里面都无法查到被删除的commit记录了。

git reset --soft CommitId (舒服的?)

     这个写法直接将HEAD回退到目标分支, 并且保留你在目标commit之后的修改, 这些修改都在暂存区, 我们可以继续开发相关的功能, 最后统一 git add. && git commit -m '' 一次即可。

     这种方式让我想到了我们可以平时在自己电脑上提交多个commit, 但是push之前我们可以先回退, 然后把commit合一再提交。

git reset --mixed CommitId (默认的, 当我们不写参数就是这个指令)

     将目标提交之后的代码还原成未被监控的状态, 也就是你的代码需要git add .一下才可以进行管理, 这个招式相当于重置了提交态, 但是也有一些小小问题, 比如我们有一些没有被git add的文件会与其他文件不好分割开, 给我的感觉就是向我们当前的代码里面塞入很多新的代码, 使用git status 查看的话就全是红色的。

七、流程梳理得出方案

     已经了解了上述的知识点, 那么可以推导出一套比较可靠的回滚流程了, 当我们要将已经mergemaster的代码暂时回滚, 并且后续还会上线这些代码时, 先点击gitlab上的revert按钮, 再将自己本地的代码git reset 线上commit版本这样就可以将这些代码变成新的commit, 这样就可以再次申请mergemaster也不会合并不上了。

八、问题的出现与思索

     刚出现这个问题的时候, 习惯性的认为gitlab出问题了, git的某些算法出问题了, 但是通过系统性的分析才明白, 出问题的是自己的操作。

     将gitlab平台设置成了中文, 导致某些英文可以表达的含义无法表达, 这也是个问题点, 写代码最好能更清楚的知道代码的本意, 而不是翻译过后的意思。

end

     这次就是这样, 希望与你一起进步。

03-05 23:41