Git合并会影响“合并”分支吗?

Kei*_*ing 4 git version-control merge github

我工作的公司一直在尝试Git工作流,它具有三个主要分支(开发,测试版,母版)以及各个功能分支。这涉及将功能分支彼此独立地合并为“ develop”和“ beta”,然后定期将“ beta”合并为“ master”以进行发布。

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

有人知道这是怎么回事吗?是否可以将一个分支合并到另外两个分支中而不包含任何不需要的提交?我们的工作流程有哪些变化可以防止这种情况发生?我知道没有代码示例或任何东西,但实际上并不是那种类型的问题,而且我不确定如何还能提供更多有用的信息。

谢谢!

tor*_*rek 6

从一个基本的意义上说,合并操作实际上不会影响任何分支。(它的确会进行新的提交,从而以通常的方式影响该分支。)Git的窍门是将以下五个同时出现的想法牢记在心:

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

  2. 一个分支的名字仅仅是一个特定的承诺,我们称之为名称提示提交该分支的。

  3. 进行新提交时,Git会以当前提交作为其父提交新提交。1 如果新提交具有多个父级(请参阅下一点),则当前提交将成为其第一个父级。无论如何,Git然后都会更新分支名称以指向新的提交。这就是分支“增长”的方式。

  4. 一个合并提交是有两个(或更多)母公司提交的提交。就像以前一样,这是“作为名词合并”。

  5. 进行合并的行为(我的意思是合并提交)涉及执行三路合并操作,然后照常进行新的提交,不同之处在于新提交具有两个(或多个)父级。“额外的”父母是合并的提交。2

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

  • 当前提交,又名HEAD。(这很容易。)
  • 待合并的提交:git rev-parse传递给的参数的ID git merge。分支名称只是找到分支提示的提交。
  • 合并基础。这就是提交历史记录的来源,这就是为什么您需要绘制图形片段的原因。

任意两个提交的合并基础松散地定义为“图形返回的(第一个)点”:

...--o--*--o--o--o     <-- branch1
         \
          o--o--o--o   <-- branch2
Run Code Online (Sandbox Code Playgroud)

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

为了执行合并操作,Git然后对这两个技巧对git diff合并基础提交*进行差异化(如所示),给出两个差异。然后,Git合并差异,每次更改仅获取一个副本:如果您(在上branch1)和他们(在上branch2)都将单词更改colorcolourin README,则Git只会进行一次更改。

存储在工作树中的结果将成为合并提交的树。请注意,直到这一点,不要紧,我们是否正在合并branch2branch1branch1branch2:我们会得到相同的合并基础,并具有相同的两个尖提交,并因此得到了相同的两个比较和在结合这两个差异列表以相同的方式到达相同的工作树。但是,现在我们使用其两个父级进行实际的合并提交现在我们在哪个分支上很重要。如果我们上branch1,我们做出新的承诺 branch1,推进名branch1

...--o--o--o--o--o---M   <-- branch1
         \          /
          o--o--o--o     <-- branch2
Run Code Online (Sandbox Code Playgroud)

新的合并提交有两个父对象:一个是的旧提示,branch1另一个是的提示branch2

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

...--o--o--o--o--o---M--o--o     <-- branch1
         \          /
          o--o--o--*---o--o--o   <-- branch2
Run Code Online (Sandbox Code Playgroud)

如果现在我们要求合并两个分支,则Git首先找到基础。这是两个分支上最接近两个技巧的提交。我*再次确定了该提交,并查看它的位置:branch2在进行合并时,以前是该提交的提示。

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

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

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

这是一个快进“合并”的示例(用引号表示,因为没有实际的合并)。我们开始像往常一样,用树枝分歧,但也有对没有提交当前分支branch1,是不是已经其他分支,branch2

...--o--*           <-- branch1
         \
          o--o--o   <-- branch2
Run Code Online (Sandbox Code Playgroud)

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

...--o--o
         \
          o--o--o   <-- branch1, branch2
Run Code Online (Sandbox Code Playgroud)

该图无处记录两个分支之间的任何“分离”,因此我们不妨弄清楚这些纠结:

...--o--o--o--o--o   <-- branch1, branch2
Run Code Online (Sandbox Code Playgroud)

直到我们对做出新的提交branch2

...--o--o--o--o--*     <-- branch1
                  \
                   o   <-- branch2
Run Code Online (Sandbox Code Playgroud)

这样做没有任何问题,但是请注意,现在已经无法断定我们“移至”第一行的三个提交已合并。


1这对于常规git commit和常规都是正确的git merge,但并非如此git commit --amend。提交的“ amend”变体照常进行新提交,但不是将当前提交设置为新提交的父级,而是将当前提交的父级(尽可能多地设置为父级,甚至可能没有父级)全部)作为新提交者的父母。这样做的结果是将当前提交搁置一旁,看起来好像提交已更改,而实际上旧的提交仍在存储库中。

2多于两个父母的情况称为“章鱼合并”,在这里我们可以忽略它。(对于重复的成对合并,它无能为力。)

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


Som*_*yya 2

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

但作为一般实践建议,建议采取以下步骤,

  1. 在开始工作时,检查一个基于关闭开发的新功能分支。

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

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

  4. 检查开发分支。

  5. 执行此操作git pull --rebase origin develop并确保您的本地开发已更新。

  6. 做一个git merge <feature branch name>

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