【问题标题】:Do Git merges affect the "merged" branch?Git 合并会影响“合并”分支吗?
【发布时间】:2023-07-16 16:04:01
【问题描述】:

我工作的公司一直在尝试一个 Git 工作流,它包含三个主要分支(开发、测试、主)以及单独的功能分支。这涉及将功能分支合并为彼此独立的“开发”和“测试版”,然后定期将“测试版”合并到“主版本”以进行发布。

问题:当我们将功能分支合并到“开发”时,它似乎会影响功能分支的提交历史记录,因此当我们尝试将同一分支合并到“测试版”时,它还会包含来自'develop',这不是我们想要的。

有人知道这里发生了什么吗?是否可以将一个分支合并到其他两个分支而不包含任何不需要的提交?对我们的工作流程进行哪些更改可以防止这种情况发生?我知道没有代码示例或任何东西,但这并不是真正的那种问题,我不确定如何提供更多有用的信息。

谢谢!

【问题讨论】:

  • 不,合并只会影响一个分支。我猜你做错了什么。但是如果特性分支是基于开发的,那么特性分支显然包含开发的所有提交,直到发生分支的提交。
  • 功能分支从哪里分支?掌握还是发展?
  • 尝试使用控制台命令“git gui”来可视化您的存储库并发布它的屏幕截图?这会很有帮助。
  • @SamVarshavchik 功能分支是从“开发”创建的,因为它们经常需要共享代码,我无法让任何人学习如何挑选。我想这是问题的很大一部分。
  • @MladenB。我似乎找不到一个有用的区域来截图。我不擅长阅读这些版本控制可视化。

标签: git github version-control merge


【解决方案1】:

从一个基本意义上来说,合并操作不会真正影响任何 分支。 (它当然会做出一个新的提交,这会以通常的方式影响该分支。)Git 的诀窍是在你的脑海中同时保留以下五个想法:

  1. 在 Git 中重要的是提交及其父链接。分支名称大多只是分散注意力(但请参阅第 2 点和第 3 点)。

  2. 分支名称只是特定提交的名称,我们称之为该分支的提示提交

  3. 进行新提交时,Git 将当前提交作为其父提交写入新提交。1 如果新提交有多个父提交(请参阅下一点),则当前提交变为它的第一个父级。在任何情况下,Git 都会更新分支名称以指向新的提交。这就是分支“生长”的方式。

  4. 合并提交 是具有两个(或更多)父提交的提交。这实际上是“作为名词合并”。

  5. 进行合并的行为——我的意思是合并提交——包括进行三向合并操作,然后进行新的提交像往常一样,除了新提交有两个(或更多)父母。 “额外”的父母是合并的提交。2

合并动作——“作为动词合并”——使用通过上述五点建立的历史。 Git 找到三个提交:

  • 当前提交,又名HEAD。 (这很简单。)
  • 要合并的提交:git rev-parse 为您传递给git merge 的参数提供的任何 ID。分支名称只是找到分支提示提交。
  • 合并基地。这就是提交历史的来源,这就是您需要绘制图形片段的原因。

任何两个提交的合并基础被松散地定义为“图形重新组合在一起的(第一个)点”:

...--o--*--o--o--o     <-- branch1
         \
          o--o--o--o   <-- branch2

名称branch1 指向顶行的提示(最右边)提交。名称branch2 指向底部的提示提交。这两个提交的合并基数是标记为*的那个。3

为了执行合并操作,Git 然后将合并基础提交 * 与两个提示进行比较(如在git diff 中),给出两个差异。然后,Git 合并差异,每次更改仅获取一份副本:如果您(branch1)和他们(branch2)在README 中将单词color 更改为colour,Git 只接受换一次。

存储在工作树中的结果 source 成为合并提交的树。请注意,到目前为止,我们是将branch2 合并到branch1 还是将branch1 合并到branch2 都无关紧要:我们将获得相同的合并基数,并具有相同的两个提示提交,因此获得相同的两个差异并以相同的方式组合这两个差异以达到相同的工作树。但是现在我们进行实际的合并commit,它的两个父级,并且现在我们在哪个分支上很重要。如果我们在branch1,我们将新的提交on branch1,并推进名称branch1

...--o--o--o--o--o---M   <-- branch1
         \          /
          o--o--o--o     <-- branch2

新的合并提交有两个父节点:一个是branch1 的旧提示,另一个是branch2 的提示。

因为我们现在有一个新图,所以稍后的git merge 将找到一个新的合并基数。假设我们在两个分支上都进行了多次提交:

...--o--o--o--o--o---M--o--o     <-- branch1
         \          /
          o--o--o--*---o--o--o   <-- branch2

