什么是GitHub PR用于显示差异的git命令

时间:2018-02-01 19:37:01

标签: git github

我想使用命令行生成GitHub提供的相同差异。帮助页面将其描述为“头部分支的尖端与头部最后与基础分支同步的提交之间的比较”(https://help.github.com/articles/about-comparing-branches-in-pull-requests

如果PR要将分支a拉入分支b,那么什么是git diff命令行?

特别是我想知道为什么当a和b都在分支m上创建并且我将分支m上的后续更改合并到a和b时,更改将出现在PR中。

示例:为分支m,a和b提交图表(从下到上阅读)

b m a
4   5 merge branch m at commit 3 to branch a and b
|\ /| 
| 3 | commit on branch m
| | 2 create branch a off m at commit 0 and commit
1 | | create branch b off m at commit 0 and commit
 \|/
  0   start with branch m at commit 0

合并分公司负责人:

git rev-parse a
5

合并基础:

git merge-base b a
3

git命令显示了提交2中引入的差异:

git diff b...a

但是GitHub PR显示了提交2和3中引入的差异

3 个答案:

答案 0 :(得分:1)

编辑:我已使用本地命令和GitHub的实际输出对此进行了扩充,现在我无法解释在他们的拉取请求比较中显示的内容。这完全是胡说八道,就像他们在网页上展示的很多其他内容一样。 GitHub显示显示已经存在于目标分支中的提交,就好像合并将要进行实际上不会实现的更改一样。也就是说,对于"合并分支' a'进入b",唯一将添加的提交是那些尚未包含在' b'但GitHub的显示中显示已经提交的两个提交包含在' b'。

TL; DR

通常(但请参见长答案):

git diff $base...$head

$base$head来自下面的长答案。 修改:那就是应该显示的内容。事实证明,这不是他们实际显示的内容。

使用示例存储库

您在https://github.com/kenocrates/ex1设置了一个存储库,我克隆了它:

$ git clone https://github.com/kenocrates/ex1
...
$ cd ex1

要从中获取refs/pull引用,您可以修改配置,使其在部分内容中读取:

[remote "origin"]
        url = https://github.com/kenocrates/ex1
        fetch = +refs/heads/*:refs/remotes/origin/*
        fetch = +refs/pull/*/head:refs/remotes/origin/pr/*

我也做过。然后:

$ git fetch origin
From https://github.com/kenocrates/ex1
 * [new ref]         refs/pull/1/head -> origin/pr/1

我现在用来找到正确的合并基数的命令是:

$ git merge-base --all refs/remotes/origin/pr/1 refs/remotes/origin/b
587593749ee46806ed2c9fd06cf8b904bbce255a

因为通过合并拉取请求合并的提交的我的全名是refs/remotes/origin/pr/1,而我的全名是GitHub调用的"基地分支"是refs/remotes/origin/b。请注意,我们还可以使用原始哈希ID或更短的名称:

$ git rev-parse origin/pr/1
1cef243f9efe6e94c9926f7992efb6c093188b8c
$ git rev-parse origin/b
48728bc19480e0c1cc9e3a399634a5f389881c47

(省略refs/remotes/,如[{3}}中所述,将由Git通常用于解析名称的六步流程中的第五步假设。)

$ git merge-base --all 1cef243f9efe6e94c9926f7992efb6c093188b8c 48728bc19480e0c1cc9e3a399634a5f389881c47
587593749ee46806ed2c9fd06cf8b904bbce255a

因此可以通过以下方式获得正确的差异(完整,而不是一次提交)

$ git diff 587593749ee46806ed2c9fd06cf8b904bbce255a origin/b

或更短的:

$ git diff origin/pr/1...origin/b

其中任何一项输出:

diff --git a/file b/file
index 4fc2681..186222c 100644
--- a/file
+++ b/file
@@ -1,6 +1,7 @@
 Section A

 Section B
+line 1

 Section C
 line 1

这就是GitHub 应该显示的内容,因为它是合并的,以及如果我们接受拉取请求它会产生的效果。它们实际显示的是不同的。 Per the gitrevisions documentation,我们看到的是四个提交,其中两个已经包含在目标分支中:

1cef243f9efe6e94c9926f7992efb6c093188b8c
587593749ee46806ed2c9fd06cf8b904bbce255a
23c2ff68c02207a2f172090566d7b2c75b6f1c16
b21d3c4067261aa295319f177ad1629b5ae12818

这里是GitHub上存储库中的实际内容,包括拉取请求(虽然不是它的合并),以及我们正在使用这一事实导致的通常名称更改我们的存储库副本,我们还没有将它们设置为纯镜像。 (制作纯镜像会使我们自己的本地存储库无法用于执行任何工作。)

$ git log --all --decorate --oneline --graph
*   48728bc (origin/b) Merge branch 'm' into b
|\  
* | f365142 added line to section B
| | *   1cef243 (HEAD -> a, origin/pr/1, origin/a, origin/HEAD) Merge branch 'm' into a
| | |\  
| | |/  
| |/|   
| * |   5875937 (origin/m) Merge branch 'p' into m
| |\ \  
|/ / /  
| * | 23c2ff6 added line to section C
|/ /  
| * b21d3c4 added line to section A
|/  
* 1ff1a3c added file

这个图非常混乱且难以阅读,但是,如果我们实际上要做这个拟议的合并,那么这些是我们通过另一条腿添加的提交,这些提交尚未在{{1}上}:

origin/a

这就是我们在 local diff中看到的内容。由于某种原因,基础GitHub选择不正确。

让我们再试一次实验 - 让我们抓住GitHub做出的实际合并提交,看看它在$ git log --decorate --oneline --graph $(git merge-base origin/pr/1 origin/b)..origin/b * 48728bc (origin/b) Merge branch 'm' into b * f365142 added line to section B 中的外观:

git log --all --decorate --graph --oneline

这正是我们应该期待的:合并提交,其第一个父$ git config --add remote.origin.fetch '+refs/pull/*/merge:refs/remotes/origin/pr-merge/*' $ git fetch remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From https://github.com/kenocrates/ex1 * [new ref] refs/pull/1/merge -> origin/pr-merge/1 $ git log --all --decorate --graph --oneline * f53dc81 (origin/pr-merge/1) Merge 1cef243f9efe6e94c9926f7992efb6c093188b8c into 48728bc19480e0c1cc9e3a399634a5f389881c47 |\ | * 1cef243 (HEAD -> a, origin/pr/1, origin/a, origin/HEAD) Merge branch 'm' into a | |\ | * | b21d3c4 added line to section A * | | 48728bc (origin/b) Merge branch 'm' into b |\ \ \ | | |/ | |/| | * | 5875937 (origin/m) Merge branch 'p' into m | |\ \ | | |/ | |/| | | * 23c2ff6 added line to section C | |/ * | f365142 added line to section B |/ * 1ff1a3c added file origin/pr-merge/1^1又名48728bc19480e0c1cc9e3a399634a5f389881c47,其第二个父origin/borigin/pr-merge/1^2又名1cef243f9efe6e94c9926f7992efb6c093188b8cHEADaorigin/pr/1origin/a

答案[编辑:除了它不是GitHub实际使用的答案]嵌入在短语中:

  

“头部分支的尖端与头部最后与基部分支同步的提交之间的比较”

要理解(并重现)这个,我们必须定义每个术语。

分支(名称)的提示只是分支名称解析的提交。例如,要查找分支origin/HEAD的提示的哈希ID,您可以运行:

master

通常,对于大多数命令,使用分支名称与使用此分支提示哈希ID具有相同的效果,因此我们甚至不必在此处使用git rev-parse master 。 (Git中的这条规则有一些例外:有时名称不仅仅意味着原始哈希ID。)

接下来,我们有头部分支基本分支。在这里,定义这些术语的是GitHub,而不是Git。在这种情况下, head branch 是您执行拉取请求的分支,而基础是您正在说的&#34;进入&#34;:< em>请将feature-X拉入master 表示head = feature-X和base = master;所以

  

如果PR要将分支a拉入分支b ...

然后&#34;头&#34;是git rev-parse和&#34; base&#34;是a,因此您可以执行bhead=a并使用下面的base=b$head。 (对于paranoida,请获取相应的引用并使用$base或其哈希ID,如下所示。)

最后,我们有一个短语上次同步的提交...... 。这个短语部分由GitHub定义,因为Git没有使用该短语,但它实际上意味着(理想单一)提交是这两个提交的合并基础。合并基础是通过提交图定义的,因此您需要足够的图来查找它。整个图总是足够的,所以如果你有拉请求提交 - 其哈希存储在名为refs/heads/$N/head的引用中 - 和基本分支哈希,存储在refs/pull/N/head中 - 运行:

refs/heads/base

或更简单:

git merge-base --all refs/pull/$N/head refs/heads/$base

将生成合并库的哈希值。

(如果有多个,在运行git merge-base --all $head $base 时,将通过合并合并库来创建Git中的实际合并库。请注意git merge -s recursive是默认策略。 GitHub也会这样做,在什么条件下,我不知道。请注意,依靠-s recursive解析为$base是相当可靠的,但如果您使用相同的名称则会失败标签和分支!)

因此,diff来自合并库 - 上面的refs/heads/$base命令的哈希输出 - 到GitHub调用&#34;头部分支&#34;。您可以运行git merge-base命令并验证它是否只生成一个哈希:

git merge-base

或依赖于使用三个点的特殊hash=$(git merge-base $head $base) # slightly sloppy ... check that $hash is just one hash ... git diff $hash $head 语法:git diff表示: 1 找到A和B之间的合并基数;随机选择一个,并从那个运行git diff A...B到B.

这是上面的简短版本来自的地方。如上所述,如果你有一个使用相同名称的分支和标记,git diff$base可能会错误地解析标记而不是分支,所以如果你是偏执狂,请拼写出来完整的参考资料。

1 当存在多个合并基础时,$head中有一个长期但很小的错误,具有三点合并语法。多个合并基础非常罕见,您可能不在乎。

答案 1 :(得分:0)

diff位于目标分支和源分支之间:

git diff <destination> <source>

这将包括源上不在目标上的所有更改,反之亦然。如果您收到此消息,则您的功能分支已过期。您可以将目标合并到分支中,也可以将分支重新绑定到目标分支(取决于您的工作流程和个人倾向)。

答案 2 :(得分:0)

Github 现在有一个官方 CLI 工具,可以提供这个功能。 Just run gh pr diff [PrNumber]

更多信息请访问 https://cli.github.com/manual/gh_pr_diff