理解 Git 合并

3

我知道这个解决方案已经有了,但是我在理解Git方面遇到了困难。

我是一名单独开发者,在使用Git时其他分支通常没有变化。

以下通常是我所做的:

git branch changes
git checkout -b changes
/* changes occur */
git commit -m "Changes Occured"
/* Changes occur */
git commit -m "More Changes"

在这之后,我脑海中浮现出以下的想象:
  :
  | c2
  | |
  | c1
  | /
master

但是,Git GUI 显示了这个可视化界面

    :
    c2 changes
    |
    c1
    |
  master

我随后会这样将我的更改与主分支合并:
git checkout -b master
git merge changes

我有以下可视化:

:
| master, changes
|
| \
| c2
| |
| c1
| /

主分支

然而,git gui 显示:

:
| master, changes
c2
|
c1
|

主节点

这是预期的行为吗?还是我做错了什么?


1
附注:不要将代码片段制作成片段,只需缩进四个空格(或在浏览器编辑器中突出显示它们并按Ctrl-K)。 - torek
1
是的,只要在此期间没有提交到主分支,这是预期的,这被称为“快进式”合并。您可以通过使用 --no-ff 请求合并命令不执行快进式合并来避免它,但在大多数情况下,您实际上希望进行快进式合并,因为这会创建更简单和更线性的历史记录。 - Lasse V. Karlsen
3个回答

4
你的可视化假设在master分支上有更多的提交:
c3 (master)
|
| c2 (changes)
| |
| c1
| /
c0
:

或者,等价地说,分支名称具有某种永久意义(在Git中不是这样的:在Git中,只有提交(commit)很重要; 分支名称基本上只是为了适应人类而存在,除了一些特殊的角色,它们在保持提交活着的同时还帮助在不同的Git之间发送它们)。

还要注意的是,我也将分支标签移动到指向(only)最新的提交。这就是Git中分支标签的工作方式:它们指向一个单独的提交,因此确定分支的尖端(commit tip)。

如果提交commit c3还不存在,Git会注意到你有这样的安排:

c2 (changes)
|
c1
|
c0 (master)
:

在提交中,它是的尖端,也可以从的到达。这意味着提交在两个分支上都存在(请注意,即使像顶部图表中指向某个提交,这种可达性也是成立的)。由于您已经有了这样的安排,Git可以以“快进方式向前滑动名称”,沿着链接从到再到。这种实际上根本不是合并的东西是每当可能时的默认操作。如果已经指向新的提交,那就不可能了,Git就必须执行并创建一个“真正的合并”(执行合并操作,并创建一个合并提交)。

要强制Git进行合并提交,仍然完全不必要进行合并动作,Git也不会费心,您可以使用git merge --no-ff。这将创建一个新的合并提交并将标签移动到指向该合并提交:

c3 (HEAD -> master)
|\
| c2 (changes)
| |
| c1
| /
c0
:

请注意,只有当前分支名称,即标签“master”会移动。为了指示哪个标签是当前的,我们添加了“HEAD”名称。

1
如果主分支上没有其他提交,git 会选择快速前进而不是进行合并提交。如果您不喜欢这种行为(很多人和我一样),想要保留分支历史记录,您可以使用git merge _branch_ --no-ff强制 git 进行合并提交。

1

Git中的分支只是它们所包含的提交记录的历史。它们本身并没有被记录下来,仅仅指向那些提交记录中最新的一个。

这就是为什么

    :
    c2 changes
    |
    c1
    |
  master

它的展示方式与实际相同。只有两个提交,c1和c2,而changes指向这两个提交中的第二个,而master没有记录历史。

合并后,master也记录了提交c1c2。由于在合并之前没有专门在主分支上进行提交,因此masterchanges现在几乎相同,即从两个视角来看,它们都记录了相同的提交历史,按照相同的顺序发生了相同的提交。这是(c1c2),因为合并是通过快进完成的。


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