Mad Physicist's answer 是正确的(并且被赞成),但图表可能会有所帮助。
理解这一点的关键是,对于 Git 中的大多数用途,分支 names 几乎完全不相关。重要的是提交图(以及该图中每个参与提交的快照)。
假设您有以下图表:
...--A--B--C <-- mainline
\
D--E--F <-- feature
并且您正在向git merge 提议将功能分支放入主线分支。通常你会这样做:
git checkout mainline && git merge feature
这将指导 Git 使用提交 B 和 C 作为“我们做了什么”,并提交 B 和 F 作为“他们做了什么”来执行合并(“合并”作为动词) .如果一切顺利,Git 会自动进行 new 提交,它会调整标签 mainline 以将 指向 新提交。如果事情进展不顺利,Git 会强迫我们修复混乱(编辑工作树中的内容,并 git add 结果)并自己做出新的提交。这也会产生一个新的提交,它还会调整标签mainline 以指向新的提交。在任何情况下,新提交都有 两个 父级:提交 C 和 F(按此顺序)。换句话说,新的提交是 a 合并(“合并”作为名词)。所以让我们画这个:
...--A--B--C------G <-- mainline
\ /
D--E--F <-- feature
现在,请注意,此图与 this 图相同:
C
/ \
...--A--B \-----G <-- mainline
\ /
D--E--F <-- feature
但是想象一下,我们可以让 Git 不在我们提交之后移动标签 mainline,以便我们得到 this 图表:
C <-- mainline
/ \
...--A--B \-----G <-- ??? (HEAD)
\ /
D--E--F <-- feature
除了单独留下分支名称mainline,效果一模一样:我们做同样的merge-as-a-verb工作,做同样的merge-as-a-名词提交对象G。
如果您创建一个新的分支名称(填写??? 部分),就会发生这种情况。 “新合并前”图片为:
C <-- mainline, test-branch (HEAD)
/
...--A--B
\
D--E--F <-- feature
合并后的图片是添加了合并提交G的图片。
Git 根据HEAD 知道要更新哪个 分支名称。
请注意,您甚至可以使用 Git 的“分离 HEAD”模式进行合并。在这种模式下,名称HEAD直接指向提交 ID,而不是让 HEAD 文件存储分支名称。 Git 的其余部分照常工作,但在进行新提交时,并没有更新存储在 HEAD 中的分支名称——没有一个——Git 只是更新 HEAD 本身。
一旦你确定合并是好的,你可以让 Git 在 快进 操作中“滑动名称 mainlineforward”:
C <-- (old mainline)
/ \
...--A--B \-----G <-- HEAD, (new mainline if slide-forward works)
\ /
D--E--F <-- feature
如果无法仅向前滑动,则此“向前滑动”操作会故意失败。例如,假设我们的工作进行合并、解决冲突或我们必须做的任何事情,然后运行我们的测试,花费了相对较长的时间......在此期间,其他人 潜入并更新mainline,添加一些他们自己的新提交:
C---------H <-- mainline
/ \
...--A--B \-----G <-- HEAD
\ /
D--E--F <-- feature
不再可能“向前滑动”到G:名称mainline 必须首先“向后滑动”到C,失去H。快进操作会失败,让我们知道我们的临时合并G毕竟不能成为分支mainline的永久成员。
(我把新的提交H画成一个普通的提交,但不管是简单提交还是合并提交都没有关系:关键是它在C之后,而不是在之后G。这些快进操作必须仅“向前”移动——在这些图表中向右移动。)