gitlab上代码回滚把自己坑了后, 陷入思考🤔"bug是谁"?
少年, 你点击过gitlab
下图中的金灿灿按钮么? 本篇文章将告诉你如何正确使用这个按钮。
一、 背景
原计划xx号下午3点我开发的功能要上线, 下午2点将开发分支合并入master
分支准备跑上线流水线, 但是不巧这一天server同学遇到了点问题, 导致上线时间延期了, 与此同时前端还有其他分支今天要上线, 已经准备合并入master
分支, 当时我要做的就是快速将master
回滚。
我提交的"mr"
信息里面有"revert"
按钮, 一看就知道这个按钮负责回滚代码, 鬼使神差的就点击了这个按钮, 当然啦代码被成功回滚并且也没有影响后续同学的上线, 但故事并没有这么简单。
当n天后我负责的代码计划再次上线时, git merge branch
在masetr
上merge
我的分支竟然失效了, 我的代码中新增的部分无法合并入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 id
为gt56th
的这条数据, 那么我们再将含有commit id
为gt56th
的提交merge
到master
分支时, git
的算法会判断出这个commit
已被移除, 所以git
会认为我分支的代码落后于master
, master
里面移除了这条commit
的代码才是最新的。
git
这样判断是没问题的, 我们多人开发的时候, 假设'a'与'b'一起开发一个项目, 'a'删除了a.js
文件并且merge
到master
, 第二天'b'改动了其他地方也merge
到master
, 此时就算b的代码上没有移除a.js
, merge
后也不会在master
分支上增加a.js
文件。
五、默认revert此事有蹊跷
我们来聊聊既然gitlab
默认使用revert
功能来回退代码, 也就是说官方认为这种回滚方式是最棒的, 那么它棒在哪里?
第一: 连续性
就算是回退操作, 也算是对master
的正常操作, 但是直接reset
会导致时间线的缺失, 让我们不知道中间发生了什么, 长期来看这样不利于解决问题。
第二: 中途有人拉代码
'a'的代码push
到了master
上, 2个小时后a将master
代码reset
掉, 看似一切正常, 但是殊不知'b'刚刚pull
了master
的代码到本地, 此时就埋下了隐患, 因为如果'b'进行master
的合并操作, 会将'a'之前删掉的代码再次发布到master
分支, 导致代码的错误上线。
第三: 方便回滚
比如说我们的gitlab
是默认merge
完毕就删除源分支的, 此时我们可以直接拉取master
的最新代码, 因为可以在git log
里面找到所有的commit
这样就不怕玩坏了分支代码找不到了。
六、reset 里的大学问
讲了不少revert
的好处与坏处改说说reset
了:
假设我当前项目里有 a.js
、b.js
、c.js
三个文件, a.js
被git commit -m''
, b.js
被git 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
查看的话就全是红色的。
七、流程梳理得出方案
已经了解了上述的知识点, 那么可以推导出一套比较可靠的回滚流程了, 当我们要将已经merge
到master
的代码暂时回滚, 并且后续还会上线这些代码时, 先点击gitlab
上的revert
按钮, 再将自己本地的代码git reset 线上commit版本
这样就可以将这些代码变成新的commit
, 这样就可以再次申请merge
到master
也不会合并不上了。
八、问题的出现与思索
刚出现这个问题的时候, 习惯性的认为gitlab
出问题了, git
的某些算法出问题了, 但是通过系统性的分析才明白, 出问题的是自己的操作。
将gitlab
平台设置成了中文, 导致某些英文可以表达的含义无法表达, 这也是个问题点, 写代码最好能更清楚的知道代码的本意, 而不是翻译过后的意思。
end
这次就是这样, 希望与你一起进步。