我的提交历史如下所示:
B-X
/
A-C
\
D
我想申请所有X
分支机构B
,,,C
。D
虽然cherry-pick
成功了,但它创造了这样的历史:
B-X
/
A-C-X
\
D-X
现在commitX
重复了3次,如果我有很多分支,这很不方便。理想情况下,我希望历史看起来像这样:
B
/
A-X-C
\
D
哪里X
只出现一次。这段历史要干净得多。我应该使用哪个命令来实现这一点?
我的提交历史如下所示:
B-X
/
A-C
\
D
我想申请所有X
分支机构B
,,,C
。D
虽然cherry-pick
成功了,但它创造了这样的历史:
B-X
/
A-C-X
\
D-X
现在commitX
重复了3次,如果我有很多分支,这很不方便。理想情况下,我希望历史看起来像这样:
B
/
A-X-C
\
D
哪里X
只出现一次。这段历史要干净得多。我应该使用哪个命令来实现这一点?
第一个要点是您请求的修复将重写历史记录,这只是在没有其他人使用分支时您应该做的事情。例如,如果您移动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
别人指出的樱桃采摘策略效果很好。只是想指出你也可以使用一组带有 --onto 标志的变基来做这种事情,它不需要临时分支。
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
这里唯一的规定是 with B
。无论字母代表分支名称还是 SHA1 提交,上述顺序都是有意义的。但是,如果B
并且X
实际上在一个分支中,那么您需要签出该分支并将其倒回到X
创建之前。为了示例,我们将分支称为“蜜蜂”。
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”之前的提交来处理。
实际上, 、 和 中的提交B
也C
将D
克隆其所有提交,并且它们都将具有新的 SHA1 哈希值。
这就是为什么在与其他开发人员共享提交后重新设置基准、挑选或修改提交可能很麻烦的原因。如果这些开发人员引入克隆,他们的本地 repo 将认为它的新工作,并且他们将获得重复的提交,并且因为这些提交做完全相同的事情,他们将获得冲突。然后克苏鲁将崛起并开始统治世界。
解释这一点的困难是由于在这些示例中,我们使用分支名称,就好像它们是提交一样。如果这个问题专门关于修改历史的副作用,我会添加更准确的图表。
一种可能的解决方案是使用多个樱桃挑选来做到这一点。结帐 A 并挑选 X 来创建
B-X
/
A-X'
\
\-C
\
D
现在你应该在 X' 上,这样你就可以挑选 B:
B-X
/
A-X'-B'
\
\-C
\
D
结帐 X' 和樱桃挑选 C。再重复一次:结帐 X' 和樱桃挑选 D。
这可能不是最有效的解决方案,但它应该可以工作。
对我来说,它看起来像XY 问题,即询问您尝试的解决方案而不是您的实际问题。
为什么不在从共同祖先开始的单独的 bugfix-X 分支上创建 bugfix commit 'X',并将其合并到 'A'、'B' 和 'C' 中。例如 Junio C. Hamano 博客文章“尽早解决主题分支之间的冲突/依赖关系”中描述了这个想法
这意味着创建以下历史:
B---------b
/ |
A-C-----c |
|\ | /
| D-d / /
\ | / /
\X/--/--/
其中“b”、“c”和“d”是合并提交。
这可以通过以下命令完成:
首先,将(rebase)提交“X”移动到分支(提交)“B”、“C”和“D”的共同祖先上。
$ git rebase --onto $(git merge-base B C D) B X
这将导致以下情况,错误修复提交“X”在单独的错误修复分支上:
B
/
A-C
|\
| D
\
\X'
接下来,将错误修复合并到所有需要它的分支中。例如合并到分支'D'可以通过以下方式完成:
$ git checkout D
$ git merge X
这将导致:
B
/
A-C
|\
| D---d
\ /
\X'/
对分支“C”和“B”执行相同的操作。