如果我们从本地推送到远程提交,该提交是分支提交 X 和功能提交 Y 的合并,但在远程提交 Y 中不存在,它会工作吗?

If we push from local to remote a commit that is merge of a branch commit X and feature commit Y but in the remote commit Y not present, will it work?

我有这种情况,我想了解一下。

at T1 Local                         |       at T1 Remote:
------------------------------------------------------------------------
 E--F featureOne                    |        E--F featureOne
/                                   |       /
A--B--C--D branchOne                |       A--B--C--D branchOne
------------------------------------------------------------------------


at T2 Local                         |       at T2 Remote:
------------------------------------------------------------------------
merged branchOne into featureOne:   |
   E------F   featureOne            |    E--F featureOne    
  /        \                        |   /       
 /          G                       |   A--B--C--D branchOne                    
/          /                        |               
A--B--C----D branchOne              |                           
------------------------------------------------------------------------


at T3 Local                         |       at T3 Remote:
------------------------------------------------------------------------
merged featureOne into branchOne    |
after git pull branchOne            |
from Remote:                        |
   E------F                         |    E--F featureOne    
  /        \                        |   /       
 /          G-. featureOne          |   A--B--C--D branchOne                    
/          /   \                    |               
A--B--C----D-----H branchOne        |                           
------------------------------------------------------------------------

本地的H是featureOne的G和branchOne的D之间的提交合并。因为在远程我们没有 G,如果我将 branchOne 从本地推送到远程(origin/branchOne),在 T4 会发生什么?会合并吗? 不幸的是,我不能尝试这个,因为我先将 G 从本地 featureOne 推送到远程 featureOne,然后将 H 从本地 branchOne 推送到远程 branchOne。

git push 所做的是发送您的 提交 ——您拥有的部分或全部(或有时 none),其他一些 Git 没有——给有问题的其他 Git,然后要求其他 Git 将一些 他们的名字 设置为某个提交哈希 ID .

所以如果你有:

 E--F--G____
/       \   \
A--B--C--D--H

在您的存储库中——无论您的任何分支 names 指向何处——然后您告诉您的 Git 将提交 H 发送给其他 Git,你的 Git 将调用另一个 Git 并提供它提交 H.

(注意:我在这里假设提交 GFD 的合并。你的问题描述清楚地表明是这种情况。我会画我自己的图表有点不同;见下文。)

(如果您明确要求 Git 向他们发送提交 G,同样的推理也适用,但在这种特殊情况下,您的 Git 必须提供 G 在提供 H 并让他们接受之后,所以我们在这里只担心 H。另外,你问了 git push branchOne,你的名字 branchOne 指向提交 H.)

如果他们提交了 Hgit push 的这一部分就完成了。在这种情况下,他们没有提交 H,因此您的 Git 将提供提交 GD,因为它们是 H 的 parents。如果他们有这些提交,git push 的这一部分就完成了:您的 Git 发送提交 H(仅)。如果他们缺少一个或多个提交,您的 Git 提供 G and/or D 的 [剩余] parent,即 FC。如果他们有这些提交,git push 的这一部分就完成了:您的 Git 发送提交 H,加上提交 G and/or D 作为需要。

那么让我们看看他们有什么:

  E--F   <-- featureOne
 /
A--B--C--D   <-- branchOne

在这里,他们确实已经提交了 FD。所以你的 Git 出价 H 并且他们接受了;您的 Git 出价 G 并且他们接受了;您的 Git 报价 D 他们说 不,谢谢,我已经有那个了; 并且您的 Git 报价提交 F,并且他们说 不用了,谢谢,我已经有那个了。您的 Git 然后向他们发送提交 GH ,他们将其插入到他们的图表中。此时,它们现有的分支名称仍然分别指向提交 F (featureOne) 和 D (branchOne)。

此时,您的 Git 进入 git push 的最后阶段,即要求他们将他们的一些名称设置为一些提交。这是您为 git push 命令提供的精确拼写的来源:

git push branchOne

这不是正确的拼写,应该是:

git push origin branchOne

origin 部分是您告诉 Git 其他 Git 呼叫的方式。名称部分 branchOne 是此替代拼写的缩写:

git push origin branchOne:branchOne

冒号左侧 : 字符是您的 Git 找到起始 commit 的方式。在这种情况下,您的名字 branchOne 指向您的提交 H,因此这是您的 Git 开始提供的提交。

右边的冒号:字符提供分支名称你问他们在他们的 Git 存储库中设置 。通过在此处使用 branchOne,您最终会要求他们将名称 branchOne 设置为指向提交 H.

Will it merge?

git push 命令永远不会导致任何合并。它只是 发送你现有的提交,他们没有 ,然后 要求他们设置一些名称 (通常是分支名称)。他们 Git 接受或拒绝此设置分支名称的请求。

如果您正在推送到 GitHub,并且有人在 Git 上的 Git 存储库中的分支名称 branchOne 上启用了 "protected branch" 模式]Hub,他们会拒绝这种改名的尝试,因为分支是受保护的。

然而,在大多数情况下,他们会接受更改名称的请求。他们目前有自己的名字 branchOne 标识提交 D。提交 H 将提交 D 作为祖先——事实上,作为直接 parent——所以这个操作是 fast-forward(不是 fast-forward 合并 ,只是 fast-forward),这使得请求可以接受。

请注意,此 根本不会 要求他们设置自己的名字 featureOne。他们的名字 featureOne 将继续指向它之前指向的任何地方,例如提交 F。出于 Whosebug 发布的目的,我将其绘制为:

    E------F   <-- featureOne
   /        \
  /          G-.
 /          /   \
A--B--C----D-----H   <-- branchOne

例如。 (我已经将 G 向前移动,因为它显然是 DF 的合并,而你绘制它的方式看起来像 DCG 的合并。)

备注

如果使用冒号语法:

git push origin <commit-specifier>:<name>

您可以在此处的左侧使用任意提交说明符。这可以是分支名称、标签名称、名称 HEAD、相对名称 master~3、reflog 条目develop@{yesterday},甚至是原始哈希 ID。如果您使用左侧的原始哈希 ID,并且您希望其他 Git 创建一个新的 分支名称 ,则必须拼写全名:

git push origin a123456:refs/heads/new-branch

缩写如:

git push origin a123456:existing-branch

依赖于 他们的 Git 将名称 existing-branch 与他们现有的分支名称之一相匹配。

如果您使用 colon-free 表格,您 必须 使用一个名字,因为这个名字需要发送给另一个 Git。

您一次可以 git push 不止一件事:

git push origin master dev feature

或:

git push origin master~3:refs/heads/new-temp-branch dev a123456:refs/tags/v1.2

假设名称 new-temp-branchorigin 的 Git 中还不存在,最后一个版本使用一个相对名称 (master~3) 来创建一个新分支命名为 new-temp-branch。它使用一个 colon-free 表达式发送本地 dev 分支的提示提交,并请求他们相应地创建或更新他们的分支名称 dev。而且,它使用原始哈希 ID (a123456) 来请求他们创建或更新他们的 tag-name v1.2。标签名称更新通常是被禁止的,1 所以我们期望这会创建一个新的标签名称。


1在 1.8.2 或 1.8.4 之前的 Git 版本中存在错误,该错误意外允许使用与分支相同的规​​则进行非强制标签名称更新名称更新。