这个问题存在一个基本的缺陷:
为什么Git合并在源分支中创建提交
实际上并不是这样的。另一方面,GitHub可能有所不同……
在此需要明确定义术语,以及非常清楚地区分Git的功能和GitHub的功能。GitHub不等同于Git!GitHub将为您托管Git存储库,然后添加自己的功能,以使您使用他们的服务(而不是某些竞争对手的服务)。因此,当您使用GitHub的附加功能时,它们可能会执行Git本身不执行的操作。(如果您喜欢这些功能,则说明GitHub取得了成功:请记住,他们的目标是让您使用他们,并最终让您或其他人支付他们。)
设置
[GitHub告诉我] kewp想将36个提交合并到来自development的patch/SEO_prices_pages
[但存在合并冲突]
在这里假设kewp
是您,而development
是您从笔记本电脑/台式机/任何计算机发送到GitHub存储库的提交名称。然后,您使用GitHub的Web界面生成了一个GitHub Pull Request(PR),使用development
作为合并源分支,使用patch/SEO_price_pages
作为合并到目标分支,在GitHub上的单个存储库中进行操作(因此在此处没有涉及其他GitHub分支的内容)。
GitHub PR不是Git合并。但是当您单击“创建PR”按钮时,GitHub会尝试在存储在GitHub上的Git存储库中进行git merge
。此尝试可能成功或失败。如果成功,则可以在GitHub上的Git中找到所得到的合并提交;否则,没有该合并提交。
GitHub PR被分配一个编号,这个编号在GitHub上的存储库中是唯一的。(这是一个在该存储库中跨所有操作全局的编号空间:问题和PR共享编号空间,因此在还没有任何PR之前创建几十个问题的存储库中,第一个PR将具有两位数字。)这个编号成为Git的命名空间,格式为refs/pull/number
。在此命名空间中,GitHub会创建一个或两个名称:
refs/pull/number/head
指代你请求合并的分支的最新提交;
refs/pull/number/merge
(如果存在)指代GitHub成功创建的合并提交。
这个合并提交目前不在任何分支上, 如果它存在,则可以在GitHub上的Git代码库中找到它,并在merge
名称下找到它,但是在Git中“提交C在分支B上”的短语通常被解释为提交C可以从分支B的最新提交中到达。
(更多关于可达性的内容,请参阅像 Git 一样思考。)
同样地,如果合并尝试失败,则没有合并提交。由于您的情况存在合并冲突,因此存在一个 refs/pull/number/head
名称,但没有refs/pull/number/merge
名称。
如何在您的笔记本电脑上进行合并操作
当您在笔记本电脑上使用Git进行合并操作时,可能会像这样执行:
git checkout patch/SEO_prices_pages
git merge development
如果存在冲突,你的Git将在合并过程中
停止,部分完成的合并结果会被保存到Git的索引和工作树中。你可以使用Git留下的内容来
解决这些冲突,任何你想用的方式都可以。然后告诉Git你已经解决了冲突,并使用:
git merge --continue
恢复(并最终完成)合并。结果是一个新提交。新提交是基于你所检出的分支 patch/SEO_prices_pages
的末端,也就是这个分支上的。在你使用 git merge
命令时指定的分支 development
完全没有改变:该名称依旧持有与操作前相同的提交哈希值。
合并冲突解决过程发生在Git的索引中,但你的助手几乎肯定使用你的工作树中的文件:你计算机中非Git命令无法使用Git索引中的文件,因为它们以一种特殊的、压缩的Git专用格式存储。要使用索引文件,必须将其复制到其他地方(通常是使用 git checkout
或 git checkout-index
),然后在那里进行操作,然后将其复制回旧的索引文件(通常是使用 git add
)。
最终提交有两个父提交:它的第一个父提交是刚才做了更新的 patch/SEO_prices_pages
分支的末端提交;第二个父提交是当前仍然是 development
分支末端的提交。
GitHub 上的冲突解决如何工作
GitHub 上的Git存储库没有工作树。(每个存储库都有一个索引,但它只是遗留下来的东西。)这意味着通常的工具不可用。
GitHub 提供的至少目前是一种技巧。他们在某个地方保存了一些冲突信息——只有一个索引,可能有多个PR“在运行”,所以不清楚保存的位置(尽管Git有使用备用索引的功能,所以也许每个PR都有一个索引,但我猜测不会有)——但由于没有工作树,就没有地方编辑文件,然后再次使用 git add
。
相反,当你使用他们的冲突解决工具时,GitHub 会创建更多提交。他们可以通过推进 refs/pull/number/head
引用来做这些提交,最终他们会这样做。但根据他们自己的信息页面,他们将新提交(从你的PR分支的末端提交的文件开始,但进行更改,以使 git merge
没有冲突)添加到你的分支中。
在这种情况下,这意味着他们:
- 从您在GitHub仓库的
development
分支中的最新提交中提取文件;
- 允许您更改所有存在冲突的文件,以便如果将其作为新提交添加到您的
development
分支中,这些文件将不再与patch/SEO_prices_pages
分支中的最新提交中的文件产生合并冲突;
- 允许您将此次提交添加到您的GitHub仓库中。
这相当于您在笔记本电脑上执行以下操作:
git checkout development
(注:这假定你的
development
与 GitHub 的
development
匹配!)然后:
<edit each file that had conflicts>
<git add each such file>
git commit
git push origin development
除非您直接使用GitHub repo,否则不会在本地计算机上得到提交记录(目前为止)。
现在,您的 GitHub repo“development”有了这个新提交,GitHub 可以再次尝试git merge。这次合并将成功,并且他们将能够创建refs/pull/number/merge。
关于GitHub的其他注意事项:
当您实际上被允许在GitHub上进行合并时,您会看到绿色的“合并”按钮旁边有一个下拉箭头。通过下拉菜单,您将看到三个选项:
- 合并(merge)
- 变基并合并(rebase and merge)
- 合并后压缩(squash and merge)
第一个选项执行正常的每日合并操作——已经存在的GitHub合并操作很好地完成了这个任务,并像往常一样将其附加到目标分支。
第二个选项将每个要合并的提交复制到新的提交中,以扩展目标分支。即使要合并的提交已经在正确的位置(在图形上)可以进行快速转发,GitHub仍然会这样做。(我自己认为这很麻烦:他们应该直接快进。)
最后一个选项相当于运行“git checkout ; git merge --squash …”。它可能会通过在GitHub上运行git commit-tree来使用现有合并的树,但效果是一样的。
git log --graph --decorate
吗? 您能向我们展示您正在使用哪些特定命令或工具吗? - Schwern