GitHub Pull Request Diff中的诡异行为




去年我负责开发了产品中的一个大型feature,这个feature被分解成了A, B两个小feature。大致的工作流程是这样的:

我从master branch out出分支feature-a,在分支feature-a上开发;然后从feature-a branch out出feature-b,在feature-b上开发;最后创建feature-b -> feature-afeature-a -> master 两个PR分别进行code review并依次合并进master。

当我在开发feature-b时由于master上有了很多更新,于是我将master分别merge进了feature-afeature-b。整个流程的简化版大致如下:

当我创建从feature-bfeature-a的PR时,奇怪的问题出现了:PR的diff中混入了大量已经在master分支上的提交。虽然我相信这个问题并不会真的影响合并,但是PR中混入了很多无关的commit则会让PR难以review。

Manager告诉我我不应该直接将master分支merge进feature-b,而是应该先将master merge到feature-a,然后将feature-a merge进feature-b,也就是这样:

我照做之后问题解决了,但是这并没有解答我的疑惑:我在本地使用git diff显示出的结果是不包含master上的那些无关commit的,为什么GitHub PR的diff会和本地的diff显示不同的结果?

于是我试着基于上图的简化版模型重现了整个过程:https://github.com/frankgx97/git-diff

在从feature-bfeature-a的PR中(https://github.com/frankgx97/git-diff/pull/2/files)我们可以发现:图中 -A, +AE 的两行是在master上的commit E中的更改,因为feature-afeature-b上均已经存在commit E,所以这个更改不应该显示在这里。

通过查找一些资料发现,GitHub PR的diff默认提供的是三点diff,而git diff默认则是两点diff。两者的区别在于三点diff是用来比较A和B的公共祖先与B的差异,git diff a...b等价于 git diff $(git merge-base a b) b

然而git cli的三点diff并没有包含 -A, +AE ,这似乎并不能用三点diff来解释。

$ git diff feature-a...feature-b
 AE
 B
+C
+D

通过git merge-base命令可以发现,git cli认为feature-afeature-b的merge base是f4c1318,也就是图中的commit E。也就是说git cli的三点diff是在比较commit E和D,这符合我们上面得到的结果。

$ git merge-base feature-a feature-b
f4c13184f9af7f2dcd0fe706a355f7703e7bc39d

不过从GitHub中的diff来看,被比较的应该是topic branch的最新commit D(85c13ed)和feature-a和feature-b的公共祖先B(01c9a49)。我猜测GitHub并不是直接使用的git中的三点diff,而是选取两个分支的公共祖先与topic branch最新的commit进行比较而得出的diff。

$ git diff 01c9a49 85c13ed
@@ -1,2 +1,4 @@
-A
+AE
 B
+C
+D

我又将git仓库上传到了GitLab和BitBucket,这两个平台上的diff均与git cli一致。

于是就引出了下面两个问题:

  1. 为什么GitHub的diff会和cli与其他平台不同?这是刻意设计,还是bug?
  2. 这种场景下使用git的best practice是什么?

References


Leave a Reply

Your email address will not be published.