去年我负责开发了产品中的一个大型feature,这个feature被分解成了A, B两个小feature。大致的工作流程是这样的:
我从master branch out出分支feature-a
,在分支feature-a
上开发;然后从feature-a
branch out出feature-b
,在feature-b
上开发;最后创建feature-b
-> feature-a
,feature-a
-> master
两个PR分别进行code review并依次合并进master。
当我在开发feature-b
时由于master上有了很多更新,于是我将master分别merge进了feature-a
和feature-b
。整个流程的简化版大致如下:
当我创建从feature-b
到feature-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-b
到feature-a
的PR中(https://github.com/frankgx97/git-diff/pull/2/files)我们可以发现:图中 -A, +AE
的两行是在master上的commit E中的更改,因为feature-a
和feature-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-a
和feature-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一致。
于是就引出了下面两个问题:
- 为什么GitHub的diff会和cli与其他平台不同?这是刻意设计,还是bug?
- 这种场景下使用git的best practice是什么?
发表回复/Leave a Reply