Git:在分支之前移动提交

时间:2013-04-03 00:31:54

标签: git

我的提交历史如下所示:

  B-X
 /
A-C
 \
  D

我想将X应用于所有分支BCD。虽然cherry-pick可以解决这个问题,但它会创建如下历史记录:

  B-X
 /
A-C-X
 \
  D-X

现在提交X重复三次,如果我有很多分支,这非常不方便。理想情况下,我希望历史看起来像这样:

    B
   /
A-X-C
   \
    D

X只出现一次。这段历史更清晰。我应该使用哪个命令来实现这一目标?

4 个答案:

答案 0 :(得分:2)

第一个要点是,您要求的修补程序将重写历史记录,这只是在没有其他人使用分支时您应该做的事情。例如,如果您将D移到X之后,那么之前签出D的其他任何人在拉出时都会感到非常困惑。你应该只在没有其他人使用它的情况下这样做,或者他们知道并期望改变。

// Make a new temporary branch starting at A, and move X into it.
git checkout -b tmp A
git cherry-pick X

// For each of B, C and D, rebase them on top of the temporary branch.
git checkout <branch>
git rebase tmp

答案 1 :(得分:1)

其他人指出的挑选策略非常有效。只是想指出你也可以使用带有--onto标志的一组rebase来做这种事情,并且它不需要临时分支。

将您的提交克隆到新位置

git rebase --onto A B X

这意味着您应用存在于X点的提交,但不存在于B点,并将它们应用于A的末尾。这将导致以下结果。

  B-X
 /
A--Y
|\
D C 

请阅读此答案底部有关Y的说明,为何其名称与X不同,以及git如何处理历史记录中的任何更改。

所以你已经在你想要的地方应用了X,现在你只需要重新定义其他分支,以便它们也可以在历史中使用它。

更新下游分支

git checkout C
git rebase Y

git checkout D
git rebase Y

git checkout B  
git rebase Y

此处唯一的规定是B。无论字母是否代表分支名称或SHA1提交,上述顺序都是有意义的。但是,如果BX实际位于分支中,那么您需要签出该分支并在创建X之前将其倒回。为了这个例子,我们可以将分支称为“bee”。

git checkout bee
git reset --hard B  
git rebase Y

现在你有......

     B
    /
A--Y-C
    \
     D 

在此之后,只吃蛋糕。

序言

重新定位,采摘樱桃,修改,哦,我的!

提交及其SHA1对于父提交,执行的更改以及其他一些细节是唯一的。因此,在更改提交的父级或祖先时,该提交实际上会被克隆并且将具有新的SHA1。重新定位,挑选樱桃或修改现有提交或提交父项的任何其他操作都会导致修改后的提交获得新的SHA1,因为提交SHA1已经改变,所有这些都会让孩子认为他们有新的父级,所以他们也会得到新的SHA1哈希值。

旧的提交将会存在,因此上面的第一个图表显示Y是具有新名称和新父级的X的克隆。两者仍然存在,在大多数情况下,旧提交可以很容易地检索,在某些情况下(像这一个),你实际上需要故意移动旧提交。这就是为什么在rebase --onto之后,你会看到图表显示X和Y.X提交没有“移动”,它更像是一个克隆。之后X仍然存在,需要通过检查B的分支来处理,并在'X'之前将其重置为提交。

实际上,BCD中的提交也会克隆所有提交,并且它们都会有新的SHA1哈希值。

这就是为什么在与其他开发人员共享后修改或修改提交可能会很麻烦。 如果那些开发人员拉入克隆,他们的本地仓库将会考虑其新工作,并且他们将获得重复提交,并且因为这些提交完全相同,他们将发生冲突。然后克苏鲁将起来并开始统治世界。

解释这个问题的困难是由于在这些例子中我们使用分支名称就好像它们是提交一样。如果这个问题是关于修改历史的副作用,我会添加更准确的图表。

答案 2 :(得分:0)

一种可能的解决方案是使用多个樱桃选择来做到这一点。结帐A和樱桃挑选X来创建

  B-X
 /  
A-X'
 \
  \-C
   \
    D

现在你应该在X'上,所以你可以选择B:

  B-X
 /  
A-X'-B'
 \
  \-C
   \
    D

Checkout X'和cherry pick C.再重复一遍:checkout X'和cherry pick D。

这可能不是最有效的解决方案,但它应该有效。

答案 3 :(得分:0)

对我来说看起来像XY problem,即询问您的尝试解决方案而不是实际问题

为什么不在从共同祖先开始的单独的bugfix-X分支上创建错误修正提交'X',并将其合并为'A','B'和'C'。例如在Junio C. Hamano博客文章“Resolving conflicts/dependencies between topic branches early

中描述了这个想法

这意味着创建以下历史记录:

  B---------b
 /          |
A-C-----c   |
|\      |   /
| D-d   /  /
\   |  /  /
 \X/--/--/

其中'b','c'和'd'是合并提交。


可以使用以下命令完成此操作:

  1. 首先,将(rebase)提交'X'提交到分支(提交)'B','C'和'D'的共同祖先。

    $ git rebase --onto $(git merge-base B C D) B X

    这将导致以下情况,错误修正提交'X'在单独的错误修复分支上:

      B
     /
    A-C
    |\
    | D
    \
     \X'
    
  2. 接下来,将错误修复程序合并到需要它的所有分支中。例如,合并到分支“D”可以通过以下方式完成:

    $ git checkout D
    $ git merge X

    这将导致:

      B
     /
    A-C
    |\
    | D---d
    \    /
     \X'/
    
  3. 对分支'C'和'B'执行相同操作。