如果我们现在要求合并这两个分支,Git 首先会找到基础。这是在 both 分支上的提交,最靠近两个提示。我再次将此提交标识为*,并查看它的位置:它曾经是branch2 的提示,当我们进行合并时。

请注意,无论我们以哪种方式进行合并,情况仍然如此。

然而,进行实际的合并提交至关重要。如果我们使用git merge --squash,它不会进行合并提交,我们将不会得到这种图表。同样重要的是,合并后两个分支都不会“重新定位”,因为git rebase 通过复制 提交工作,而git merge 在提交身份和跟随父指针的基础上工作。任何副本都是不同的提交,因此任何旧提交都不会指向新复制的提交。 (在这些图中,可以在合并点之后将提交变基——向右,在这些图中;不可以复制左侧的提交。)

如果您不禁止git merge 执行“快进”操作,git merge 也可以跳过合并提交,而只是移动分支标签。在这种情况下,两个分支标签——一个你刚刚移动的,一个你要求合并的——最终指向同一个提交。一旦发生这种情况,就没有办法“解开”这两个分支,除非将标签移回。要防止git merge 执行此快进而不是实际合并,请使用--no-ff

这是一个快进“合并”的示例(在引号中,因为没有实际的合并)。像往常一样,我们从分歧的分支开始——但是在 current 分支上没有提交,branch1,在 other 上还没有提交/em> 分支,branch2:

...--o--*           <-- branch1
         \
          o--o--o   <-- branch2

如果我们坐在branch1 上运行git merge branch2——注意缺少--no-ff——Git 注意到不需要实际的合并。相反,它执行标签“快进”操作,将名称branch1 向前滑动,直到遇到branch2 上的提示提交:

...--o--o
         \
          o--o--o   <-- branch1, branch2

这张图没有地方记录两个分支之间的任何“分离”,所以我们不妨理顺一下这个扭结:

...--o--o--o--o--o   <-- branch1, branch2

直到我们在 branch2 上做出新的提交:

...--o--o--o--o--*     <-- branch1
                  \
                   o   <-- branch2

这并没有错误,但请注意,现在无法判断我们“向上”移动到第一行的三个提交是否已合并。


1这适用于普通的git commitgit merge,但不适用于git commit --amend。提交的“修改”变体像往常一样进行新提交,但不是将 current 提交设置为新提交的父提交,而是设置当前提交的 父提交(如他们中的许多人,甚至可能根本没有父母)作为新提交的父母。效果是将当前提交推到一边,使其看起来好像提交已更改,而实际上旧提交仍在存储库中。

2超过两个父母的情况称为“章鱼合并”,我们可以在这里忽略它。 (它不会做任何重复的成对合并无法做到的事情。)

3在复杂的图形中,这样的“第一点”可能不止一个。在这种情况下,所有最低公共祖先节点是合并基础,对于 Git,-s <em>strategy</em> 合并策略参数决定如何处理这种情况。 (当然还有-s ours策略,它忽略了所有其他提交,完全绕过了三路合并代码。但我这里假设正常合并。)

【讨论】:

  • 经过一天的搜索,这个解释是迄今为止对合并最直接的解释。
【解决方案2】:

听起来好像有一个pull 是从功能分支完成的。这是从开发中提交的最常见方式之一出现在您的功能分支中。

但作为一般做法,建议采取以下步骤,

  1. 在开始工作时检查基于 off 的新功能分支 发展。

  2. 完成工作并将其全部提交到功能分支上。

  3. 执行git pull --rebase origin develop 以确保 功能分支已准备好合并。

  4. 检查开发分支。

  5. 做一个git pull --rebase origin develop 并确保您的本地 开发已更新。

  6. 做一个git merge &lt;feature branch name&gt;

这应该是一种万无一失的方法。

【讨论】:

  • 这不是 only 方式,因为分支名称只是指向提交的指针。任何移动名称标签的操作都可以做到这一点。然而,git pull 运行 git merge,除非你告诉它运行 git rebase,当然git merge 会进行合并提交——或者,对于这种特殊情况,更糟糕的是,做一个快进标签移动,如果可能并允许的话。
  • 同意。实际上,我的意思是最常见的方法之一。 +1
  • 感谢您的评论。我无法让团队中的任何人为此使用命令行,那么有没有办法使用 Github 应用程序 + 网站复制这些步骤?这些基本上是我们已经遵循的步骤: 1. 从开发中创建一个新功能分支 2. 提交功能分支上的所有工作 3. 合并功能分支以使用 github 网站进行开发 简化,但我能做到的基本相同告诉。我们哪里出错了?
  • 不推荐使用github网站进行合并。它搅乱了历史。您也可以使用 tortoise git 从功能分支对开发分支进行拉动。