使用其他分支撤消对较早提交的修订

问题描述 投票:0回答:1

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

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

git git-rebase branching-and-merging
1个回答
1
投票

TL; DR

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

此要求:

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

意味着您知道git rebase通过

copying

提交而变得很重要。要快速回顾一下想法,请注意:
    每个提交都有一个唯一的哈希ID;
  • 每个提交都保存所有文件的快照;和
  • 每个提交还包含一些元数据。
  • 提交中的元数据不仅包括作者,提交者和日志消息,而且是整个过程的关键-提交

    parent

  • 的哈希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必须

    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'的差与从c2c1的差相同。也就是说,如果比较c2与它的父级c1,我们将获得一些更改。如果将c2'c1'进行比较,即使c1c1'的内容不同,我们也会得到相同的

    changes

    现在c2c2'替换,这也迫使Git也将c3复制到c3'。这也迫使Git也复制c6c7。最终复制的提交为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复制至少c8c9(假设它们存在),和

      告诉Git到
    • 不是
    • 复制c1'c3'
  • 这意味着使用git rebase --onto,因此您可以分离git rebase通常合并的两条指令。也就是说,git rebase的作用是:

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

      合并

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

    哪些提交被列出?这是从您的[[upstream

    参数得出的:要进行[[复制的提交是通过向后走可以从HEAD到达的那些,提交以提交:c7引回到c6,该跳转回到c3,再回到c2,依此类推。但是,从该列表(可能会追溯到很长的时间)中,我们remove

    upstream参数可到达的所有提交。因此,如果upstreamc0的哈希ID,我们将从列表中获取c0 away,以及在c0之前的所有提交。这意味着列表以c1开头,以c7结束,跳过了无法到达的c4c5

    --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

    upstreamc8,则可以通过检查得知c9目标是--onto,并且第一个提交您

    不要] >想要复制的是c7。毕竟,这不需要c7。如果您有可用的哈希ID git rebase --onto,则可以运行:c7

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

    因此我们可以让Git再次复制c7c6'。我们将
    c7'
    设置为upstream作为要复制的第一个提交

    not,并将c3'设置为c3目标:

    --onto

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

    因此,这为您提供了git log --graph --oneline b1 b2 和上游参数的哈希ID。

    1 --onto及其放弃的历史记录仍在您的Git存储库中,如果您想稍后再返回它们的话。可以通过Git的

    reflogs

    找到它们。 reflog条目仅持续一段时间。默认情况下,在1到3个月后,reflog条目将过期并被删除。一旦发生这种情况,被放弃的提交本身也可以被删除,然后,您
    无法将它们取回,至少不通过您自己的Git。

    (reflog和reflog的详细信息有些复杂,但在这里并不重要。)

    2要查看分支c9的引用日志,请运行b2。如果幸运的话,副本不多,随机运动也不多,您可以用这种方式找到git reflog b2
    © www.soinside.com 2019 - 2024. All rights reserved.