【问题标题】:How do merge and rebase work?合并和变基如何工作?
【发布时间】:2016-01-02 23:54:38
【问题描述】:

我试图了解合并和变基的作用,就数学中的集合操作而言。

下文中,“-”表示diff(类似于数学中取集差,但“A-B”表示A中不存在B,减去B不属于A),“+”表示patch(即在数学中采用不相交的联合。我之前没有使用过patch,所以我不确定)。

  1. 来自 Chacon 的 Pro Git,

    最近的共同祖先C2在这方面的作用是什么 合并?

    C5 = (C4-C2) + (C3-C2) + C2 对吗?

  2. 对于变基而不是合并:

    C4' = (C4-C2) + C3 对吗?

    那么,merge 的 C5 和 rebase 的 C4' 内容一样吗?

  3. 来自 Loeliger 的 Git 版本控制

    W'、X'、Y'、Z'是如何生成的?

【问题讨论】:

标签: git git-merge git-rebase


【解决方案1】:

您无法使用“差异”和“补丁”语义真正正确地解释 Git。原因是 Git 不跟踪更改。它跟踪内容。当您有一个提交A 和一个父提交B 时,对于Git,A 不是BA 之间的区别,即从B 到@987654328 所需的更改@,而是A的实际内容。您可以自行提交,此时您拥有重建存储库的一切。

因此,我不会遵循您的“提交算术”,而是尝试用文字解释每种情况。

  1. C5 是与两个父级 C3 和 C4 的合并提交,它们都将 C2 作为父级。所以假设没有冲突,并且Git能够自己解决整个合并,那么C5将包含以下两个等价表达式表达的内容:

    • 已应用补丁的 C3 可将您从 C2 带到 C4。
    • C4 应用了可以让您从 C2 到 C3 的补丁。

    所以在某种程度上,你可以用你的公式来制定它。

  2. 变基基本上会让你处于完全相同的境地。唯一的区别是它实际上重写了它变基的提交,将它们的更改(补丁)重新应用到另一个提交。所以结果 C4 将与 (1) 中的 C5 具有相同的内容。

    对于你的“算术”,我通常会说(C3-C2) + C2 = C3。所以这两个公式在这里也是等价的。

  3. 如上所述,rebase 只是将提交补丁重新应用到另一个父级。 Git 正在编写包含与原始提交相同的 change 的新提交,但这些新提交将应用于不同的父级。

    因此,如果您想为此获取公式,它可能看起来像这样:

    W' = E  + (W - B)        (the patch from B to W, applied on E)
    X' = W' + (X - W)        (the patch from W to X, applied on W')
    Y' = X' + (Y - X)        (the patch from X to Y, applied on X')
    Z' = Y' + (Z - Y)        (the patch from Y to Z, applied on Y')
    

    因此W 的基础更改为E 而不是B,并且所有后续提交都只是更新为跟进新的提交W'

    但同样,Z' 的结果与合并EZ 的合并提交具有相同的内容

这一切都给我们留下了一个问题:如果合并和变基都产生相同的内容,那么它们之间有什么区别?由于您不会以两种方式丢失提交(rebase 将创建新的提交对象,但这些对象将保留所有原始信息),所以基本上都是为了改变历史的样子:

合并创建一个合并提交,可以清楚地查看历史分歧的位置(创建分支的位置)以及再次合并的位置。这很好,因为您可以遵循确切的发展。但它也可能变得混乱,特别是如果您有多个并发分支线最终合并多次。

另一方面,重新定基会使历史变平。它“伪造”了它,所以一切都是线性发展,一切都顺理成章地接踵而至。由于您总是重新定位一个完整的分支,因此您将相关的提交保存在一起,因此您仍然可以看到彼此属于什么,但您会丢失任何分支信息。此外,您正在创建新的提交对象,这将破坏已经知道这些提交对象的每个人的存储库(这就是为什么您永远不应该对已发布的提交进行变基)。

这两种方式各有利弊。这在很大程度上取决于存储库工作流程和个人偏好。

【讨论】:

  • 我赞成这个特别的答案,也赞成helmbert's。当我添加两项时,我希望避免在这里进行大的评论讨论:(1)helmbert 是正确的,merge 找到(或“a”)最低的共同祖先节点。如果有多个 LCA,则操作取决于合并策略;默认的“递归”采用两个 LCA,将它们合并以产生一个新的基础,然后迭代将该基础与每个剩余的基础合并,一次一个。 (2) Rebase 可以看成(和--interactive)内部的一系列git cherry-pick操作。
  • Rebase 可能是一系列 git-cherry-pick 操作,如果您使用 git rebase --mergegit rebase --interactive 调用它。不过,默认情况下,rebase 会创建一系列补丁,然后应用它们。
【解决方案2】:

最近的共同祖先 C2 在这次合并中的作用是什么?

C5 = (C4-C2) + (C3-C2) + C2 对吗?

最近的共同祖先用于确定在master 中尚未出现的分支experiment(在您的示例中)中进行了哪些更改(参数是所有提交可以从experiment 分支的分支尖端最后一个共同祖先还不是主分支的一部分。

当将experiment 分支合并到mastergit merge experiment 时,Git 然后使用共同祖先和要合并分支的分支尖端(在本例中为experiment)之间的增量来计算在合并提交中应用哪些更改。

在您为问题选择的集合符号中,我将其表示为 C5 = C3 + (C4 - C2)。

C4' = (C4-C2) + C3 对吗?

那么,merge 中的 C5 和 rebase 中的 C4' 内容相同吗?

这两个假设都是正确的。请记住,git rebase 保持提交的增量不变,因此在您的示例中,表达式 (C4' - C3) = (C4 - C2) 为真。

W'、X'、Y'、Z'是如何生成的?

如前所述,git rebase 保留了提交的增量。这意味着(例如)W 与其父提交 B(或 W - B,您也可以使用 @ 生成)之间的差异987654330@)。

然后,rebase 只需将相同的增量应用于新的父提交;所以在你的例子中 (W' - E) = (W - B) (相应地对于 XYZ)。

【讨论】:

  • 在最后一个例子中,rebase 的工作方式是:W' = (W-B) + E, and X' = (X-B) + E or X' = (X-B) +W'?
【解决方案3】:
  1. 最近的共同祖先 C2 在这次合并中的作用是什么?

c2c3+c4 有 2 个父母

变基而不是合并的全部意义在于,您将有一条“直线”,而不是途中的所有那些叉子(蛇形)。

您将工作放在分支的顶部,作为信号线,在合并中,您将至少有 2 个父级(提交)到合并点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-29
    • 2012-04-14
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 2019-01-21
    • 1970-01-01
    相关资源
    最近更新 更多