我的假设是解决方案很简单,但我找不到任何明确解决我问题的例子。我寻求以下指导:
我目前有一段历史:
A - - - C - - - - - F Master
\ \ \
B - - - D - E - - - G - H Topic
D
,E
,G
和H
应该全部发生在Master
上。此时唯一的区别应该是提交B
。所以看起来应该是这样的:
A - - - C - D - E - F - G - H Master
\
B Topic
甚至更好:
A - C - D - E - F - G - H Master
\
B Topic
......但我也不知道如何实现这一目标。
似乎我不能同时合并D
,E
,G
和H
。我一直在B
中以Master
结束。
我相信我可以挑选它们,但这似乎是未来的问题。什么是最好的解决方案?
答案 0 :(得分:1)
如果您已将这些分支机构推送到任何远程仓库,那么重写过去的历史记录并不是一个好主意。如果你推了,我会通过这样做避免改写:
Master
' F
HEAD。D
上挑选E
,G
,H
和Master
。Topic
并从Master
合并。如果你这样做,你将拥有:
A - - - C - - - - - F - - - - - D' - E' - G' - H'- Master
\ \ \ \
B - - - D - E - - - G - H - - - - - - - - - - - - Topic
历史不会看起来超级整洁,但你会在你想要的分支中拥有所有代码。
如果您真的想重写历史记录,因为这只是您本地的,或者您不在乎与可能从您的回购中提取的其他人混淆,那么:
C
(在回滚之前记下F
的哈希值)。D
上挑选E
,F
,G
,H
和Master
。Topic2
' Master
头的H
分行。B
上的Cherry-pick Topic2
。Topic
,将Topic2
重命名为Topic
。你得到:
A - C - D - E - F - G - H Master
\
B Topic
答案 1 :(得分:1)
假设图纸准确无误,“你无法从这里到达那里”。 (尽管如此,你可能已经足够接近。)具体来说,绊脚石是提交F
。
您标记为F
当前位于分支Master
的提交已提交C
作为其父级。
在新的所需图表中,标记为F
的提交已将提交E
作为其父级。
由于提交是其所有内容(其树,消息,作者/提交者和时间戳,以及 - 这里的问题 - 所有其父ID)的加密校验和,因此任何新的“F-like”将E
作为其父级的提交必然是不同的提交。
“更好的”图表显示以B
作为其父级的提交H
,因此这与“现有”图表不同。因此,这与F
具有相同的问题(具有相同的潜在解决方案)。
分支标签Master
和Topic
也必须更改。这两种变化都不会是“快进”(用git术语)。
您需要回答的问题是:除了您自己以外还有谁可以访问此存储库或此存储库的副本,并且他们(所有此类访问者)是否愿意为处理非快进操作所需的操作分支标签改变?
如果答案是“没有其他人有这样的访问权”,或者“他们都愿意做所需的事情”,你就没事了。如果没有,你需要另一种方法。让我们假设现在最好。
你仍然无法从这里到达那里,因为我们需要对你提交的内容进行更改F
,并且永远不能更改提交。您也无法更改D
和E
,虽然您不需要直接更改H
,但您需要更改G
- 它目前有两个父母 - 并改变“泡沫通过”:我们会在一瞬间看到为什么父母ID会发生变化。
您需要做的是将提交复制到新的,略有不同的版本。我们会将提交D
复制到一个版本,该版本会忽略B
的任何更改,并且只有C
作为其父级。我们将此副本称为D'
,以表明它与“D
非常相似,但不完全相同”。
然后,我们会将E
复制到与旧D
- 到 - E
具有相同更改的版本,但会将D'
列为其父级。
接下来,我们会发现C
和F
之间发生了什么,并对E'
中的内容进行了相同的更改,并将其作为F'
提交,并使用相同的消息等,与原始F
中一样。
最后,我们希望F
和G
之间发生的更改(不 E
到G
之间的更改)应用于F'
已提交G'
,并在G
中使用相同的消息等提交G
;然后我们将更改从H
更改为G'
,将其应用到H'
,然后提交为A---C---D'--E'--F'--G'--H' <-- Master
\ \
\ \-----F [old Master was here]
\ \ \
B----D--E--G--H <-- Topic
。
结果看起来像这样(我们还不太喜欢你不太喜欢的选择):
Topic
现在要让B
在B
结束,我们只需将其重新指向提交A---C---D'--E'--F'--G'--H' <-- Master
\ \
\ \-----F
\ \ \
B----D--E--G--H
`.......................<-- Topic
即可。剩下的提交仍然在存储库中,但没有指向它们的标签(除了reflog之外):
B
我们现在拥有您想要的不太优选的选择。
要获得您的首选替代方案,我们现在只需将Master
重新定位到B
。这会将B'
复制到与旧A
- 到 - B
具有相同更改的新提交C
(忽略由于进入{{}}而不再需要的任何提交H
{1}}到H'
),但A---C---D'--E'--F'--G'--H' <-- Master
\
B' <-- Topic
为其父级。重新绘制图形,省略所有不再标记的提交,给出:
git cherry-pick
至于实际复制提交的机制,那么,执行此操作的工作是git rebase -i
。使用-p
(没有-m
),你可以让git做一系列的挑选。但是,合并提交有一些绊脚石。交互式rebase可以省略简单的合并,但是对于更复杂的合并,您需要向git cherry-pick
提供git checkout -b temp Master^ # make new "temp" branch pointing to commit C
git cherry-pick -m 2 Topic~3 # get C->D changes, make D'
git cherry-pick Topic~2 # get D->E changes, make E'
git cherry-pick Master # get C->F changes, make F'
git cherry-pick -m 2 Topic~1 # get F->G changes, make G'
git cherry-pick Topic # get G->H changes, make H'
参数,以告诉它选择合并的哪一侧。
所以,从现有的起点来看,我就是这样做的(有很多可能的方法):
git show
一路上或最后,在每次提交时使用-m
以确保您获得了所需的内容(并且我在此处Master
参数正确)。
如果一切顺利,请移动分支temp
以匹配新的git checkout master; git reset --hard temp # reset Master to equal temp
git branch -d temp # and delete temp, no longer needed
:
Topic
现在您可以重置并重新定位git checkout Topic; git reset --hard HEAD~4 # reset to point to B
git rebase Master # copy B to B' and reset to B'
:
git checkout -b temp
(略微更高级的技巧:代替git checkout
,只需Master
并使用分离的HEAD模式构建所需的新{{1}},然后手动移动。这就是交互式rebase的方式shell脚本做到了。但它更令人困惑,所以最好是明确的,如上所述。)
如果您有其他人使用这些分支机构,他们必须通过自己的重置和/或重组从“非快进”更改中恢复。该方法在git rebase documentation中被描述为“从上游rebase中恢复”。