关于Git的rebase操作与cherry-pick操作的建议

缘起

最近一次新疆采编发版时,发现代码中有一处配置的一个变量没有了,经过查询Git的提交历史发现这次改动涉及的提交bbb的作者是开发人员A,但A君表示从来没有动过这块代码。

这就很奇怪了,明明Git上显示的作者是A君,但A又表示不是自己改的,那到底是Git在说谎还是A君在说谎?

经过线下询问其他采编开发人员,并对比Git的分支图谱,多方查证,终于弄清楚了原因:

  1. 开发人员B君要开发的一个功能正好在产品分支上已经有了,所以采用了cherry-pick的方式从产品分支将这个功能的那次提交000(作者是D,提交者是A)从产品分支复制到了新疆开发分支1上面。复制时产生了冲突,B君解决了冲突,进行了提交aaa
  2. 在解决冲突的过程中,由于那个丢失的变量在产品分支上没有,而在新疆分支上有,但是那个变量上面便是要从产品分支上复制过来的代码,所以B君直接通过工具采用了产品分支的代码,导致新疆分支上有的那个变量被产品分支代码所替换而丢失。
  3. B君继续在开发分支1上进行了其它功能的开发,进行了两次提交,然后提了MR给C君,请求将开发分支1合并到主开发分支0上面。
  4. 这时D君说,不用合并了,他要直接rebase,所以C君关闭了MR,而D君使用rebase命令直接将开发分支1上的这几次提交复制到了主开发分支0上,复制后提交aaa变为了提交bbb

然后就是后来,我们开头说的,发版时发现了变量丢失的问题。

这个过程中,我们产生过两个疑问:

  1. 我们通过历史提交查询的时候为什么没有查询到提交aaa
  2. 为什么提交bbb的作者不是B君而是A君。

破案

解决这两个问题,我们先要弄清楚:

当我们查看提交历史的时候,所显示的Author(作者)真的就是这次提交中那些文件的真正改动者吗?

$ git log
commit af7af21595aba7d6ac02b34c3a852ed267a395b7 (HEAD -> master, origin/master, origin/HEAD)
Author: chen.chen <chenchen@trs.com.cn>
Date:   Thu Aug 5 17:55:12 2021 +0800

    修复全部栏目检索问题

首先,我们应该知道,Git中所谓的【提交】在底层实现上就是一个二进制对象,它存储在.git/objects目录下。

就比如上例中af7af21这次提交,其实指向的是.git/objects/af/7af21595aba7d6ac02b34c3a852ed267a395b7这个二进制对象,我们可以通过Git的底层命令来查看一下这个二进制对象的内容:

$ git cat-file -p af7af21
tree 3ab963b8fca3658404126787e075b4d4dbdfc697
parent ab8c90a1ca8544802c3e00b2ea5ccaf01d20976b
author chen.chen <chenchen@trs.com.cn> 1628157312 +0800   
committer chen.chen <chenchen@trs.com.cn> 1628157312 +0800

修复全部栏目检索问题

可以看到,这个提交对象了有两个和人员有关的属性:

  • author:代表这次提交所涉及的更改真正的作者
  • committer: 创建这个提交的人。

从这一点来看,一般情况下,提交都是由作者进行的,那作者和提交者应该是同一个人。

但是,总有不一般的情况,就比如:

  1. cherry-pick操作:A将另一个分支上的提交a复制到自己的分支时,会在当前分支上重新产生一个新的提交b,这个提交b的内容和提交a的内容一模一样,但是提交a的提交者将变成提交b的作者,而A将成为提交a的提交者。
  2. rebase操作: 当A将另一个分支上的提交通过rebase复制到自己分支时,那个提交的作者不变,但提交者将由原来的提交者变为A。
  3. MR 操作:A在一个分支上进行了提交若干次提交后,发送了一个MR请求给B,B在合并时勾选了合并提交选项,这时A做的若干次提交将被合并为一个新的提交(相当于rebase -i操作),此时,这个新的提交的作者是A,提交者是B。

而在本文开头所述那次事故,正好就是踩了这几个坑,我们来梳理一下整个过程:

  1. C君在产品分支1上开发了若干功能(其中就包含B君要的那个),进行了提交,发送了一个MR请求给A君,A君勾选了【合并提交】选项后将产品分支1合并入了产品分支。此时D君做的所有提交被合并后生成了一个新的提交000,这个提交的作者是C,提交者是A。
  2. B君在新疆开发分支1上进行了cherry-pick,将000分支复制到了开发分支1,解决冲突后提交,生成了提交aaa,此时aaa提交的作者是A,提交者是B。
  3. D君使用了rebase操作将aaa从开发分支1复制到了开发分支0上,此时开发分支0上产生了提交bbb,这个提交的作者还是A,但提交者变成了D。

当发现代码问题时,我们顺着开发分支0的提交历史去看,自然看到的作者是A。

所以,Git没有说谎,A也没有说谎,这是一场由A、B、C、D共同参与的【无心的共谋】。

教训

这次事故的原因找到后,我们从中应该学到一些东西。

  1. rebase合并提交操作会重写提交历史。(MR时的合并提交其实就是rebase -i
  2. rebase复制提交操作后,提交历史无法反映提交来源自哪个分支以及被复制提交的原始信息
  3. cherry-pick复制提交操作后,提交历史无法反映提交来源自哪个分支以及被复制提交的原始信息,并且无法真实反映被复制提交原来的作者信息。(当被复制提交作者与提交者不同时)

这些特性的存在,会严重影响代码出问题时的溯源工作。

所以,这里做出如下建议,供大家讨论决定:

  1. rebase操作不允许应用在共享分支上。(可以在自己的私有分支间随便折腾)
  2. cherry-pick操作必须记录到单独日志文件里,注明原始分支和提交的相关信息。

·

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值