如何在更新/合并历史记录时执行Git合并

时间:2018-08-20 11:49:33

标签: git

首先,我知道--squash,但这不能单独解决我的问题。

我有两个分支AB,其中A需要定期合并到B中。出于所有意图和目的,这两个分支代表了不同的项目,不同的团队负责这些项目,但是A包含的通用代码类似于B中的库。现在,忽略了这是一个简化的示例,答案并不像“将公共库代码提取到新的分支/存储库C那样简单,我如何定期将A合并到{{1 }}并没有牵涉到B的全部历史记录?

我们不想要历史记录的原因是因为它们实际上是两个单独的项目,A上90%以上的更改对A来说都无关紧要(从开发人员了解了什么从历史的角度来看)。更复杂的是,我们仍然希望支持一种功能分支方法,即开发人员应该能够从B创建一个功能分支,然后从A创建另一个功能分支,并最终合并从B的功能分支更改为A的功能分支。


我们考虑过的选项:

常规合并

一切正常,除了我们在B中拥有A的全部历史。

南瓜

Squash摆脱了历史,但是当我们继续从B合并到A时,除非我们手动跟踪已合并的提交,否则将陷入冲突。这对于功能分支工作流来说是一个问题,因为每个开发人员都必须仔细地进行合并,以排除之前(或选择摘录)之前被压缩的所有内容。

子树(读取树)

据我们所知,不很好地支持分支。当开发人员创建两个功能分支时,他们将必须以某种方式更新子树以指向B的功能分支,而不是A本身。合并功能分支以掌握功能时,开发人员将不得不再次还原/更正此子树指针。

子模块

与子树相同的问题,但还添加了许多其他问题。


关于如何实现主要目标的任何建议(频繁的合并而没有完整的历史记录)?

1 个答案:

答案 0 :(得分:1)

在您施加的限制内,没有一个好的解决方案。有一个解决方案,我将在下面概述。但首先我想清楚-我不认为这是一个好的解决方案,我认为您可能最终会遇到问题。[1]

但是如果出于某种原因您仍然必须尝试:

您将必须创建一种桥分支。该分支将包含A的简要历史记录。您将仅使用B的有用信息来定期更新它;然后将其合并到B中。

因为您说的是简单合并,但合并会带来太多的历史记录,因此我认为不需要对内容进行任何转换-因此,您所需要的只是稀疏的历史记录。这意味着尽管B中的历史记录将减少为更少的提交,但所有更改仍在那些更少的提交中。如果您甚至需要避免这种情况,我想在评论中让我知道...但是解决方案变得更加复杂。

同样因为合并将起作用,所以我假设存在分支AB的共同祖先,我将其称为O

... O -- x -- x -- x -- P <--(A)
     \
      y -- y -- y <--(B)

BO是“最新的”,但与A之后的任何更改都不相同。您希望对P中的B进行更改,但不想看到x历史中的B提交。首先,我们将在O处创建“ bridge branch”。我们需要一个解析为O的表达式-它可以是O的提交ID,但是在本示例中,我们可以使用A~4A指的是{{1} },因此我们希望P的第4个父级。

A

现在的问题是如何重复地更新git checkout A~4 git checkout -b bridge 。如您所述,您第一次可以做

bridge

但是这是不可重复的,因为您第二次尝试使用git不会知道正确的合并基础。同样,您第一次可以进行交互式git merge --squash 并使用TODO列表将rebase的所有提交放在一起,但是要重复一次,您必须跟踪{{1} }已经被复制到squash,并且容易出错。

但是bridge分支为您提供的保证是-A的唯一更改来源是bridge的合并,因此在每个“合并”中,可以假设所有更改都是“他们的”。换句话说,我们总是只想将bridge的特定状态(可能是当前状态)提交到A上。

有几种方法可以做到这一点。从

开始
bridge

一种瓷器的方法是

A

现在有

      x -- x -- x -- P <--(A)
     /
... O <--(bridge)
     \
      y -- y -- y <--(B)

所以你可以

git checkout --detach A
git reset --soft bridge
git checkout bridge
git commit -m "Update from A through P"

获得

      x -- x -- x -- P <--(A)
     /
... O -- P' <--(bridge)
     \
      y -- y -- y <--(B)

现在,您已经最小化了git checkout B git merge bridge 中将显示 x -- x -- x -- P <--(A) / ... O ----------- P' <--(bridge) \ \ y -- y -- y -- M1 <--(B) 的历史记录的数量。您仍然可以看到所有的更改,当然,我们再次修改了历史记录,但仍合并了所有内容更改。

现在发生了更多的工作,您进入了一个新状态,在A的提交B中,公用代码已再次更新。

A

再次注意,Q不会从任何其他来源进行更改;它仍然与 x -- x -- x -- P -- x -- x -- Q <--(A) / ... O ----------- P' <--(bridge) \ \ y -- y -- y -- M1 -- y -- y -- y <--(B) 匹配,因此您仍然只想更新它以匹配bridge。所以你重复这个过程

P

尽管此过程中没有任何“知道” Qgit checkout --detach A git reset --soft bridge git checkout bridge git commit -m "Update from A through Q" git checkout B git merge branch 的情况,但我们仍然以

结尾
branch

您可以根据需要重复多次。


[1]-我假设仅在P命令上使用诸如 x -- x -- x -- P -- x -- x -- Q <--(A) / ... O ----------- P' ----------------- Q' <--(bridge) \ \ \ y -- y -- y -- M1 -- y -- y -- y -- M2 <--(B) 之类的选项是不合适的解决方案,因为这也可能会切断{{ 1}}您要查看的项目。但是您可能要考虑从这个角度看问题是否可以简化任何事情……

而且,尽管您已经排除了它,但是最好的解决方案可能是将通用代码提取到库中,并使其成为两个项目的依赖项(在此过程中,我会将项目移到单独的位置回购而不是单独的分支)。那也许“说起来容易做起来难”,但是如果这样的话,那可能就指出了一个体系结构问题,如果不解决,它将导致您头疼。