分支机构的分支

时间:2020-08-02 16:56:16

标签: git pull-request git-squash

假设我具有以下git历史记录:以提交A开始的主分支,以提交feature-1和{{1}从A分支出来的B分支},以及第二个功能分支C,该分支基于提交feature-1C的提交D而构建。

E

现在假设提交master A \ feature-1 B--C \ feature-2 D--E 已经过测试,可以合并了,因此我们使用C

git switch master; git merge feature-1 --squash

master的历史记录很不错,只提交了master A------C' \ / feature-1 B--C \ feature-2 D--E A,但是如果我们现在想比较C'master(例如{{1 }}),我们最终看到feature-2中所有已合并的提交。

问题1:是否有一种简单的方法来压缩git log master..feature-2的历史记录以匹配压缩后的合并?如果历史稍微复杂些,并且在feature-1的分支点feature-2之后有更多提交被压入母版,该怎么办?

问题2:假设重写历史记录很困​​难(或者只能用C来繁琐;我在每个分支上的提交都超过了两次)有什么方法只能查看feature-1中没有被压入母版中的提交?在github或bitbucket上为git rebase -i执行一次pull请求时,有什么方法只能列出那些真正的新提交吗?

2 个答案:

答案 0 :(得分:2)

现在假设提交C已经过测试,可以合并了,因此我们使用git switch master; git merge feature-1 --squash

master     A------C'
            \    /
feature-1    B--C
                 \
feature-2         D--E

这幅图不太正确:它应该阅读我在下面绘制的方式。请注意,我也将名称也移到了右边,原因很快就会变得更加清楚。我也将其称为壁球提交BC,这是为了明确表明只有一次提交可以完成B-and-C的共同工作。

您绘制的是一个真正的合并(尽管您将合并提交称为C')。与matt said一样,“南瓜合并”根本不是合并。

A--BC   <-- master
 \
  B--C   <-- feature-1
      \
       D--E   <-- feature-2

目前,几乎没有理由保留名称feature-1。如果删除它,我们可以像这样重新绘制图形:

A--BC   <-- master
 \
  B--C--D--E   <-- feature-2

请注意,提交A-B-C-D-E都在分支feature-2上(无论我们是否删除名称feature-1);提交BC仅在master上。

保留名称feature-1的主要原因是它标识了提交C,这很容易将提交DE(无其他)复制到新的和改进的提交D'-E'

问题1:是否有一种简单的方法来压缩feature-2的历史记录以匹配压缩后的合并?

我不清楚您所说的“压榨历史”是什么意思。但是,运行以上git merge --squash后,提交BC中的快照将与(完全匹配)提交C中的快照,因此运行:

git switch feature-2 && git rebase --onto master feature-1

(注意这里的--onto 1 )将告诉Git复制提交DE(仅),并在提交{{1 }},像这样:

BC

现在可以安全删除名称 D'-E' <-- feature-2 (HEAD) / A--BC <-- master \ B--C <-- feature-1 \ D--E [abandoned] ,因为我们不再需要记住提交feature-1的哈希ID。如果我们停止绘制废弃的提交,那么最终结果是:

C

这可能就是您想要的。


1 通常,A--BC <-- master \ D'-E' <-- feature-2 使用一个名称或提交哈希ID。然后:

  1. 使用提交哈希ID作为限制,列出要复制的一组提交;
  2. 在提交哈希ID上等同于git rebase
  3. 复制步骤1中列出的提交;
  4. 移动您在步骤2之前所在的分支名称,以指向步骤3复制的最后一个提交;和
  5. git switch --detach还原为在步骤4中刚刚移动的分支名称。

如果不使用git switch ,则步骤1和2中的提交哈希ID 相同。当使用--onto时,步骤1和2中的提交哈希ID是(或至少可以是) 。因此,使用--onto,我们可以告诉Git:仅复制一些提交,而不是许多提交。

具体来说,如果没有--onto,我们将复制所有可从 --onto到达的提交,但 不能从(单个)到达)参数,副本将转到(单个)参数。使用HEAD,我们可以说:可以将--onto而不是我指定的限制器中的提交提交复制到我单独的HEAD参数指定的位置。让我们说不要尝试复制--ontoB 的情况。


另一方面,您也可以简单地运行:

C

如果您只想将D-E链球一次合并:

git switch master             # if needed - you're probably already there
git merge --squash feature-2

