0

我开始通过使用 git hub for Mac 来掌握 git hub 的窍门。我注意到合并分支选项允许您将某个分支 A 合并到某个分支 B 中。您也可以用另一种方式将分支 B 合并到某个分支 A 中。有什么区别?我从 git hub 文档中找到了这个,但它没有解释我想知道的内容。

在此处输入图像描述

4

3 回答 3

2

值得补充的是,git 的分支标签并不存储在提交本身中。

在进入其余部分之前,让我们在内部记下什么是提交。这是一个描述(以复杂的方式)整个工作树1的存储版本的条目,以及:

  • 作者和提交者(姓名和电子邮件地址)
  • 两个时间戳(作者和提交者,再次)
  • 零个或多个“父提交”

“父提交”是您遵循分支历史记录的方式。“常规”提交有一个父提交,即派生新工作树的先前提交。“合并”提交至少有两个父节点,而“根”提交没有父节点:它的树是从无到有的。

Git 有很多命名任何单个提交的方法。“真实姓名”是 SHA-1 哈希,从9e17c4e问题中的图像开始。该名称永远不会改变,事实上,它是提交的全部内容的加密强校验和。2

但是,这些 SHA-1 值很难输入(如果需要,我会尝试剪切和粘贴)。所以通常有另一种方式来指代提交。Git 提供了“分支”和“标签”名称作为给它们一个有意义的名称的一种方式。git branchandgit tag命令可以为现有提交创建一个新的“参考名称” 。分支只是存储在refs/heads/(例如refs/heads/master)中的引用,而标记是存储在refs/tags/. (通过将这些引用存储在这样的目录层次结构中,git 还允许在未来发明新类型的引用。事实上,已经有refs/notes/commits、 使用的git notes,当然还有存储在 中的“远程分支” refs/remotes/。)

“分支”和“标签”之间的主要(用户级)区别是标签应该是永久的,而分支应该移动。当您在“分支上”添加新提交时,分支(其中的引用)会refs/heads/自动移动。如果您告诉 git 进入 branch B,然后将新提交添加到 branch B,git 会进行新提交,然后将分支标签移动到新提交。如果你喜欢,你可以将分支标签想象成一种便签,git 会剥离并放在分支的新末端/尖端。

这正是纸杯蛋糕所说明的。如果您在“分支上B”并且您git merge A,则合并会向 B 添加一个新的提交3 — 特别是一个“合并提交”,其中包含两个父提交。然后,由于您“在”分支上,git 将分支标签移动到新提交。现在该名称B指的是合并提交,以及它的两个父级。

同样,如果您在分支上A并且您git merge B,则合并会添加一个新提交。它几乎是完全相同的提交——事实上,它具有完全相同的树,当然还有相同的作者和提交者。如果你以某种方式设法做到这一点——在平行宇宙中,也许:-)——在完全相同的时间,它甚至有相同的时间戳。但还有另一个区别。 尽管新提交将具有相同的两个父级,但它们的顺序是相反的。 当然,接下来 git 会移动分支标签,但这次的标签是A而不是B.

父顺序很重要。 也许不是很多,对于只有一个父级的“普通”提交,首先没有什么可订购的。但它们会影响 SHA-1 校验和,也会影响“父遍历”。当通过这个父提交链向后跟踪分支时,git 具有“第一个父”的概念,并且各种命令带有一个标志--first-parent, 来限制父提交。

请记住,分支标签实际上只标记了一个单一的提交:分支顶端的那个。该分支“上”的一组提交是根据需要通过跟随父级动态派生的。在有多个父级的合并提交中,分支可以包含其所有父级(同时向后跟随所有父级),或者对于简单的情况,仅包含第一个父级。这样,您可以在合并“引入”其他分支上的所有内容之前找到“分支上”的内容。

因此,这就是为什么(以及何时顺序在合并中很重要:当您想查看“在分支上”“合并之前”的内容时,您可以假设仅跟随第一个父级即可。

(不过这有一些问题。特别是,“快进”合并不会创建合并提交,它只是将分支标签“向前”拉出。而且,由于分支标签完全“在”密码校验和系统“之外” , 任何人都可以随时重新标记任何分支,并且在 git repo 中没有记录。“reflog”中有记录,但是 reflog 条目会随着时间的推移而过期,并且可以被清除:它们的意思是如果你不小心射中自己的脚,帮助你解决这个问题,而不是为了让你不要偷偷摸摸和混淆别人。但这已经太长了,我还没有到脚注......)


1从技术上讲,提交树记录了暂存索引的内容。这就是为什么您git addgit rm事物,将它们“放在舞台上”并按照您喜欢的方式排列树,然后才是git commit舞台索引。

2 “内容”涉及对各个部分进行 SHA-1 校验和,因此这部分是校验和的校验和。我不是密码学专家,但我知道这通常会降低密码强度。不过,这并不是为了安全。这些校验和是使 git 工作所必需的。如果两个不同的底层 git 对象(文件又名“blob”、树、带注释的标签和提交)总和为相同的值,git 将中断。

3这假设一个“非平凡的”合并不会导致“快进”。当您将“功能分支”合并回“主分支”并且主分支上没有发生任何工作时,会发生简单的合并:

A--B                master
    \
     C--D--E        feature

如果你只是git checkout master然后git merge feature,git 会注意到它master是 的“直接祖先” feature,因为它可以从提交Efeature所指的提交)开始并向后工作并到达提交B(当你“开启”时)重新“在分支主机上”)。在这种情况下,git merge feature将简单地将标签从提交上剥离B并将其粘贴到提交E上。

要强制进行“真正的”合并,您仍然git checkout master需要使用 branch master,然后使用,与两个父git merge --no-ff feature级创建一个新的合并提交:M

A--B---------M      master
    \       /
     C--D--E        feature

“第一个”父级,您现在可以通过写master^或来命名master~1,是 commit B。“第二个”父级,您现在可以用master^2或命名feature,是 commit E。请注意,master~2指的是提交A:波浪号编号遵循第一个父项。D(你可以用稍微尴尬ormaster^2^master^2~1命名commit

于 2013-09-02T19:18:45.590 回答
0

如果将分支 A 合并到 B 中,则只有 B 的分支会更新为新的子提交,而如果将分支 B 合并到 A 中,则只有 A 的分支会更新为新的子提交。

这是图表形式的样子,从未合并的分支开始:

---o---o A
    \
     o B

现在让我们将分支 A 合并到 B 中(使用git merge A):

---o---o A
    \   \
     o---o B 

看看 B 如何更新到新的子提交,而 A 保持不变?

现在让我们看看如果我们通过将 B 合并到 A (with git merge B) 反过来尝试会发生什么:

---o---o 
    \   \
     o---o A
     B

在这种情况下,A 已更新为新的子提交,而 B 保持不变。从这里,您还可以将 A 合并到 B 中,这将使 B 快进到与 A 相同的提交上:

---o---o 
    \   \
     o---o A, B

当使用^修订说明符时,您合并的顺序也可能会影响哪个父提交被认为是第一个与第二个:

  1. head^并且head^1都指子提交的第一个父级。
  2. head^2指子提交的第二个父级。

然而,就提交图而言,您合并的顺序没有区别,您最终仍然会得到一个指向两个父节点的子提交。

于 2013-09-02T14:47:26.187 回答
0

它们是有区别的。如果将 A 合并到 B 中,A 不变,B 现在将具有来自分支 A 的更改。

于 2013-09-02T14:55:46.237 回答