如果我进行
git diff --staged
它与HEAD进行了比较。
如果我做
git diff master...HEAD
它将master与我的分支进行了比较(以GitHub PR的方式)
我如何将两者结合起来,以便将大师级和舞台级进行比较?
TL;DR
如果不使用至少一个额外的Git命令,就无法将您没有直接指定的第三次提交与暂存区进行比较。如果要在分支名称B
指定的分支提示提交和索引/暂存区域的内容之间选择合并基,则必须使用两个Git命令。例如,作为一个两行shell脚本,您可以执行以下操作:
base=$(git merge-base <B> HEAD)
git diff --cached $base # or --staged
你可以在一行中创建一个Git别名,例如:
git config --global alias.bvs='!f() { git diff --staged $(git merge-base $1 HEAD); } f'
(名称bvs
代表Base Vs Staged)。(这个别名有点缺陷,因为它不能验证你实际上只给出了一个参数。它也不能测试是否只有一个这样的合并基提交,但git diff
也不能;见下文。)
Long-ish
注意虚线语法:git diff
会对其进行特殊处理。
您可以:
git diff
git diff --staged # or --cached, exactly the same thing
git diff <commit>
git diff --cached <commit>
git diff <commit1> <commit2>
git diff <commit1>..<commit2> # note two dots
git diff <commit1>...<commit2> # note three dots
(还有更多!但"更多"的情况不属于这种主要模式)。在每种不同的情况下,您都要选择两种东西进行比较:
- commit1与commit2,对于使用两个提交说明符但不使用三个点的情况
- commit与工作树,用于命名一个commit而不使用
--staged
的情况 - 提交与索引/暂存区域,用于命名一个提交并使用
--staged
或--cache
的情况
到目前为止,所有这些都使用您指定的提交,或者同时使用您指定的两个提交。但是:
git diff <commit1>...<commit2> # note three dots
实际上将一些第三次提交与commit2
进行了比较。
Git选择的第三个提交基于运行的结果:
git merge-base --all <commit1> <commit2>
或者(更接近git diff
在内部的实际作用):
git rev-parse <commit1>...<commit2>
git merge-base
命令根据提交图查找两个指定提交的所有合并基。提交图是解释所有提交存储的父级的结果。您在评论中链接到的问题的公认答案中的图表有助于直观地解释合并基础。(这个链接可能应该在你最初的问题中,1,因为它有助于定义问题。)
如果您运行此处显示的实际git rev-parse
,您将看到如下输出:
$ git rev-parse master...origin/maint
da72936f544fec5a335e66432610e4cef4430991
083378cc35c4dbcc607e4cdd24a5fca440163d17
^da72936f544fec5a335e66432610e4cef4430991
这是rev-parse的文本表示:
- 由名称
master
指定的提交:分支master
的顶端 - 名称
origin/maint
指定的提交:截至我最后一次让我的Git调用其他Git时,其他Git的maint
分支的提示(origin/maint
是他们maint
分支的远程跟踪名称);以及 - 必须在修订步行中排除的第一个提交,该修订步行发现提交可以从两个提示提交中的任何一个进行,但不能从两个都进行
合并基是可以从两个提示提交中访问的提交。它比这更受约束:它是最好的此类提交2可以有多个"最佳"提交,在这种情况下,git merge-base --all
将找到所有这些提交。git rev-parse
命令也会:每个命令都需要用前缀^
字符排除。
有三个点的git diff A...B
所做的就是调用git rev-parse
的内部等价物来查找所有的合并基。(您可以自己使用git merge-base --all
或使用git rev-parse
来完成此操作,尽管您没有获得内部接口使用的所有内部标志的提交。)然后,注意到有一些正选择的提交和至少一个负选择的提交,git diff
从列表中随机选择一个负选提交。因此,如果git rev-parse
产生一个合并基,则git diff
选择该一个合并基。如果git rev-parse
列出了两个或多个,git diff
仍然只选择一个,而不是产生警告或错误。
要手动执行此操作,只需在没有--all
的情况下运行git merge-base
,即可执行基本相同的操作3所以这就是我们应该做的。
找到合并基础后,git diff
现在将合并基础提交与右侧提交哈希(git rev-parse
输出的第二行)进行比较。因此,这也是我们应该做的
这就产生了我们编写的两步操作,然后让shell在别名中的一个命令行命令(运行两个Git命令)中执行。
1注意,在您的问题中您说:
git diff master...HEAD
。。。将master与我的分支进行比较(以GitHub PR的方式)
事实上,GitHub的比较并不是这样的,我不知道他们是如何生产的。但这——也就是说,三点语法——确实使用了merge基。
2最佳的定义有点棘手。图的最低共祖先有两个很好的图论定义,这两个定义都来自Bender、Michael a和Farach Colton、Martín和Pemmasani、Giridhar和Skiena、Steven和Sumazin、Pavel在《算法杂志》上发表的一篇题为《树和有向无环图中的最低共祖》的论文:
定义1.设G=(V,E)为DAG,并设x,y∈V。设Gx,y是集合诱导的G的子图x和y的所有共同祖先。将SLCA(x,y)定义为out度的集合Gx、y中的0个节点(叶)。x和y的最低共同祖先是SLCA的元素(x,y)。
定义2.对于任何DAGG=(V,E),我们定义偏序集S=(V,≼)如下:元素i≼j当且仅当i=j或(i,j)在Gtr的传递闭包中。设SLAC(x,y)为最大元素集共同祖先集合的{z|z≼x∧zy}⊆V。x和y的最低共同祖先是SLAC的元素(x,y)。
3注意,这些可能会选择不同的合并基提交!但你无法控制git diff
选择哪一个,所以你不必太在意git merge-base
是否选择了不同的。
尝试
git diff --cached master
请参阅https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-emgitdiffmltoptionsgt--cachedltcommitgt-ltpathgt82308203
git diff master
、git diff --staged master
和git diff --cached master
将从master
以外的另一个分支给出相同的结果,它将考虑您的阶段性更改。
如果相反,您想要在不考虑阶段性更改的情况下区分,那么您必须使用git diff HEAD...master