ATL*_*DEV 2 git github rebase git-merge
假设您在处理功能分支时从远程存储库中提取数据,并将新提交添加到主分支中。
您完成功能分支上的工作,提交更改,然后将其合并/变基到主分支。功能提交插入到 master 分支的哪个位置,是在拉取的提交之前还是之后?如果您从未执行过拉动操作,它们会插入哪里?会得到同样的结果吗?
另外,如果将 master 合并/变基到功能分支而不是相反,会发生什么?如果您在拉取之前或之后合并/变基,master 会插入到功能分支的哪里?会得到同样的结果吗?它的历史看起来与前面提到的将功能合并/变基到主版本中会有什么不同吗?
更简洁地说,合并的方向和/或合并的顺序是否会对合并结果产生不同的结果?我怀疑这并不重要。
旁白:对于使用 Git 的人来说,这个问题可能很容易回答,但是有数百甚至数千的文档和教程资源,我从未见过它被直接解决。如果不知道答案,Git 可能会非常难以理解。
因为操作顺序在某些 Git 概念和一般编程中很重要,所以它可能会导致某人得出这样的结论:它会产生影响。此外,我们通常倾向于认为修订是按时间顺序发生的,并期望 Git 也以同样的方式工作。用户界面和命令行也通过暗示操作顺序来体现这种看法。
其他版本跟踪工具在抽象这种认知腐蚀性操作方面做得更好。
\n\n\n此外,我们通常倾向于认为修订是按时间顺序发生的,并期望 Git 也以同样的方式工作。
\n
这是一个可怕的错误。Git 按图形顺序工作,而不是按时间顺序。当图形顺序按时间顺序\xe2\x80\x94(有时但并非总是\xe2\x80\x94)时,它可以正常工作。
\n\n(我什至不打算在这里讨论变基。有关变基的正确讨论,请参阅之前的帖子。我只是将重点放在合并中的对称性以及可以显示接缝的位置。)
\n\n\n\n\n更简洁地说,合并的方向和/或合并的顺序是否会对合并结果产生不同的结果?我怀疑这并不重要。
\n
答案既是否定的,又是肯定的。
\n\n要理解为什么是这个答案,您需要了解有关 Git 的几件事。
\n\n首先,让我们考虑一下 Git 如何对待提交,与有多少其他人这样做相比(大多数其他人?所有其他人?必须至少有一个其他 VCS 也这样做......)。在版本控制系统中,在分支中进行提交的过程会将新的提交添加到分支中;但在 Git 中,一旦提交就保存的分支集可以而且确实是动态变化的。也就是说,提交一旦完成,就独立于任何分支,并且如果您愿意,可以在任何分支上进行提交。
\n\n例如,在 Mercurial 中,这是完全不可能的。虽然 Mercurial 在许多方面与 Git 非常相似,但提交的存在需要其分支的存在。提交是在该分支上进行的,并且永远是该分支的一部分。这是保存提交的唯一分支:提交永久附加到其分支。
\n\n在 Git 和 Mercurial 中,每个提交都有一个唯一的 ID,如果是普通提交,则每个提交都会存储其父(单个)提交的唯一 ID 。通过遵循这个向后看的链条,从最后一次提交开始向后工作,我们可以找到分支上提交的历史记录:
\n\n... <-grandparent <-parent <-child\nRun Code Online (Sandbox Code Playgroud)\n\n在 Mercurial 中,这些提交永远位于其分支上,因此我们可以将分支名称写在左侧:
\n\nbranch-A: ...--I--J\nRun Code Online (Sandbox Code Playgroud)\n\n在 Mercurial 中查找最新提交很容易,因为提交在本地存储库中有一个简单的序列号(以及用于在存储库之间共享的唯一哈希 ID)。
\n\n但在 Git 中,分支名称是可以移动的。在提交J存在之前,名称branch-A存储提交的原始哈希IDI:
...--H--I <-- branch-A\nRun Code Online (Sandbox Code Playgroud)\n\n当进行新的提交时,Git 只需将新提交的哈希 ID 写入分支名称,以便分支指向新的提交:
\n\n...--H--I--J <-- branch-A\nRun Code Online (Sandbox Code Playgroud)\n\n在版本控制中,merging,动词,或者合并一些提交,是一个过程。结果\xe2\x80\x94至少在Git和Mercurial中\xe2\x80\x94是一个合并提交,其中我们使用单词merge作为修饰提交的形容词,或者简称为合并(名词)。
\n\n合并提交之所以成为合并提交是因为它们有两个父级,将两条开发线结合在一起:
\n\n...--I--J\n \\\n M\n /\n...--K--L\nRun Code Online (Sandbox Code Playgroud)\n\n再次强调,Git 和 Mercurial 基本上是相同的,但有一个非常重要的区别:在 Mercurial 中,合并提交专门在一个分支上,即您运行时所在的分支hg merge。提交\xe2\x80\x94(包括合并提交\xe2\x80\x94)永久附加到其分支。一旦合并过程完成并且合并提交存在,该合并提交就位于该一个分支上。在 Git 中,合并提交会进入当前分支,但是因为我们可以移动名称所以在某种意义上这并不重要。
但我们必须同时关注两个部分:动词部分,to merge,然后仔细观察名词部分,a merge。(旁白:Git 还允许合并提交有两个以上的父级,而 Mercurial 将每次合并限制为两个父级。这些时髦的合并没有什么,Git 称之为章鱼合并)可以做的事情,是一系列的合并提交做不到的。成对合并,但章鱼合并在某些情况下可以更清楚地显示意图。)
\n\n举例来说,假设branch-A和branch-B在合并之前是这样的:
...--I--J <-- branch-A\n\n...--K--L <-- branch-B\nRun Code Online (Sandbox Code Playgroud)\n\n在我们合并它们之前,我们必须追溯到足够远的历史以找到合并基础,即这两条开发路线分歧的共同提交。(这在 Mercurial 中也是如此。)所以让我们补充一点:
\n\n I--J <-- branch-A\n /\n...--H\n \\\n K--L <-- branch-B\nRun Code Online (Sandbox Code Playgroud)\n\n在这里,commitH是共同的起点。在 Git 中,提交同时在两个分支H上进行(但在 Mercurial 中则不然)。尽管如此,两个版本控制系统都以几乎相同的方式执行合并过程:首先选择一个提交进行签出,例如使用或进行提交。然后,您使用命令的动词:或选择另一个提交。VCS 找到适当的合并基础,即 commit ,并将 的内容与 的内容进行比较以查看更改的内容:Jgit checkout branch-Ahg update branch-Amergegit merge branch-Bhg merge branch-BHHJ
git diff --find-renames <hash-of-H> <hash-of-J> # what we changed\nRun Code Online (Sandbox Code Playgroud)\n\n并重复相同的比较以找出它们更改的内容:
\n\ngit diff --find-renames <hash-of-H> <hash-of-L> # what they changed\nRun Code Online (Sandbox Code Playgroud)\n\nGit 将这些更改合并为一个大的“所有更改”,将这些更改应用到基础(提交H),并进行新的提交。
(Mercurial 的做法大致相同,但在 Mercurial 如何了解重命名方面存在一些非常重要的差异。只有当您自合并基础以来重命名了某些文件时,这些差异才重要。如果您重命名了文件,并且存在重命名/重命名冲突,合并顺序变得非常重要。但我们假设不存在重命名问题。)
\n\n这里最有趣的事情是您可以控制更改的组合方式。在 Git 中,您可以通过合并策略和扩展策略参数来完成此操作;在 Mercurial 中,您可以通过所选的合并工具来执行此操作。如果您使用“我们的”策略/工具,VCS 完全忽略它们的更改:组合的更改只是您的更改,因此 VCS 使用与当前提交中相同的代码进行新的合并提交。在这里,合并顺序显然很重要:如果我们忽略他们的更改,我们最好确定谁是“我们”!
\n\n即使没有“我们的”策略,您的更改和他们的更改之间也可能存在冲突。如果是这样,Git 和 Mercurial 都可以被告知:更喜欢我的或更喜欢他们的。这些会产生不同的结果,因此合并顺序再次很重要。当然,此时有一个对称的选择:选择我的或选择他们的。如果你交换角色,你可以交换选项\xe2\x80\x94,所以虽然顺序很重要,但它并不那么重要。
\n\n假设没有冲突,也没有我们/他们的特殊事件,然后运行git checkout branch-A; git merge branch-B. 如果一切顺利,VCS 会进行合并提交M与其两个父级进行合并提交:
I--J\n / \\\n...--H M <-- branch-A\n \\ /\n K--L <-- branch-B\nRun Code Online (Sandbox Code Playgroud)\n\n在 Mercurial 中,合并顺序在这里很重要,因为一旦提交M,它就会永远粘在它的分支上。但 Git 允许我们在事后移动分支名称。我们可以M这样制作,将其保留在原处,branch-A向后推一步再次指向J,然后向前移动branch-B以指向M,给出:
I--J <-- branch-A\n / \\\n...--H M <-- branch-B\n \\ /\n K--L\nRun Code Online (Sandbox Code Playgroud)\n\n这与我们这样做后得到的结果几乎相同git checkout branch-B; git merge branch-A。所以在这里,合并顺序似乎是无关紧要的(前提是动词部分没有任何问题)。但这里的图表缺少一些东西,那就是第一父母和非第一父母的概念!
在 Git 和 Mercurial 中,合并提交的第一个父级是“同一分支”父级。M也就是说,由于我们J在运行合并时处于提交状态,因此第一个父级M是 commit J。这L就是第二个父母。如果我们在 Git 中移动分支标签,我们就可以知道我们已经这样做了,因为第一个和第二个父级仍将采用其他顺序。
所以我们在这里看到,对于 Git,即使 to merge动词没有顺序问题,生成的合并提交(形容词或名词)也有顺序。由您(用户)决定该订单对您是否重要。如果不是,则对称性可用。如果提交父顺序很重要,那么合并的方向/顺序在 Git 中与在 Mercurial 中一样重要。
\n\n在任何基于图形的版本控制系统中\xe2\x80\x94,因此在 Git 和 Mercurial\xe2\x80\x94 中,图形就是历史。特别是在 Git 中,不存在文件历史记录这样的东西;该图不仅是主要历史,而且是唯一的历史。(Mercurial 以更传统的方式存储文件数据,因此既有提交历史记录,也有有文件历史记录,但无论如何,如果没有提交历史记录,文件历史记录就没有用。)
\n\n为了能够理解 Git 的作用及其原因,您需要了解足够的图论来在城市中驾驶或乘坐公共交通工具。这并不是那么多\xe2\x80\x94,但是如果你没有意识到 Git 有这些单向铁轨/街道/桥梁,从最后一次提交回到第一次提交,很多事情都会发生没有意义。Git 从末尾开始,并向后工作到开头。分支名称可让您进入图形的分支末端。其他一切都是提交图的函数。
\n| 归档时间: |
|
| 查看次数: |
1115 次 |
| 最近记录: |