A--BC--DE <-- master (HEAD) \ B--C <-- feature-1 \ D--E <-- feature-2 通常也会很顺利,因为与常规git merge --squash一样,git merge的开头是:

  • 找到合并基础(在这种情况下为git merge --squash
  • 将合并基础与当前提交(A进行比较,因为BCHEAD,它标识提交master);和
  • 根据指定的提交(BC,因为E的名称提交feature-2)来区分合并基础。

第一个差异显示E所做的是因为B+C的快照匹配了BC,而第二个差异显示了C的做了,因为B+C+D+E'快照是EBCD的结果。因此,除非E和/或D特别是 un -做E和/或B做某事,否则两组更改很可能会自动合并。

(请注意,即使C和/或D撤消某些操作,此处的重新设置也总是很顺利。)

“不完全合并”和“真正合并”之间的区别仅限于最终提交:该南瓜只有一个父级(在这种情况下为E)的提交,而真正合并与两个父母有约定。在这种情况下,真正的合并会给您BC作为一个父级,而BC作为另一个父级。如果您想使用E壁球合并,则可能要重新设置B的基础,然后C首先提交。

如果历史稍微复杂一点,并且在特征1的分支点BC之后有更多提交被压入母版,该怎么办?

一如既往,诀窍是绘制实际图形。我们可以从这里开始:

C

A <-- master \ B--C--F--G <-- feature-1 \ D--E <-- feature-2 之后产生的内容:

git switch master && git merge --squash feature-1

现在适合使用:

A--BCFG   <-- master
 \
  B--C--F--G   <-- feature-1
      \
       D--E   <-- feature-2

请注意,这与我们在较早情况下使用的命令相同。它表示(与上面的脚注1中的步骤比较):

  1. 列出从git switch feature-2 && git rebase --onto master feature-1 (在feature-2之后的位置)可以到达的提交,但从git switch 不能到达。从feature-1可以到达的提交是feature-2,从A-B-C-D-E可以到达的提交是feature-1。从A-B-C-F-G中减去A-B-C-F-G将留下A-B-C-D-E

  2. D-E 上进入一个独立的HEAD,即,提交master

  3. 复制步骤1中列出的提交,即BCFGD

  4. 表示分支名称E我们现在所在的位置(提交feature-2)。

  5. 再次执行E'的等效操作。

结果是:

git switch feature-2

在此之后可以安全删除名称 D'-E' <-- feature-2 (HEAD) / A--BCFG <-- master \ B--C--F--G <-- feature-1 \ D--E [abandoned] :我们不再需要通过提交feature-1查找提交C的简便方法。

问题2:假设重写历史记录很困​​难(或者只能用G来乏味;我在每个分支上的提交都超过了两次)

正如您在上面看到的,这不一定是正确的假设。变基的难度取决于您每次要复制的提交会遇到多少合并冲突,这取决于在上一次公共提交(上图中的git rebase -i之后发生的情况)。还是:

...有什么方法可以仅查看C中没有被压入feature-2中的提交?

master对此具有简单的语法,只要您仍具有git log的名称即可标识适当的提交,如上图所示即可。

feature-1

就是那样。此语法表示git log feature-1..feature-2 开始并向后工作可到达的所有提交,减去从feature-2开始并向后工作可到达的所有提交。请注意,这是同一组提交我们在上面的示例中使用feature-1操作复制的内容。 2

在github或bitbucket上为git rebase执行拉取请求时,有什么方法只能列出那些真正的新提交吗?

否,因为这些系统没有等效的语法。但是,一旦您使用rebase复制所需的提交,并强制推动GitHub或Bitbucket存储库匹配,它们就会显示您想要的内容。


2 上面没有提到的是,feature-2 -> master在默认情况下故意忽略了步骤1中的某些提交。在您的情况下,这里没有应该忽略的提交,因此这并不是很重要,但是值得一提:

  • 默认情况下,git rebase忽略所有 merge 提交。
  • 根据设计(并且没有其他选择可以阻止),git rebase还使用与git rebasegit cherry相同的计算,以从复制中消除其{{3 }}与上游提交集中的提交匹配。 (如果不详细介绍git log --cherry-pick对称差异表示法的工作原理,很难定义此集合。)在您的情况下,这都不重要,因为在这里这种补丁ID匹配极不可能。对于上游人员故意使用A...B将您提交的一项或多项提交复制到您要重新建立基础的分支的情况,这意味着更多。
  • 在某些情况下,但又不是您的情况下,git cherry-pick默认运行git rebase来查找要忽略的提交,这会产生令人惊讶的结果。

从历史上讲,rebase文档对于提及这些内容比较松懈,可能是因为它们并不经常出现。就您而言,它们不应该出现。最新的rebase文档得到了极大的改进。

答案 1 :(得分:0)

在简单的情况下,如果您只是在rebasefeature-2 master,那么您将只有非空的提交。

在更复杂的情况下,我建议执行以下操作:

git switch feature-2
git merge origin/master
git reset --soft origin/master
git commit -m 'feature 2'

这将导致一次提交,该提交将feature-2上的所有更改有效地压缩到master