0

从这个提交历史开始,有两个本地分支a并且b没有推送到原点:

C1 - C2 - C3 - C4 - C5
          |         |
          a         b

我想以这种情况结束,其中 C2+C5 是 C5 被压缩到 C2 之后的提交:

C1 - C2+C5 - C3 - C4 
              |   |
              a   b

问题是进行交互式 rebaseab分支分歧,在上述情况下没有办法结束。

要进行更改,我必须a在执行 rebase 后删除分支并重新创建它。有一个更好的方法吗?

4

2 回答 2

1

您执行交互式 rebase 以重新排序/压缩

git checkout b
git rebase --interactice C1
# in the editor you move C5 right after C2
# you set C5 to squash
# leave C3 and C4 with pick after C5
# save and let it run
# when rebase is finished:
git branch -f a b~

就是这样

于 2020-09-18T12:54:21.187 回答
1

这里有三件事要知道1当你进去时:

  1. 您实际上无法更改任何提交。在内部,即使是 Git 也无法做到这一点。Git 在这里必须做的是进行新的提交。

  2. 每个提交都有一个唯一的“数字”:它的哈希 ID。新的提交将有自己的唯一编号,与旧的(仍然存在的)提交不同,旧的(仍然存在的)提交将至少在您的存储库中保留一段时间。

  3. 一个分支名称,比如你的ab这里,2只是记住了以某种顺序最后一次提交的原始哈希 ID 。在您的情况下,a是您序列中的最后一次提交C1-C2-C3,并且b是您序列中的最后一次提交C1-C2-C3-C4-C5

所以真正发生的事情是你从......开始,我将使用单个大写字母而不是C1,C2等来绘制提交,这意味着我也会将分支名称更改为branch-aand branch-b。我们从这个开始:

A--B--C   <-- branch-a
       \
        D--E   <-- branch-b

我们现在保留原始提交A,但进行新的提交F,该提交是转变B为更改(通过比较Bvs A)和转变E为更改(通过比较Evs D),并结合这些更改并将它们全部应用于 commit 中的快照A

  F   <-- HEAD
 /
A--B--C   <-- branch-a
       \
        D--E   <-- branch-b

(这在内部使用 Git 的“分离 HEAD”模式,在 rebase 过程中,因此使用特殊名称直接HEAD定位提交F。)完成这个新提交后,Git 现在必须将现有提交复制C到新提交G,通过比较CvsB查看发生了什么变化并将这些更改应用于F. 结果是:

  F--G   <-- HEAD
 /
A--B--C   <-- branch-a
       \
        D--E   <-- branch-b

新提交G是您希望名称branch-a最后指向的位置,所以让我们继续复制提交——还有一个要复制——D变成H但又画了一个步骤:

       H   <-- HEAD
      /
  F--G
 /
A--B--C   <-- branch-a
       \
        D--E   <-- branch-b

这是变基过程的核心,对于任何类型的变基:它一次复制一个提交,就好像按git cherry-pick. 交互式风格的rebase 只是让您可以使用一组pick命令——单独的樱桃挑选指令——并修改它们以更改顺序或组合提交。3

在任何情况下,完成复制后,最后几个步骤git rebase涉及将分支名称拉到周围以指向最后复制的提交。在这种情况下,这是新的提交H,是由挑选旧的提交产生的D​​。特殊名称HEAD立即标识此提交,因此 Git 运行相当于:

git branch -f branch-b HEAD

强制名称branch-b指向此处:

       H   <-- branch-b, HEAD
      /
  F--G
 /
A--B--C   <-- branch-a
       \
        D--E   [abandoned]

然后,Git 重新附加HEAD到分支名称,退出它在变基期间内部使用的分离 HEAD 模式:

       H   <-- branch-b (HEAD)
      /
  F--G
 /
A--B--C   <-- branch-a
       \
        D--E   [abandoned]

现在你看到你所看到的提示了你的问题:有什么方法可以让 Git 在branch-a执行所有这些操作时移动名称?

答案是否定的。Git现在确实有一个更高级的版本git rebase -i,您可以使用git rebase -r. 为了使这个更好的版本工作,Git 在内部添加了实现这一目标所需的所有4 个工具——但还有更多工作要做,以一种可用的方式将这些工具粘合在一起。所以现在,在你完成你的之后,从他的回答git rebase -i中使用eftshift0 的最后一个git branch -f命令。这会强制移动另一个分支名称,因此我们得到:

       H   <-- branch-b (HEAD)
      /
  F--G   <-- branch-a
 /
A--B--C
       \
        D--E   [abandoned]

由于该名称branch-a实际上从未在此处删除,因此它的 reflog 保留了它曾经指向 commit的事实C,就像branch-b' reflog 保留了它曾经指向 commit 的事实一样E


1嗯,还有更多的事情要知道,但我要说的是这三个,因为它们特别相关。

2通常,明智的做法是避免在不添加至少一个超出此范围的其他字母或连字符或其他内容的情况下使用数字0through9和/或字母athrough构建分支名称。f原因是提交“数字”是用十六进制拼写的,就像一个大而难看的字符串,如e9b77c84a0a0df029f2a3a8114e9f22186e7da24. 这完全由该[0-9a-f]集合中的字符组成。如果你保持名称简短——三个字符或更少——你是“安全的”,所以分支名称ab它们本身都很好,cabbed. 但是一旦你得到四个或更多的字母,例如cafeordeafdecade: 那么,现在 Git 可能会尝试将该名称作为缩写的哈希 ID

如果一个名称用作一个或多个缩写的哈希 ID,则该名称可能被认为是模棱两可的:您是指该名称,还是该名称缩写的哈希 ID?由于字母r不能在集合中,因此分支名称beer很好,但beef可能不明确。

在实践中,这几乎不是问题,5如果你遇到了一个模棱两可的情况并且 Git 不正确地处理它,只需拼出一个更长的名称版本:分支名称beef是真的,如果输入太多refs/heads/beef,你可以缩写。heads/beefrefs/heads/beef

3当然,您可以做的不仅仅是这两个。通过一些额外的努力,Git可能会更容易一些,您可以将提交拆分为多个单独的提交。您可以完全放弃提交。您可以在复制步骤后让 Git 运行命令。但这是您目前正在使用的两个。

4或者可能是“最”:标签命令让我们大部分时间到达那里,但取决于我很确定其他人已经考虑过但尚未实施的事情,不清楚我们是否需要一个或两个新的脚本级别也为此命令。

5用这些拼写的单词并不多。在“cafebabe”和“deadbeef”之后,必须进入 133t-5p34k 之类的。不过,我想出了一个假标题:FADED ABACA DEFACE CAFE FACADE。

于 2020-09-18T21:04:01.437 回答