为什么我的推送中突然有一个合并提交?
Why do I suddenly have a merge commit in my pushes?
好吧,我好像去把事情搞砸了。
直到最近,我曾经能够进行合并提交,然后在不显示单独提交的情况下推送到原点。现在它确实如此,合并提交是我在我的管道中所能看到的:
在此开始之前,只有手动提交被推送到原点(或至少如此显示):
这是行为更改后的团队资源管理器 (VS 2019 v16.6.5):
...这是我当地的分行历史:
看到变化了吗?
这一切都是在我恢复提交 a13adadf
、修复它并重新发布后立即开始的。现在我遇到了某种奇怪的分支效应,我不知道如何让事情恢复到原来的状态。 (我尝试研究这个问题,但在搜索与 merge commit
相关的任何内容时,信噪比非常低。)
如何让我的 repo 到 'ignore'(即停止显示)合并提交?
(注意:我是唯一从事此回购的开发人员。)
您之前似乎进行过 fast-forward 操作。如果条件正确,git merge
命令将执行此操作 而不是 合并:
- A fast-forward 需要成为可能。
- 您需要避免
--no-ff
选项,这会禁用 fast-forward.
This all started right after I reverted commit a13adadf
, fixed it and republished it.
这一定是创建了一个b运行ch。这个词“b运行ch”有问题,也就是说,它会让您误入歧途,但您在问题中显示的图表片段表明这实际上是发生了什么。
How can I get my repo to 'ignore' (i.e. stop displaying) the merge commits?
如果您只是想避免显示它们,您的查看器可能有一些选项可以做到这一点。
如果你想回到不制造它们——你之前的情况——你需要消除你制造的b运行ch。
Long:这是怎么回事(以及为什么“b运行ch”这个词有问题)
首先要记住的是 Git 是关于提交的。刚接触 Git 的人,甚至那些已经使用了一段时间的人,通常认为 Git 是关于文件的,或者 b运行ches。但它不是,真的:它是关于 提交 .
每个提交都有编号,但编号不是简单的计数。相反,每个提交都会得到一个 random-looking——但实际上根本不是 运行dom—— 哈希 ID。这些东西又大又丑,Git 有时会缩写它们(例如你的 a13adadf
),但每一个都是一些 Git 对象的数字 ID——在这个案例,对于 Git 提交。
Git 有一个包含所有对象的大数据库,可以通过 ID 查找。如果您给 Git 一个提交编号,它会根据 ID 找到该提交的内容。
提交的内容分为两部分:
首先,有一个 Git 知道的所有文件的快照。这往往是大多数提交的大部分,除了一件事:文件以特殊的 read-only、Git-only、压缩和 de-duplicated 格式存储。当您进行新提交时,其中大多数文件与一些 previous 提交基本相同,新提交实际上并没有存储文件 again.它只是 re-uses 现有文件。换句话说,一个特定文件的一个特定版本被分摊到许多提交 re-use 上。 re-use 是安全的 因为 文件是 read-only.
除了保存的快照,每个提交存储一些关于提交本身的元数据:信息。这包括提交人的姓名和电子邮件地址,以及一些 date-and-time 信息,等等。值得注意的是,为了 Git 的使用,每个提交的元数据还存储了提交的提交编号(哈希 ID)或在 之前 此特定提交的提交. Git 称其为 parent 或者,对于合并提交,是提交的 parents。
这样做是为了让 Git 可以 向后 工作。这就是 Git 向后工作的方式。如果我们有一长串提交,全部连续,就像这样:
... <-F <-G <-H
其中H
代表链中last提交的实际哈希ID,Git将从提交H
开始,从对象数据库中读取它。在提交 H
、Git 中将找到所有已保存的文件,以及较早提交 G
的哈希 ID。如果 Git 需要它,Git 将使用此哈希 ID 从对象数据库中读取提交 G
。这给出了 Git 较早的快照,以及 even-earlier 提交的哈希 ID F
。
如果 Git 需要,Git 将使用散列 ID F
(存储在 G
中)来读取 F
,当然 F
还包含另一个父散列 ID。因此,以这种方式,Git 可以从 last 提交开始并向后工作。
这给 Git 留下了一个问题:它如何快速找到链中 last 提交的哈希 ID?这是 b运行ch names 进来的地方。
A b运行ch 名称只包含最后一次提交的哈希 ID
考虑到上述情况——并故意变得有点懒惰,并将提交与提交之间的连接画成一条线,而不是从子项到父项的箭头——我们现在可以绘制 master
b运行像这样输入:
...--F--G--H <-- master
name master
仅包含现有提交的实际哈希 ID H
。
让我们添加另一个名称,develop
,也包含哈希 ID H
,如下所示:
...--F--G--H <-- develop, master
现在我们有一个小问题:我们要使用哪个name?在这里,Git使用特殊名称HEAD
来记住使用哪个b运行ch名称,所以让我们稍微更新一下绘图
...--F--G--H <-- develop, master (HEAD)
这表示 git checkout master
之后的结果:当前 b运行ch name 现在是 master
,并且 master
选择提交 H
,这就是我们正在使用的提交(以及我们正在使用的 b运行ch 名称)。
如果我们现在 运行 git checkout develop
,Git 将切换到那个 b运行ch。 name 仍然标识提交 H
,因此没有其他要更改的内容,但现在我们有:
...--F--G--H <-- develop (HEAD), master
如果我们现在进行新的提交,Git 将:
- 打包它知道的所有文件(这是Git的索引或暂存区的用武之地, 但我们不会在这里介绍);
- 添加适当的元数据,包括您作为作者和提交者的姓名以及作为时间戳的“现在”,但重要的是,使提交
H
成为新的 parent提交;
- 使用所有这些来进行新的提交,我们称之为
I
。
还有一件事Git会做,但让我们现在画这部分。结果是:
...--F--G--H
\
I
那两个名字呢?还有一件事:Git 会将 I
的哈希 ID 写入 当前名称 。如果那是 develop
,我们得到这个:
...--F--G--H <-- master
\
I <-- develop (HEAD)
请注意,master
保持不变,但名称 develop
已移至指向最新的提交。
当两个名称标识相同的提交时,任一名称都选择该提交
请注意,最初,当 master
和 develop
都选择了提交 H
时,从某种意义上说,与 git checkout
一起使用哪个并不重要.无论哪种方式,您都将提交 H
作为当前提交。但是当你提交 new 时,现在它很重要,因为 Git 只会更新一个 b运行ch name.没有人知道新提交的哈希 ID 是什么(因为它部分取决于您进行提交的确切秒数),但是一旦提交,develop
将保存该哈希 ID,如果 develop
是当前的 name.
请注意,如果您现在 git checkout master
并再次提交,名称 master
将是这次更新的名称:
...--F--G--H--J <-- master (HEAD)
\
I <-- develop
我们暂时假设您还没有这样做。
Fast-forward
考虑到之前的情况,现在让我们 运行 git checkout master
,然后返回使用提交 H
:
...--F--G--H <-- master (HEAD)
\
I <-- develop
在这种状态下,让我们现在运行git merge develop
。
Git 将执行它为 git merge
所做的事情——见下文——并发现 merge base 是提交 H
,这也是当前的提交。另一个提交 I
领先于 提交 H
。这些是 Git 可以执行 fast-forward 操作的条件。
A fast-forward 不是真正的合并。 Git 对自己说: 如果我进行了真正的合并,我会得到一个快照与提交 I
相匹配的提交。因此,我会走捷径,只是 签出 提交 I
,同时将名称 master
拖到我身边。结果如下所示:
...--F--G--H
\
I <-- develop, master (HEAD)
现在没有理由保留绘图中的扭结了——我们可以把它全部画成一行。
真正的合并
有时,上述那种 fast-forward-instead-of-merge 技巧是行不通的。假设您开始于:
...--G--H <-- develop, master (HEAD)
并进行两次新提交 I-J
:
I--J <-- master (HEAD)
/
...--G--H <-- develop
现在你 git checkout develop
再做两次提交 K-L
:
I--J <-- master
/
...--G--H
\
K--L <-- develop (HEAD)
此时无论你给git checkout
哪个名字,如果你运行git merge
在另一个名字上,就没有办法前进了 从 J
到 L
,反之亦然。从 J
,你必须备份到 I
,然后下降到共享提交 H
,然后才能前进到 K
,然后是 L
。
这种合并,那么,不能是fast-forward操作。 Git 将改为进行真正的合并。
要执行合并,Git 使用:
- 当前 (
HEAD
) 提交:让我们先 git checkout master
来实现 J
;
- 您命名的另一个提交:让我们使用
git merge develop
选择提交 L
;
- 还有一个提交,Git 自己找到了。
最后一次——或者实际上是第一次——提交是合并基础,合并基础是根据称为最低公共祖先的图形操作定义的,但是短并且可以理解的版本是 Git 从 both 提交找到 最佳共享共同祖先 。在这种情况下,就是提交 H
:两个 b运行 分支的点。虽然 G
和更早的提交也被共享,但它们不如提交 H
.
所以 Git 现在将:
- 将合并基础
H
快照与 HEAD
/J
快照进行比较,以查看我们在 master
; 上所做的更改
- 将合并基础
H
快照与其他快照进行比较L
快照,看看他们在 develop
上做了什么改变;和
- 合并 两组更改,并将它们应用于合并基础快照。
这是合并的过程,或者合并作为动词。如果可以,Git 将自行完成所有这些工作。如果成功,Git 将进行新的提交,我们称之为 M
:
I--J
/ \
...--G--H M <-- master (HEAD)
\ /
K--L <-- develop
请注意,新提交 M
指向 both 提交 J
和 L
.这实际上是使这个新提交成为合并提交的原因。因为 fast-forward 实际上是不可能的,所以 Git 必须 进行此提交,以实现合并。
你最初在做 fast-forwards
您一开始遇到的是这种情况:
...--G--H <-- master, develop (HEAD)
然后产生:
...--G--H <-- master
\
I <-- develop (HEAD)
您使用 git checkout master; git merge develop
或类似的方法得到:
...--G--H--I <-- master (HEAD), develop
之后你可以重复这个过程,首先制作develop
,然后develop
和 master
,命名新提交J
:
...--G--H--I--J <-- master (HEAD), develop
但是此时你做了一些不同的事情:你在 master
.
上做了 git revert
git revert
命令进行了新的提交。新提交的快照就像之前一个提交的快照 backed-out,所以现在你有:
K <-- master (HEAD)
/
...--G--H--I--J <-- develop
K
中的快照可能与 I
中的快照匹配(因此它 re-uses 所有这些文件),但提交编号是 all-new.
从这里开始,你做了 git checkout develop
并写了比 J
更好的提交,我们可以称之为 L
:
K <-- master
/
...--G--H--I--J--L <-- develop (HEAD)
然后你又回到了master
和运行git merge develop
。这一次,Git 不得不 进行新的 合并提交 。所以它就是这样做的:
K--M <-- master (HEAD)
/ /
...--G--H--I--J--L <-- develop
现在,当您返回 develop
并进行新提交时,您会得到相同的模式:
K--M <-- master
/ /
...--G--H--I--J--L--N <-- develop (HEAD)
当您切换回 master
和 git merge develop
时,Git 必须再次进行新的合并提交。 Fast-forwarding 是不可能的,相反你得到:
K--M--O <-- master (HEAD)
/ / /
...--G--H--I--J--L--N <-- develop
你能做些什么
假设你现在运行git checkout develop && git merge --ff-only master
。第一步选择develop
作为当前b运行ch。第二个要求与 master
合并。这个额外的标志,--ff-only
,告诉Git:但只有当你可以作为 fast-forward.
时才这样做
(我们已经相信 Git 可以像 fast-forward 一样做到这一点,所以这个 --ff-only
标志只是一个安全检查。不过我认为这是个好主意。)
因为 fast-forward 是 可能的,你会得到这个:
K--M--O <-- master, develop (HEAD)
/ / /
...--G--H--I--J--L--N
注意名称 develop
是如何向前移动的,指向提交 O
,而没有添加新的合并提交。这意味着您在 develop
上进行的下一次提交将以 O
作为其父级,如下所示:
P <-- develop (HEAD)
/
K--M--O <-- master
/ / /
...--G--H--I--J--L--N
如果你现在git checkout master; git merge develop
你会得到一个fast-forward,两个名字都标识新的提交P
,你会回到那种情况下提交develop
允许 fast-forward.
请注意,这样做实际上是在声明您根本不需要 develop
这个名字
如果您的 work-pattern 是:
- 进行新提交
- 向前拖动
master
以匹配
那么您需要做的就是在 master
上进行新的提交。
在另一个名字上进行新的提交本身并没有什么错误,如果这只是有时你的工作模式,那可能是一个好习惯:使用大量的 b运行ch 名字对你以后有帮助,养成在开始工作前起一个新名字的习惯是好的。不过,您可能需要考虑使用比 develop
更有意义的名称。
无论如何,请注意 Git 这里关心的是 提交 。 b运行ch names 只是您可以让 Git 帮助您 find 特定提交的方式:由每个名称都是您使用该名称进行工作的时间点。实际的 b运行ching,如果有的话,是你所做的提交的函数。
换句话说:要将提交形式变成 b运行ches,您需要 b运行ch 名称,但要有 b运行ch 名称单独不会将提交形式变成 b运行ches. 即:
...--F--G--H <-- master
\
I--J <-- develop
给你两个“最后”提交,但是一个线性链在提交 J
结束。从某种意义上说,有两个 b运行ches,一个以 H
结束,一个以 J
结束,但在另一种意义上,只有一个 b运行ch,结束于 J
。我们可以添加更多名称,指向现有提交:
...--F <-- old
\
G--H <-- master
\
I--J <-- develop
现在有三个 names(和三个“最后”提交)但是存储库中的实际 commits 集还没有变了。我们只是把 F
单独画在一条线上,让名字 old
指向它。
好吧,我好像去把事情搞砸了。
直到最近,我曾经能够进行合并提交,然后在不显示单独提交的情况下推送到原点。现在它确实如此,合并提交是我在我的管道中所能看到的:
在此开始之前,只有手动提交被推送到原点(或至少如此显示):
这是行为更改后的团队资源管理器 (VS 2019 v16.6.5):
...这是我当地的分行历史:
看到变化了吗?
这一切都是在我恢复提交 a13adadf
、修复它并重新发布后立即开始的。现在我遇到了某种奇怪的分支效应,我不知道如何让事情恢复到原来的状态。 (我尝试研究这个问题,但在搜索与 merge commit
相关的任何内容时,信噪比非常低。)
如何让我的 repo 到 'ignore'(即停止显示)合并提交?
(注意:我是唯一从事此回购的开发人员。)
您之前似乎进行过 fast-forward 操作。如果条件正确,git merge
命令将执行此操作 而不是 合并:
- A fast-forward 需要成为可能。
- 您需要避免
--no-ff
选项,这会禁用 fast-forward.
This all started right after I reverted commit
a13adadf
, fixed it and republished it.
这一定是创建了一个b运行ch。这个词“b运行ch”有问题,也就是说,它会让您误入歧途,但您在问题中显示的图表片段表明这实际上是发生了什么。
How can I get my repo to 'ignore' (i.e. stop displaying) the merge commits?
如果您只是想避免显示它们,您的查看器可能有一些选项可以做到这一点。
如果你想回到不制造它们——你之前的情况——你需要消除你制造的b运行ch。
Long:这是怎么回事(以及为什么“b运行ch”这个词有问题)
首先要记住的是 Git 是关于提交的。刚接触 Git 的人,甚至那些已经使用了一段时间的人,通常认为 Git 是关于文件的,或者 b运行ches。但它不是,真的:它是关于 提交 .
每个提交都有编号,但编号不是简单的计数。相反,每个提交都会得到一个 random-looking——但实际上根本不是 运行dom—— 哈希 ID。这些东西又大又丑,Git 有时会缩写它们(例如你的 a13adadf
),但每一个都是一些 Git 对象的数字 ID——在这个案例,对于 Git 提交。
Git 有一个包含所有对象的大数据库,可以通过 ID 查找。如果您给 Git 一个提交编号,它会根据 ID 找到该提交的内容。
提交的内容分为两部分:
首先,有一个 Git 知道的所有文件的快照。这往往是大多数提交的大部分,除了一件事:文件以特殊的 read-only、Git-only、压缩和 de-duplicated 格式存储。当您进行新提交时,其中大多数文件与一些 previous 提交基本相同,新提交实际上并没有存储文件 again.它只是 re-uses 现有文件。换句话说,一个特定文件的一个特定版本被分摊到许多提交 re-use 上。 re-use 是安全的 因为 文件是 read-only.
除了保存的快照,每个提交存储一些关于提交本身的元数据:信息。这包括提交人的姓名和电子邮件地址,以及一些 date-and-time 信息,等等。值得注意的是,为了 Git 的使用,每个提交的元数据还存储了提交的提交编号(哈希 ID)或在 之前 此特定提交的提交. Git 称其为 parent 或者,对于合并提交,是提交的 parents。
这样做是为了让 Git 可以 向后 工作。这就是 Git 向后工作的方式。如果我们有一长串提交,全部连续,就像这样:
... <-F <-G <-H
其中H
代表链中last提交的实际哈希ID,Git将从提交H
开始,从对象数据库中读取它。在提交 H
、Git 中将找到所有已保存的文件,以及较早提交 G
的哈希 ID。如果 Git 需要它,Git 将使用此哈希 ID 从对象数据库中读取提交 G
。这给出了 Git 较早的快照,以及 even-earlier 提交的哈希 ID F
。
如果 Git 需要,Git 将使用散列 ID F
(存储在 G
中)来读取 F
,当然 F
还包含另一个父散列 ID。因此,以这种方式,Git 可以从 last 提交开始并向后工作。
这给 Git 留下了一个问题:它如何快速找到链中 last 提交的哈希 ID?这是 b运行ch names 进来的地方。
A b运行ch 名称只包含最后一次提交的哈希 ID
考虑到上述情况——并故意变得有点懒惰,并将提交与提交之间的连接画成一条线,而不是从子项到父项的箭头——我们现在可以绘制 master
b运行像这样输入:
...--F--G--H <-- master
name master
仅包含现有提交的实际哈希 ID H
。
让我们添加另一个名称,develop
,也包含哈希 ID H
,如下所示:
...--F--G--H <-- develop, master
现在我们有一个小问题:我们要使用哪个name?在这里,Git使用特殊名称HEAD
来记住使用哪个b运行ch名称,所以让我们稍微更新一下绘图
...--F--G--H <-- develop, master (HEAD)
这表示 git checkout master
之后的结果:当前 b运行ch name 现在是 master
,并且 master
选择提交 H
,这就是我们正在使用的提交(以及我们正在使用的 b运行ch 名称)。
如果我们现在 运行 git checkout develop
,Git 将切换到那个 b运行ch。 name 仍然标识提交 H
,因此没有其他要更改的内容,但现在我们有:
...--F--G--H <-- develop (HEAD), master
如果我们现在进行新的提交,Git 将:
- 打包它知道的所有文件(这是Git的索引或暂存区的用武之地, 但我们不会在这里介绍);
- 添加适当的元数据,包括您作为作者和提交者的姓名以及作为时间戳的“现在”,但重要的是,使提交
H
成为新的 parent提交; - 使用所有这些来进行新的提交,我们称之为
I
。
还有一件事Git会做,但让我们现在画这部分。结果是:
...--F--G--H
\
I
那两个名字呢?还有一件事:Git 会将 I
的哈希 ID 写入 当前名称 。如果那是 develop
,我们得到这个:
...--F--G--H <-- master
\
I <-- develop (HEAD)
请注意,master
保持不变,但名称 develop
已移至指向最新的提交。
当两个名称标识相同的提交时,任一名称都选择该提交
请注意,最初,当 master
和 develop
都选择了提交 H
时,从某种意义上说,与 git checkout
一起使用哪个并不重要.无论哪种方式,您都将提交 H
作为当前提交。但是当你提交 new 时,现在它很重要,因为 Git 只会更新一个 b运行ch name.没有人知道新提交的哈希 ID 是什么(因为它部分取决于您进行提交的确切秒数),但是一旦提交,develop
将保存该哈希 ID,如果 develop
是当前的 name.
请注意,如果您现在 git checkout master
并再次提交,名称 master
将是这次更新的名称:
...--F--G--H--J <-- master (HEAD)
\
I <-- develop
我们暂时假设您还没有这样做。
Fast-forward
考虑到之前的情况,现在让我们 运行 git checkout master
,然后返回使用提交 H
:
...--F--G--H <-- master (HEAD)
\
I <-- develop
在这种状态下,让我们现在运行git merge develop
。
Git 将执行它为 git merge
所做的事情——见下文——并发现 merge base 是提交 H
,这也是当前的提交。另一个提交 I
领先于 提交 H
。这些是 Git 可以执行 fast-forward 操作的条件。
A fast-forward 不是真正的合并。 Git 对自己说: 如果我进行了真正的合并,我会得到一个快照与提交 I
相匹配的提交。因此,我会走捷径,只是 签出 提交 I
,同时将名称 master
拖到我身边。结果如下所示:
...--F--G--H
\
I <-- develop, master (HEAD)
现在没有理由保留绘图中的扭结了——我们可以把它全部画成一行。
真正的合并
有时,上述那种 fast-forward-instead-of-merge 技巧是行不通的。假设您开始于:
...--G--H <-- develop, master (HEAD)
并进行两次新提交 I-J
:
I--J <-- master (HEAD)
/
...--G--H <-- develop
现在你 git checkout develop
再做两次提交 K-L
:
I--J <-- master
/
...--G--H
\
K--L <-- develop (HEAD)
此时无论你给git checkout
哪个名字,如果你运行git merge
在另一个名字上,就没有办法前进了 从 J
到 L
,反之亦然。从 J
,你必须备份到 I
,然后下降到共享提交 H
,然后才能前进到 K
,然后是 L
。
这种合并,那么,不能是fast-forward操作。 Git 将改为进行真正的合并。
要执行合并,Git 使用:
- 当前 (
HEAD
) 提交:让我们先git checkout master
来实现J
; - 您命名的另一个提交:让我们使用
git merge develop
选择提交L
; - 还有一个提交,Git 自己找到了。
最后一次——或者实际上是第一次——提交是合并基础,合并基础是根据称为最低公共祖先的图形操作定义的,但是短并且可以理解的版本是 Git 从 both 提交找到 最佳共享共同祖先 。在这种情况下,就是提交 H
:两个 b运行 分支的点。虽然 G
和更早的提交也被共享,但它们不如提交 H
.
所以 Git 现在将:
- 将合并基础
H
快照与HEAD
/J
快照进行比较,以查看我们在master
; 上所做的更改
- 将合并基础
H
快照与其他快照进行比较L
快照,看看他们在develop
上做了什么改变;和 - 合并 两组更改,并将它们应用于合并基础快照。
这是合并的过程,或者合并作为动词。如果可以,Git 将自行完成所有这些工作。如果成功,Git 将进行新的提交,我们称之为 M
:
I--J
/ \
...--G--H M <-- master (HEAD)
\ /
K--L <-- develop
请注意,新提交 M
指向 both 提交 J
和 L
.这实际上是使这个新提交成为合并提交的原因。因为 fast-forward 实际上是不可能的,所以 Git 必须 进行此提交,以实现合并。
你最初在做 fast-forwards
您一开始遇到的是这种情况:
...--G--H <-- master, develop (HEAD)
然后产生:
...--G--H <-- master
\
I <-- develop (HEAD)
您使用 git checkout master; git merge develop
或类似的方法得到:
...--G--H--I <-- master (HEAD), develop
之后你可以重复这个过程,首先制作develop
,然后develop
和 master
,命名新提交J
:
...--G--H--I--J <-- master (HEAD), develop
但是此时你做了一些不同的事情:你在 master
.
git revert
git revert
命令进行了新的提交。新提交的快照就像之前一个提交的快照 backed-out,所以现在你有:
K <-- master (HEAD)
/
...--G--H--I--J <-- develop
K
中的快照可能与 I
中的快照匹配(因此它 re-uses 所有这些文件),但提交编号是 all-new.
从这里开始,你做了 git checkout develop
并写了比 J
更好的提交,我们可以称之为 L
:
K <-- master
/
...--G--H--I--J--L <-- develop (HEAD)
然后你又回到了master
和运行git merge develop
。这一次,Git 不得不 进行新的 合并提交 。所以它就是这样做的:
K--M <-- master (HEAD)
/ /
...--G--H--I--J--L <-- develop
现在,当您返回 develop
并进行新提交时,您会得到相同的模式:
K--M <-- master
/ /
...--G--H--I--J--L--N <-- develop (HEAD)
当您切换回 master
和 git merge develop
时,Git 必须再次进行新的合并提交。 Fast-forwarding 是不可能的,相反你得到:
K--M--O <-- master (HEAD)
/ / /
...--G--H--I--J--L--N <-- develop
你能做些什么
假设你现在运行git checkout develop && git merge --ff-only master
。第一步选择develop
作为当前b运行ch。第二个要求与 master
合并。这个额外的标志,--ff-only
,告诉Git:但只有当你可以作为 fast-forward.
(我们已经相信 Git 可以像 fast-forward 一样做到这一点,所以这个 --ff-only
标志只是一个安全检查。不过我认为这是个好主意。)
因为 fast-forward 是 可能的,你会得到这个:
K--M--O <-- master, develop (HEAD)
/ / /
...--G--H--I--J--L--N
注意名称 develop
是如何向前移动的,指向提交 O
,而没有添加新的合并提交。这意味着您在 develop
上进行的下一次提交将以 O
作为其父级,如下所示:
P <-- develop (HEAD)
/
K--M--O <-- master
/ / /
...--G--H--I--J--L--N
如果你现在git checkout master; git merge develop
你会得到一个fast-forward,两个名字都标识新的提交P
,你会回到那种情况下提交develop
允许 fast-forward.
请注意,这样做实际上是在声明您根本不需要 develop
这个名字
如果您的 work-pattern 是:
- 进行新提交
- 向前拖动
master
以匹配
那么您需要做的就是在 master
上进行新的提交。
在另一个名字上进行新的提交本身并没有什么错误,如果这只是有时你的工作模式,那可能是一个好习惯:使用大量的 b运行ch 名字对你以后有帮助,养成在开始工作前起一个新名字的习惯是好的。不过,您可能需要考虑使用比 develop
更有意义的名称。
无论如何,请注意 Git 这里关心的是 提交 。 b运行ch names 只是您可以让 Git 帮助您 find 特定提交的方式:由每个名称都是您使用该名称进行工作的时间点。实际的 b运行ching,如果有的话,是你所做的提交的函数。
换句话说:要将提交形式变成 b运行ches,您需要 b运行ch 名称,但要有 b运行ch 名称单独不会将提交形式变成 b运行ches. 即:
...--F--G--H <-- master
\
I--J <-- develop
给你两个“最后”提交,但是一个线性链在提交 J
结束。从某种意义上说,有两个 b运行ches,一个以 H
结束,一个以 J
结束,但在另一种意义上,只有一个 b运行ch,结束于 J
。我们可以添加更多名称,指向现有提交:
...--F <-- old
\
G--H <-- master
\
I--J <-- develop
现在有三个 names(和三个“最后”提交)但是存储库中的实际 commits 集还没有变了。我们只是把 F
单独画在一条线上,让名字 old
指向它。