假设我已经git rebase
创建了存储库的分支b2
,并修改了较早的提交(c1
)。此提交未经修改地存在于另一个分支b1
上(c1
之后的两个分支上都有一些更常见的提交,然后b2
与b1
分叉)。
[现在,我想用来基本上撤消对c1
的b2
的修正。我应该如何做,以便两个分支的历史再次变得最大相同?
使用git rebase --onto
。使用--onto
参数指定目标,并使用通常的[[upstream
参数指定要复制的提交not。从这里很难确切地说出这些论点应该是什么。请参阅下面的详细讨论。
...两个分支的历史再次变得完全相同
意味着您知道git rebase
通过
copying
提交而变得很重要。要快速回顾一下想法,请注意:parent
git show
或git log -p
可以自己找到它。同时,像b2
这样的
分支名称
仅保存分支中last提交的哈希ID。因此,我们可以绘制它们(提交和分支名称),如下所示:... <-c1 <-c2 <-c3 <--b2
其中每个分支名称c i是由其散列表示的实际提交,箭头出来表示指向:
b2
指向提交c3
,c3
指向c2
,c2
指向c1
,依此类推。关于任何提交的任何事情都不会改变,因此我们可以绘制从提交到提交的内部箭头,而不是箭头作为连接线,只要我们记住它们是孩子的一部分并向后指向父对象即可。这使我们可以在原始文本图形中绘制多个分支。我将使用X1
及更高版本,因为这仅是示例,与您的起点不完全相关:...--X1 <-- branch1
\
X2--X3 <-- branch2
如果branch1
获得更多提交,则最新的最终会导致返回X1
:
...--X1--X4--X5 <-- b1 \ X2--X3 <-- b2
现在返回您的原始设置:假设我已经对存储库的分支b2
进行了git变基,修改了较旧的提交(c1
)。此提交未经修改地存在于另一个分支b1
上(c1
之后的两个分支上都有一些更常见的提交,然后b2
与b1
分叉)。我将尽力绘制原始设置的图片:
c4--c5 <-- b1 / ...--c0--c1--c2--c3 \ c6--c7 <-- b2
((我用猜测填充了缺失的细节,尽管最后猜测应该没有多大关系。)当您在
git rebase -i <start-point>
上运行b2
并修改/编辑了提交c1
时,Git必须copy
c1
进行一些新的和不同的提交。新提交具有与往常一样的作者和日志消息,它们最初是设置为从c1
复制的,但是它具有新的和不同的哈希ID以及不同的快照和/或不同的日志消息(甚至是不同的作者) ),具体取决于您所做的更改。让我们将新副本称为c1'
来区分它们: c4--c5 <-- b1
/
...--c0--c1--c2--c3
\
c1' [copying in progress]
因为复制c1
被复制到c1'
,所以现在Git被[[强制
c2
到新的c2'
: c4--c5 <-- b1
/
...--c0--c1--c2--c3
\
c1'-c2' [copying in progress]
从c1'
到c2'
的差与从c2
到c1
的差相同。也就是说,如果比较c2
与它的父级c1
,我们将获得一些更改。如果将c2'
与c1'
进行比较,即使c1
与c1'
的内容不同,我们也会得到相同的changes
。
现在c2
被c2'
替换,这也迫使Git也将c3
复制到c3'
。这也迫使Git也复制c6
和c7
。最终复制的提交为c7'
,并通过将name
git rebase
拖过去来完成b2
,因此最终结果为: c4--c5 <-- b1
/
...--c0--c1--c2--c3--c6--c7 [old b2, now abandoned]
\
c1'-c2'-c3'-c6'-c7' <-- b2 (HEAD)
现在,我想使用基本上撤消对b2上的c1的修改。我应该如何做,以便两个分支的历史再次变得最大相同?
此后,您可能还添加了更多提交:
c4--c5 <-- b1
/
...--c0--c1--c2--c3--c6--c7 [old b2, now abandoned]
\
c1'-c2'-c3'-c6'-c7'-c8--c9 <-- b2 (HEAD)
您可能最终想要得到的是:
c4--c5 <-- b1 / ...--c0--c1--c2--c3--c6--c7--c8'-c9' <-- b2 (HEAD) \ c1'-c2'-c3'-c6'-c7'-c8--c9 [abandoned]
如果最终得到以下结果,可能就可以了(通常
do容易很多:]
c4--c5 <-- b1
/
...--c0--c1--c2--c3--c6"-c7"-c8'-c9' <-- b2 (HEAD)
1
要获得以上任何一个,您想要:
告诉Git复制至少c8
和c9
(假设它们存在),和
c1'
至c3'
git rebase --onto
,因此您可以分离git rebase
通常合并的两条指令。也就是说,git rebase
的作用是:c1
至c7
。如果您使用git rebase -i
,则该列表将进入可修改的说明表,其中使用单词pick
以及每个提交的哈希(缩短)和提交日志消息中的主题行。合并
提交(具有两个或多个父级的那些提交)会立即从列表中弹出。 (有些模式不能拒绝它们;它们更加复杂,在这里我们将忽略它们。)哪些提交被列出?这是从您的[[upstream
HEAD
到达的那些,提交以提交:c7
引回到c6
,该跳转回到c3
,再回到c2
,依此类推。但是,从该列表(可能会追溯到很长的时间)中,我们remove从upstream
参数可到达的所有提交。因此,如果upstream
是c0
的哈希ID,我们将从列表中获取c0
away,以及在c0
之前的所有提交。这意味着列表以c1
开头,以c7
结束,跳过了无法到达的c4
和c5
。
--onto
选择目标。如果使用--onto
,则直接选择。如果不是,则使用upstream
git rebase master
,上游是master
,--onto
目标是名称master
指向的提交。 Git在此处(或内部等效项)执行git checkout --detach
,以便使用要复制的提交从分支中退出。git cherry-pick
一样一次提交。一些基准操作实际上使用git cherry-pick
,而有些则没有。复制完成后,移动原始的HEAD
,这是最后复制的提交,或者-如果我们毕竟没有复制任何提交,则[ C0]目标。然后回到该分支,就像通过--onto
一样。
使用git checkout name
可以更改
--onto
参数,而无需同时设置基准的目标。因此,如果要复制just
upstream
和c8
,则可以通过检查得知c9
目标是--onto
,并且第一个提交您不要] >想要复制的是c7
。毕竟,这不需要c7
。如果您有可用的哈希ID git rebase --onto
,则可以运行:c7
在分支git rebase <hash-of-c7>
上。但是要找到原始的b2
,在第一次重新设置基准之前,您必须仔细阅读reflog。这可能很困难,因为reflog往往包含很多动作,并且一旦复制了一次提交,就可能已经复制了很多次。2
c7
和c6'
。我们将c7'
设置为upstream
作为要复制的第一个提交not,并将c3'
设置为c3
目标:
--onto
例如。 Git将通过从git rebase --onto <hash-of-c3> <hash-of-c3'>
(HEAD
)向后走直到到达c9
来枚举提交,您说过不要复制(也没有更早的内容)。这将列出c3'
,c6'
,c7'
和c8
作为要复制的提交。副本要放在c9
(c3
)之后。请注意,提交--onto
和c3
在Git绘制的历史记录和原始ASCII图中都很容易看到,您可以使用以下命令查看:c3'
git log --graph --oneline b1 b2
和上游参数的哈希ID。2要查看分支1找到它们。 reflog条目仅持续一段时间。默认情况下,在1到3个月后,reflog条目将过期并被删除。一旦发生这种情况,被放弃的提交本身也可以被删除,然后,您--onto
及其放弃的历史记录仍在您的Git存储库中,如果您想稍后再返回它们的话。可以通过Git的reflogs
无法将它们取回,至少不通过您自己的Git。(reflog和reflog的详细信息有些复杂,但在这里并不重要。)
c9
的引用日志,请运行b2
。如果幸运的话,副本不多,随机运动也不多,您可以用这种方式找到git reflog b2
。