如何在交互式rebase之后重新附加git中的分支

时间:2014-08-03 10:45:14

标签: git branch git-rebase

我有一个相对简单的Git历史记录,大多数是一行,旁边有几个标签。然而,沿路有四个分支,进行了几次实验性改变。

所以,树看起来像这样:

HEAD | D | ... | C-C1-C2-<branch1-head> | ... | B-B1-<branch2-head> | ... | A-<some-tag> | ...

它是一个完全本地的存储库,因此没有像远程存储库这样的复杂功能。

最近我做了一个与提交有关的修正提交,真的非常深入了解历史(比如说图中的commit A)。然后我跑了

git rebase -i A~1

然后将修复提交压缩到提交A.Git愉快地重新运行所有75次提交,但随后历史变得完全错误。更改后的A提交后的所有提交都被复制到单独的提交行中,并且所有分支都保留在它们所在的位置,并附加到原始提交行。所以,我现在有HEAD指向原始提交行的克隆,但我当然不希望在repo中有这种重复。

  • 在交互式rebase中我不明白这会导致这个问题吗?

  • 我现在如何将分支重新附加到新的提交行,以便垃圾收集器能够清除旧的?除了手动更正提交对象以使它们指向新的父提交之外,还有其他选择吗?

1 个答案:

答案 0 :(得分:2)

jthill已经给出了答案as a commentgit rebase(无论是否交互)只需副本提交到链,然后移动(单个)分支标签到新链:

..- * - A - ... - B - ... - C - ... - D   <-- branch
                    \         \
                     ...       ...        [other branches]

变为:

        A'- ... - B'- ... - C'- ... - D'  <-- branch
      /
..- * - A - ... - B - ... - C - ... - D
                    \         \
                     ...       ...      [other branches]

其中' - 后缀变种是git rebase制作的副本(必须制作这些副本是由于git&#39; s内部设计,&#34;泄漏&#34;因为git的抽象并不是那么抽象。)

其他分支当然仍然附加到原始的,未经修改的提交。

要使这些其他分支重新基于新提交,您必须单独git rebase每个(制作更多副本,此时仅提交目前只能通过其他分支访问) - < em>或,而不是重新设置branch,你可以使用git&#39; filter-branch命令,类似git rebase类固醇。 1

git filter-branch的作用与rebase的作用非常相似:它会复制提交,允许您沿途进行更改(在最终确定每个新副本之前)。然后,也像rebase一样,它移动标签(至少分支,也可以选择标签,但你必须给它一个标签名称过滤器才能实现)。除了令人眼花缭乱的过滤器选项之外,git filter-branch执行git rebase的另一件事是它可以移动许多标签,而不仅仅是一个分支标签。

这里的缺点是git filter-branch是(或可能)非常难以使用。您需要详细了解git在每次提交时所做的事情。通过限制问题集,git rebase - 这对大多数用户来说已经不是最简单的命令 - 避免了这一点。

因此,您可能最好使用git rebase来移动branch1(基于C的链)和branch2B如果您真的想以这种方式重写提交历史记录,请分别在C'B'上。要做到这一点:

# Figure out which commit is original commit `C` and
# what its new copy `C'` is; put these into the commands
# below.

C=$(git merge-base branch1 'branch@{yesterday}')
git show $C               # make sure you have the right one
git log --oneline branch  # find the SHA-1 for C'
C1=badcafe                # or whatever its SHA-1 is

顺便提一下,'branch@{yesterday}'表示法是查看reflog以查看昨天branch结束的地方(可能是在图中找到了提交D),而不是branch。 {1}}今天结束(branch的提示现在提交D',这不是您需要的内容)。如果你知道原始提交D的原始SHA-1,或者可以通过其他方式找到它(例如通过运行git rev-parse ORIG_HEAD),这是获得它的另一种方式。

您必须手动为C1找到正确的SHA-1。上面的git log --oneline将显示每个复制提交的短消息;您可以将它们与git show $C的输出进行比较。如果您有多个候选人(因为有几个简短日志,只是说&#34;修复错误&#34;例如),您可能需要使用更多git show命令来查找C1 。但我假设你可以找到副本,并设置了C1

# now that we have the IDs of C ($C) and C' ($C1), use
#    git rebase --onto
# to re-base branch1 (the chain coming off original C)
# onto commit $C1, and then move label "branch1"
git rebase --onto $C1 $C branch1

现在我们只为您呼叫B的{​​{1}}链重复此过程:

branch2

如果您应用于提交B=$(git merge-base branch2 'branch@{yesterday}') git show $B git log --oneline branch B1=d0gf00d # or whatever the correct copy SHA-1 is git rebase --onto $B1 $B branch2 的修正(导致新提交A)干扰了您在A'branch2中所做的任何更改, branch1会暂停,让您手动恢复,然后让您使用git rebase完成更改。

(如果你试图用git rebase --continue来完成所有这些,你实际上必须提供一种处理任何此类冲突的脚本方法。这是做事的其中一件事git filter-branch如此困难。)


1 我认为对此最有趣的是人们已经认为git filter-branch是非常类固醇的。 git rebase命令基本上运行rebase s。

的自动化系列