使用其他分支撤消对较早提交的修改 TL; DR 长

假设我已git rebase进入了存储库的分支b2,并修改了较早的提交(c1)。此提交未经修改地存在于另一个分支b1上(并且c1之后的两个分支上都有一些更常见的提交,然后b2b1有所不同)。

现在,我想使用基本上撤消对c1b2的修订。我应该如何做,以便两个分支的历史再次变得最大相同?

jjj0716 回答:使用其他分支撤消对较早提交的修改 TL; DR 长

TL; DR

使用git rebase --onto。使用--onto参数指定目标,并使用通常的 upstream 参数指定要复制的提交 not 。从这里很难确切地说出这些论点应该是什么。请参阅下面的详细讨论。

此要求:

...两个分支的历史再次变得完全相同

意味着您必须知道git rebase是通过复制提交起作用的,这一点很重要。为了快速回顾一下想法,请注意:

  • 每个提交都具有唯一的哈希ID;
  • 每个提交都保存所有文件的快照;和
  • 每个提交还保存一些元数据。

提交中的元数据不仅包括作者,提交者和日志消息,而且是整个过程的关键-提交的父级的哈希ID。

要将某项提交转变为变更集,即找出某人在任何给定提交中所做的更改,我们让Git将提交与其父项进行比较。提交存储了其父级的哈希ID,因此git showgit log -p可以自己找到它。

同时,像b2这样的分支名称仅保存分支中 last 提交的哈希ID。这样我们就可以绘制它们(提交和分支名称),如下所示:

... <-c1 <-c2 <-c3    <--b2

其中每个 c i 是由其散列表示的实际提交,并且从某处出来的箭头表示指向:分支名称b2指向提交c3c3指向c2c2指向c1,依此类推。

关于任何提交的任何事情都不会改变,因此我们可以绘制从提交到提交的内部箭头,而不是箭头,而是连接线,只要我们记得它们是孩子的一部分并向后指向父对象即可。这使我们可以在原始文本图形中绘制多个分支。我将使用X1及更高版本,因为这只是一个示例,与您的起点不完全相关:

...--X1   <-- branch1
       \
        X2--X3   <-- branch2

如果branch1获得了更多提交,则最新的最终会导致返回到X1

...--X1--X4--X5   <-- b1
       \
        X2--X3   <-- b2

现在回到您的原始设置:

假设我已经对存储库的分支b2进行了git变基,修改了较旧的提交(c1)。此提交未经修改地存在于另一个分支b1上(并且c1之后的两个分支上都有一些更常见的提交,然后b2b1有所不同)。

我会尽可能准确地绘制原始设置的图片:

                    c4--c5   <-- b1
                   /
...--c0--c1--c2--c3
                   \
                    c6--c7   <-- b2

(我用猜测填充了缺失的细节,尽管最后猜测应该没有多大关系。)

当您在git rebase -i <start-point>上运行b2并修改/编辑提交c1时,Git必须复制 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'的差异与从c2c1的差异相同。也就是说,如果将c2与它的父c1进行比较,就会得到一些变化。如果将c2'c1'进行比较,即使c1c1'的内容不同,我们也会得到相同的更改

现在将c2替换为c2',这迫使Git也将c3复制到c3'。这迫使Git也复制c6c7。最终复制的提交是c7'git rebase的开头是 name 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]

如果您最终得到:

,可能没问题(通常容易得多)。
                    c4--c5   <-- b1
                   /
...--c0--c1--c2--c3--c6"-c7"-c8'-c9'  <-- b2 (HEAD)

没有引入任何放弃的提交。 1

要获取其中任何一个,您想要:

  • 告诉Git至少复制c8c9(假设它们存在),并且
  • 告诉Git 通过c1'复制c3'

这意味着使用git rebase --onto,以便您可以分隔git rebase通常合并的两条指令。

也就是说,git rebase的作用是:

  1. 枚举一些要复制的提交列表。在上面的示例中,它们是c1c7。如果您使用git rebase -i,则该列表将进入可修改的说明表,其中使用单词pick以及每个提交的哈希(缩短)和提交日志消息中的主题行。

    通常,合并提交(具有两个或多个父级的提交)会立即从列表中弹出。 (有些模式不能拒绝它们;它们更复杂,我们在这里将忽略它们。)

    列出哪些提交?这是基于您的 upstream 参数:要进行复制的提交是从HEAD可以通过向后走来到达的那些,提交以提交:{{ 1}}回到c7,然后再跳回到c6,再回到c3,依此类推。但是,从该列表(可能会追溯到很长的时间)中,我们删除 c2 参数可以到达的所有提交。因此,如果 upstream upstream的哈希ID,我们将从列表中删除c0 ,以及所有提交在c0之前。这意味着列表以c0开头,以c1结尾,并跳过了无法到达的c7c4

  2. c5选择目标。如果使用--onto,则直接选择它。如果不是,则使用 --onto 参数选择它。例如,对于upstream,上游是git rebase master,而master目标是名称--onto指向的提交。 Git在此处(或内部等效项)执行master,以使您要复制的提交脱离分支。

  3. 开始复制,就像一次git checkout --detach一样。一些变基操作实际上使用git cherry-pick,而有些则没有。

  4. 复制完成后,移动原始的分支名称,使其指向git cherry-pick,这是最后复制的提交,或者-如果我们未复制毕竟是任何提交-HEAD目标。然后回到该分支,就像通过--onto

使用git checkout name可以更改 --onto 参数,而无需设置基准的目标。

因此,如果您要复制 just upstreamc8,则可以通过检查得知c9目标是--onto,并且您不想要复制的第一个提交是c7。毕竟,这不需要c7。如果您拥有git rebase --onto的哈希ID,则可以运行:

c7

在分支git rebase <hash-of-c7> 上。但是要找到原始的b2,在第一次重新设置基准之前,您必须仔细阅读reflog。这可能很困难,因为reflog往往包含很多动作,并且一旦复制了一次提交,就可能复制了很多次。 2

因此,我们可以让Git再次复制c7c6'。我们将 c7' 设置为upstream作为要复制的第一个提交 not ,并将c3'设置为{{1 }}目标:

c3

例如。 Git将通过从--ontogit rebase --onto <hash-of-c3> <hash-of-c3'> )向后走直到到达HEAD(您说不可以复制(之前也没有复制))来枚举提交。这将列出c9c3'c6'c7'作为要复制的提交。副本将放在c8c9)之后。请注意,提交c3--onto在Git绘制的历史记录和原始ASCII图中都很容易看到,您可以使用以下视图查看:

c3

因此,这为您提供了c3'和上游参数的哈希ID。


如果您希望以后再返回它们,则

1 git log --graph --oneline b1 b2 及其废弃的历史记录仍在Git存储库中。可以通过Git的 reflogs 找到它们。 reflog条目仅持续一段时间。默认情况下,在1到3个月后,reflog条目将过期并被删除。一旦发生这种情况,被遗弃的提交本身也可以被删除,之后,您 至少不能通过自己的Git将它们找回。

(reflog和reflog到期的细节有些复杂,但在这里并不重要。)

2 要查看分支--onto的引用日志,请运行c9。如果幸运的话,副本不多,随机运动也不多,您可以以这种方式找到b2

本文链接:https://www.f2er.com/2768584.html

大家都在问