G W*_*erg 1 git merge mercurial
我可以使用“移植”将我在特定提交中所做的相同更改从一个分支复制到另一个分支。我想做的是类似的事情,只是我想从分支复制所有更改。
也就是说,我从一个称为分支 A 的分支开始。我在分支 A 上创建了一个名为 feat1 的新分支,它添加了一个新功能。我在 feat1 中进行了多次提交,然后将其合并回分支 A。
我想将 feat1 分支中所做的所有更改合并到另一个分支(我将其称为分支 B)中。但我不想将分支 A 中的所有内容合并到分支 B 中,而只想将 feat1 分支过程中所做的更改合并。我认为我可以使用一系列移植来完成此任务,我的 feat1 分支中的每个提交都有一个移植,但我认为应该有更好的方法。是否有实现此目标的标准方法?
我目前正在使用 Mercurial,尽管我计划在某个时候过渡到 git,所以我想知道如何在任一系统中执行此操作。
(注意:这是交叉发布到git和Mercurial 的,所以我们需要一个涵盖两者的答案。我也很抱歉这太长了,但是有很多概念需要理解。如果这是 TL;DR,请跳过直到最后一部分,避免所有复制。但请注意,您必须向上阅读才能看到我在说什么。)
\n\nGit 和 Mercurial 的合并方式相同。1 特别是,分支名称并不重要。重要的不是名字,而是名字。重要的是提交图。
\n\n1有很多微小的差异,甚至有一些相对较大的差异,但我在这里的意思是总体思路是相同的。Git 还支持 Git 所谓的快进合并的概念,这根本不是合并。这个概念在 Mercurial 存储库中没有任何意义,并且根本不可能发生:Mercurial 不执行快进。跑步hg merge <commit-specifier>大致相当于跑步git merge --no-ff <commit-specifier>。
在 Git 和 Mercurial 中,每个提交都以某种方式记录其父提交的 ID,或者在合并提交的情况下\xe2\x80\x94\xe2\x80\x94 其父提交(对于 Mercurial 来说正好是两个,两个或多个)对于 Git)。因为每个提交都有自己唯一的ID,并且每个提交都记录了它的父级或父级,所以我们可以画一个图\xe2\x80\x94它以树开始,这更简单,我将在此处说明\xe2\ x80\x94这些提交:
\n\nA <--B <--C\nRun Code Online (Sandbox Code Playgroud)\n\n这里我们有一个简单的存储库,其中只有三个提交。提交C是最新的。它有一个单亲B,所以C记住了 的 ID B。 B,同样,记住 的 ID A。(请注意,A不知道B并且B不知道C:箭头仅向后移动。)
任何提交的任何内容都无法更改(这在 Git 中更是如此,但在 Mercurial 中也几乎如此)。所以我们不需要把箭头画成箭头;我们可以只使用连接线,只要我们记住 VCS 必须向后跟随它们即可。这很方便,因为我们现在可能决定添加一个新的提交D,其父级B不是C:
A--B--C\n \\\n D\nRun Code Online (Sandbox Code Playgroud)\n\n这就是 Mercurial 和 Git 的分歧点:在 Mercurial 中,我们经常通过将提交D放在不同的分支上来(有意)实现此结果。Mercurial 记录了进行任何提交的分支的实际名称,因此从那时起,Mercurial 就知道D是在分支上dev还是其他地方。Git 使用完全不同的方案:Git 不知道也不关心提交是在哪里进行的;Git 通过让名称记住最后一次提交来查找提交,并按照两个系统可以遵循的内部箭头连接提交的方式向后工作。
因此,在 Mercurial 中,我们可能会说:
\n\ndefault: A--B--C\n \\\n dev: D\nRun Code Online (Sandbox Code Playgroud)\n\n每个提交都位于与我们提交提交的行一致的分支上。但在 Git 中,我们可能会改用这样的方式绘制它们:
\n\n C <-- master\n /\nA--B\n \\\n D <-- dev\nRun Code Online (Sandbox Code Playgroud)\n\n也就是说,名称master标识 commit C,并且名称dev标识 commit D。承诺A并B现在在两者上分支上。
此时,Mercurial 用户可能会宣称 Git 简直就是疯子,许多人都会同意他们的观点。但 Mercurial 用户在这里并没有摆脱困境,因为我们也可以在 Mercurial 中执行此操作:我们可以D在分支上进行提交default,但仍然将B其作为其父级。那是:
A--B--C\ndefault: \\\n D\nRun Code Online (Sandbox Code Playgroud)\n\n在 Mercurial 中也有效!而在现代的 Mercurial 中,我们可以设置两个书签来记住 commitC和D,这与 Git 中的情况相同。
因此,在这两个系统中了解提交图的工作原理非常重要。由于 Mercurial 的分支系统更加严格,因此您通常可以在没有这种了解的情况下逃脱\xe2\x80\x94,但最终,您确实需要了解它。
\n\n现在,让我们看看您的文本描述,并绘制图表,因为图表才是最重要的。
\n\n\n\n\n我从一个称为分支 A 的分支开始。我在分支 A 上创建了一个名为 feat1 的新分支,它添加了一个新功能。我在 feat1 中进行了多次提交,然后将其合并回分支 A。
\n\n我想将 feat1 分支中所做的所有更改合并到另一个分支(我将其称为分支 B)中。但我不想将分支 A 中的所有内容合并到分支 B 中,而只想将 feat1 过程中所做的更改合并分支。我认为我可以使用一系列移植来完成此任务,我的 feat1 分支中的每个提交都有一个移植,但我认为应该有更好的方法。是否有实现此目标的标准方法?
\n
这里缺少一块,因为在 Mercurial 中,您几乎肯定是从 开始的default,就像 Git 用户从 开始一样master。让我们在 上绘制至少一个提交default,然后担心branchA和feat1和branchB。
default: A\n \\\nbranchA: B--C-...--M\n \\ /\nfeat1: D--E\nRun Code Online (Sandbox Code Playgroud)\n\n提交M是您的合并,通过运行进行hg checkout branchA; hg merge feat1.
合并的工作方式是,它不是查看分支名称,而是查看提交图。前M存在之前,图表显示:
...--C--...\n \\\n D--E\nRun Code Online (Sandbox Code Playgroud)\n\n我把 放在...这里是因为可能会有一些提交或之后的提交C,例如F或F-G-H或其他什么。让我们假设有。对 Mercurial 的影响不如对 Git 的影响重要,因为在 Git 中,这会迫使Git 进行真正的合并而不是快进非合并,但它在这里有助于说明合并的工作原理。让我们只使用一次提交F:
...--C--F\n \\\n D--E\nRun Code Online (Sandbox Code Playgroud)\n\n为了实现合并,两个VCS 此时都会查看图表。它们采用当前提交\xe2\x80\x94(在本例中为F\xe2\x80\x94)和请求的(“其他”或--theirs)提交(在本例中为E\xe2\x80\x94),并在图表中向后搜索最佳共同祖先提交。从图中可以明显看出该提交:它是提交C!
因此,此时,两个 VCS 执行的逻辑等效操作为:
\n\nC与 commit F,找出我们更改了什么;C与 commit E,找出它们更改了什么;C;如果一切顺利,请M使用F第一个父级和E第二个父级进行新的合并提交:
...--C--F---M\n \\ /\n D--E\nRun Code Online (Sandbox Code Playgroud)现在您已经了解了这一点,让我们继续阅读文本的下一部分:
\n\n\n\n\n我想将 feat1 分支中所做的所有更改合并到另一个分支(我将其称为分支 B)中。
\n
这个新分支branchB并不是凭空出现的。你必须创建它。这里的方法在 Git 和 Mercurial 中有点不同,但最终的结果是一样的……嗯,基本上是一样的。
在 Git 中,您现在选择存在的全部提交集中的一个(A通过F加上合并M),并选择它作为名称branchB将指向的提交。然后运行git branch branchB <commit-specifier>,名称branchB现在指向该提交。
在 Mercurial 中,您现在检查一些现有的提交:hg update -r <rev>或类似的。选择其中一个提交,也许A在这种情况下我们可以只使用hg update default. 然后运行hg branch branchB; hg commit进行新的提交,这将创建branchB.
在这两个系统中,如果该分支上没有提交,则该分支不能存在;但在 Git 中,任何提交都可以在多个分支上,因此我们可以branchB通过将其指向 commit 来使其存在A。在 Mercurial 中,一次提交仅在一个分支上,因此我们需要进行一次新的提交才能branchB使其存在。所以图片有点不同。
这是 Git 图片:
\n\nA <-- master, branchB\n \\\n B--C--F---M <-- branchA\n \\ /\n D--E <-- feat1\nRun Code Online (Sandbox Code Playgroud)\n\n这是 Mercurial 的:
\n\nbranchB: N\n /\ndefault: A\n \\\nbranchA: B--C--F---M\n \\ /\nfeat1: D--E\nRun Code Online (Sandbox Code Playgroud)\n\n此时,我们可以在 Git 中进行无偿提交,只是为了使图片匹配(除了由于 Git 分支名称move,我们将它们放在右侧,箭头指向图表;Mercurial 分支名称是固定的,所以我们可以让他们坐在左边并永远标记他们的提交,只要我们的图表不会变得很大)。Git 将写入新的提交N2并移动当前分支名称以指向该新的提交。
\n\n\n但我不想将分支 A 中的所有内容合并到分支 B 中,只想将 feat1 分支过程中所做的更改合并。我认为我可以使用一系列移植来完成此任务,我的 feat1 分支中的每个提交都有一个移植,但我认为应该有更好的方法。是否有实现此目标的标准方法?
\n
在 Git 和 Mercurial 中,您实际上必须复制提交。该图是提交;提交D和E您所做的feat1都停留在原处。分支名称是否移动(Git)或固定(Mercurial)并不重要,因为提交本身是不可变的。
在 Mercurial 中,副本提交是正确的hg graft。您可以使用单个命令来复制所有提交feat1。由于提交特定于一个特定分支,因此可以轻松复制所有提交feat1。
在 Git 中,该git cherry-pick命令复制提交。您也可以使用单个命令在此处复制所需的所有提交,但因为提交D-E位于两个分支 \xe2\x80\x94 上,所以它们位于两个分支上feat1 , branchA现在 \xe2\x80\x94 您需要使用更复杂的说明符。Git\ 的方便说明符\xe2\x80\x94 可能不是那么方便\xe2\x80\x94 使用图形操作:feat1~2..feat1会指定提交D和E,在这种情况下,就像 一样branchA^..feat1。Mercurial 也可以做到这一点,但在 Mercurial 中您不需要经常这样做。
2请注意,我们必须git checkout branchB并且可能使用git commit --allow-empty. 该--allow-empty标志告诉 Git 它应该进行新的提交,即使索引\xe2\x80\x94 这是一个 Git 特定的概念;Mercurial 没有索引\xe2\x80\x94 与当前提交完全匹配。该git checkout步骤的副作用是将名称附加HEAD到分支名称,以便 Git 知道哪个名称是当前分支。(Mercurial 将当前分支名称记录在一个名为 dirstate 的隐藏数据结构中,与 Git 的索引不同,您不需要知道该数据结构。)
由于您的目标是避免使用or hg graft,git cherry-pick让我们考虑如何实现这一目标。请注意,这并不总是可能的,即使有可能,有时也需要大量的远见和规划。但关键是:Git 和 Mercurial 都使用图表中的合并基础来合并各种功能。
让我们看上面的最终 Mercurial 图,其中两个feat1提交必须被嫁接才能让它们影响 commit N:
branchB: N\n /\ndefault: A\n \\\nbranchA: B--C--F---M\n \\ /\nfeat1: D--E\nRun Code Online (Sandbox Code Playgroud)\n\n现在,假设,当我们去创建分支feat1,然后实现该功能时,我们事先知道我们希望能够运行hg update branchA; hg merge feat1; hg update branchB; hg merge feat1。
为了使这项工作有效,我们必须在图中的较早提交feat1之后进行第一次提交。具体来说,它必须发生在某个提交之后,该提交是和的提示的祖先。Commit离 的道路太远了:它不是 commit on的祖先。branchA branchBCbranchBNbranchA
理想的提交是branchA和branchB第一次回到一起的提交。也就是说,Git 将通过运行来拼写出其哈希 ID 的提交git merge-base branchA branchB:最近的(以图形术语表示)提交,它是两个提示提交的祖先。
请注意,因为我们还没有开始feat1,所以此时我们实际上必须拥有的只是:
branchB: N\n /\ndefault: A\n \\\nbranchA: B--C\nRun Code Online (Sandbox Code Playgroud)\n\nMercurial 没有特别方便的工具来查找所需的哈希 ID,3但由于 Mercurial 提交牢固地固定在其分支上,因此通常非常明显。在本例中,这就是 commit A,这是 上的最后一次提交default。所以我们可以:
hg update default\nhg branch feat1\nRun Code Online (Sandbox Code Playgroud)\n\n然后编写并提交我们的代码:
\n\nbranchB: N\n /\ndefault: A\n |\\\nfeat1: | D--E\n \\\nbranchA: B--C\nRun Code Online (Sandbox Code Playgroud)\n\n同时提交像以前一样F进行branchA,所以我们hg update branchB发现现在有:
branchB: N\n /\ndefault: A\n |\\\nfeat1: | D--E\n \\\nbranchA: B--C--F\nRun Code Online (Sandbox Code Playgroud)\n\n我们运行hg merge feat1并得到:
branchB: N\n /\ndefault: A\n |\\\nfeat1: | D------E\n \\ \\\nbranchA: B--C--F--M\nRun Code Online (Sandbox Code Playgroud)\n\n其中第一个父级M是F,第二个父级是E,就像以前一样。但现在我们可以了hg update branchB; hg merge feat1。N和的合并基础E是 commit A;Mercurial 进行比较A,N看看我们做了什么,A看看E他们做了什么。Mercurial 构建新的合并提交O并提交它:
branchB: N----------O\n / /\ndefault: A /\n |\\ /\nfeat1: | D------E\n \\ \\\nbranchA: B--C--F--M\nRun Code Online (Sandbox Code Playgroud)\n\n除了使用不同的命令以及分支标签本身移动这一事实之外,Git 中的过程是相同的。
\n\n3-r您可以使用选项和面向图形的修订说明符找到提交。从数字上来说,您希望最后一次提交满足ancestor(tip-of-branchA) & ancestor(tip-of-branchB). 更新该提交,然后创建feat1提交。
在 Git 中,要查找哈希值,只需运行git merge-base branchA branchB. 找到哈希后,将新的分支名称指向该哈希 ID,检查该分支,然后开始提交。
| 归档时间: |
|
| 查看次数: |
907 次 |
| 最近记录: |