为什么Git合并在主分支上创建多个提交?

7

我正在处理两个分支,一个是master,另一个是newFeature。在开发“新功能”时,我向newFeature分支添加了多个提交。我的理解是,当合并分支时,git merge会在master上创建一个单一的提交,但是实际上,合并后,master现在具有newFeature上的完整提交历史记录。例如-

master (pre-merge):

    1=>2=>3

newVersion:

    1=>2=>3=>4=>5=>6

master (actual results of merge):

    1=>2=>3=>4=>5=>6

master (expected results of merge):

    1=>2=>3=>6

在合并时有没有办法从newVersion中删除中间提交,并且为什么合并不能按预期工作?


1
git merge 会创建一个新的提交,如果需要的话,但是它不会删除任何现有的提交。在您的情况下,它没有创建任何新的提交。它只是将 master 分支移动到已经被 newVersion 分支指向的提交。这是因为这两个分支实际上没有分叉,不需要新的提交。 - axiac
你可以将newVersion分支rebase到一个提交中。 - Tom
2个回答

9

需要注意的关键是,在您在newVersion上工作期间,没有对master进行任何更改。在这些情况下,Git默认使用“快进”合并,可以将newVersion中的所有新提交附加到master上最近的提交上(这不会分离在newVersion上完成的提交历史)。例如,可以使用--no-ff标志来覆盖此设置:

git merge newVersion --no-ff

结果如下:

master (pre-merge):

    1=>2=>3

newVersion:

    1=>2=>3=>4=>5=>6

master (actual results of merge):

    1=>2=>3=========>7
            4=>5=>6

注意,提交7代表合并,而不是替换提交历史记录。
参考文献:https://sandofsky.com/images/fast_forward.pdf 或者,如果您希望将“newVersion”的整个提交历史记录 consolodate 到一个单独的提交中,并将其合并到“master”上(如果它们只是“新版本”进展过程中的小修改可能有用),您可以使用--squash标志运行合并。例如:
git merge --squash newVersion

结果为:

master (pre-merge):

    1=>2=>3

newVersion:

    1=>2=>3=>4=>5=>6

master (actual results of merge):

    1=>2=>3=>7

请注意,7号提交包含了4-6号提交的历史记录。

相对应的编程内容翻译为:从1到2再到3,最后到7(而7有两个父节点:3和6)。 - eftshift0
2
这不是 --no-ff 的作用。使用 --no-ff 时,您的历史记录将显示为 1=>2=>3=>4=>5=>6=>7,其中 7 是合并提交。快进式合并所做的只是避免创建合并提交。 - Michael Mior
@MichaelMior 如果我可以的话,我会给你展示一张截图,但根据我所看到的log历史记录,现在一切看起来都很好。@Edmundo是正确的,这将是一个新的提交(即7而不是6)。 - John Smith
如果你查看 git log --graph,你应该会看到提交 7 有两个父节点 - 36 - Paul
1
  1. 我不知道为什么这个回答被踩了。这是正确的答案,它提到了“快进”,并且解释了确切发生的事情。
  2. 为了教学价值,在您的“主分支(合并的实际结果)”图片中,我会展示分支,以便清楚地看到有一系列提交3=>4=>5=>6=>7 除了您所展示的部分之外。那部分和3=>7部分一样“有效”和重要,并将永远成为历史的一部分。
- AnoE
1
@AnoE 我曾经给一个之前的回答投了反对票,因为其中有一些不正确的信息。但是现在这个回答已经被编辑过了,我的反对票也变成了赞同票 :) - Michael Mior

1
无法得到你想要的最终历史记录。Git中的每个提交都与其父提交相关联。每个提交包含对其父提交的引用,因此6明确引用5。
如果你只想在1=>2=>3的基础上添加5和6之间的更改,那么可以使用git cherry-pick newFeature。这将创建一个新提交,其中仅包含5和6之间的更改,但应用于3的基础上。
如果你想要4、5和6的所有更改,但只想要一个提交,则可以使用--squash标志进行git merge。这将创建一个包含4、5和6的所有更改的新提交。然而,你的历史记录将不是1=>2=>3=>6,而是1=>2=>3=>7,其中7是这个新的提交。
请注意,如果您选择--squash选项,则每个分支只能执行一次此操作,因为通过压缩,您正在失去有关两个分支之间差异的信息。

在你回答之后,我可能仍需要稍微研究一下。https://sandofsky.com/images/fast_forward.pdf - John Smith
“6仅包含从5到6的更改”这种说法是不正确的。在Git中,提交并不记录更改。每个提交都是完全自包含的。如果将他所说的数字1、2、3等与提交的内容(而不是实际哈希值)相对应,那么完全可以创建他想要的历史记录。 - AnoE
@AnoE 您说得对,我的解释过于简单了。我的观点是提交取决于其父级,因此您无法将完全相同的提交移动到具有不同父级的位置,因为它的哈希值会更改。我编辑了答案以澄清这一点。 - Michael Mior
@MichaelMior,是的,我的评论主要是关于表述方式 - 这个特定的表述方式(“提交包含更改”)是来自其他VCS(如svn)的人们感到困惑的一个重要原因,因为在那里这确实是正确的。你目前的表述很好。哦,你可以用“父级”替换成“父级”,因为合并提交有多个父级。;) - AnoE
哦,我不确定你最后一句话的意思是“每个分支只做一次”。你指的是什么? - AnoE
我的意思是,当你改变分支时,你不能使用 git merge --squash 多次合并同一个分支。 - Michael Mior

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