为什么 git 合并会在源分支创建一个提交?

4

我正在尝试将补丁分支中的更改合并到主分支中。下面是我在Github看到的信息:

kewp wants to merge 36 commits into patch/SEO_prices_pages from development

当我点击解决冲突按钮时,我看到的是这条消息。
Resolving conflicts between development and patch/SEO_prices_pages and committing changes development

为什么要将提交内容提交到development分支?我试图用开发分支的变更更新补丁分支...

2
你可以这样做,但那不是合并。听起来你想要挑选或变基。 - symlink
1
@symlink 这是不正确的。如果你将一个分支合并到主分支,那么该分支仍然保持不变。 - Schwern
1
“我正在尝试将补丁分支的更改合并到主分支中。” 如果您指的是“开发”,那么似乎您正在做相反的事情。 您正在从“开发”合并到补丁分支。 您能为patch/SEO_prices_pages和development显示git log --graph --decorate吗? 您能向我们展示您正在使用哪些特定命令或工具吗? - Schwern
@symlink 不完全正确,但我不确定“两个分支的父级”指的是什么。新提交是每个分支中的提交。如果它有任何后代,则这些提交可能是或可能不是分支中的提交。 - William Pursell
不确定如何发布“git log”,因为它非常长? - Karl Penzhorn
显示剩余7条评论
1个回答

7

这个问题存在一个基本的缺陷:

为什么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 checkoutgit 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来使用现有合并的树,但效果是一样的。

非常详细的回答,谢谢。我需要一些时间来消化它。我会再回来的。 - Karl Penzhorn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接