我在git的repo中有如下的分支。
_d_e_f__(branch A)
(Master)_a_b_c__/
_g_h_i__
(branch B)
之后,从a创建新的分支C并将B合并到Cgit checkout A && git checkout -b C && git merge B
_d_e_f__(branch A)
(Master)_a_b_c__/ __________(branch C)
_g_h_i__/
(branch B)
现在,假设主分支添加了一个新的提交,我想将所有分支(a,B,C)重置到主分支的新提交。正确的做法是什么?我已经尝试了以下,一切都很好,除了当我检查图形,然而,从分支B合并到C似乎没有在rebase后集成。
git checkout A
git rebase master
git checkout B
git rebase master
git checkout C
git rebase --onto A f
从图中我可以看到,一个分离的提交(m)是为了合并B到c而创建的。
_m__C
A___/
master_____/
B
TL;DR:考虑使用石墨。我自己没有使用过Graphite,但是请阅读下面的文本,看看它是否与您的目标相匹配,然后查看链接项,可能是一种跳出框的方法。
您可以使用(1)新奇的--rebase-merges
选项和(2)随后的分支名称更新来实现您的目标。这取决于你的最终目标在我写这个答案的时候,从你的问题中我并没有完全清楚这个目标。
执行此操作的命令顺序为:
git checkout branch-C
git rebase --rebase-merges master
git log --graph # to find hash IDs below
git branch -f master <hash1>
git branch -f branch-A <hash2>
git branch -f branch-B <hash3>
,其中两个哈希id只能在git rebase --rebase-merges
完成后运行git log
才能发现。
这里有几件事要记住:
Rebase通过复制提交来工作,就像
git cherry-pick
一样。(--rebase-merges
所做的这种重基实际上是在内部使用git cherry-pick
;有些人有时会使用替代命令,这些命令可能运行得更快,但在某些情况下效果不佳。)每个复制的commit "做同样的事情"作为每个原始提交,并重用其原始提交的日志消息,但有一个新的,不同的哈希ID。
cherry-pick命令不能复制合并提交,所以
--rebase-merges
选项甚至不会尝试这样做。相反,在复制了作为原始合并输入的提交之后,rebase-merges代码运行一个全新的git merge
命令来创建新的合并。因为这是一个全新的合并,所以在原始合并期间所采取的任何特殊操作都将丢失。当且仅当启用了
git rerere
时,以前的冲突解决方案才会保留(这将在即将发布的Git版本中出现,但在大多数现有版本和旧版本中不会出现)。您使用的任何特殊标志,如-X ours
或-X theirs
,都将丢失。
这意味着我们开始,说:
D-----E-----F <-- branch-A
/
A--B--C <-- master M--N--O <-- branch-C (HEAD)
/
G-----H-----I <-- branch-B
其中branch-A指向提交F
,branch-C
指向提交O
,branch-B
指向提交I
,master
指向提交C
。
我们在开始rebase之前检出branch-C
,并使用--rebase-merges
,这样Git就会生成一组列出要复制/合并的提交的指令。这组指令如下:
- 移动到
C
作为一个分离的头。 - 复制
D
、E
、F
. - 保存新拷贝
F'
的哈希ID - 返回起点
C
- 复制
G
、H
、I
. - 保存新拷贝
I'
的哈希ID - 签出
F'
并合并I'
(生成M'
) - 复制
N
、O
和P
.
有一个隐含的最后指令,将名称branch-C
移动到指向这里所做的最后一次提交。1如果您将--interactive
添加到这个rebase中,您将看到一个文本版本的指令表,Git将允许您编辑它。如果没有,Git就使用指令表开始工作,即使没有--interactive
,它也会生成和解释指令表,因为rebase可能会在中间停止。如果是这样,您可能会稍后恢复它,为此它需要保存这些指令。(rebase还会运行"已完成的工作"one_answers"下一步要做的工作",以配合待办事项指示;这里的确切机制取决于rebase代码,并且随着时间的推移而改变。
如果一切顺利,最终的结果是,在之后的所有指令——包括最后和隐含的"移动分支名称";Step-have been被执行,这是:
D-----E-----F <-- branch-A
/
C <-- master M--N--O ???
/ /
| G-----H-----I <-- branch-B
/
A--B
| D'----E'----F'
/
C' M'-N'-O' <-- branch-C (HEAD)
/
G'----H'----I'
也就是说,这个git rebase --rebase-merges
已经完成了所需的所有复制,因为master
、branch-A
和branch-B
只需要在复制完成后指向C'
、F'
和I'
提交。
1在写这篇文章的时候,我突然想到,如果把它作为一个明确的指令会更好。在未来的Git中,这仍然可以完成。