我有一个相对简单的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中我不明白这会导致这个问题吗?
我现在如何将分支重新附加到新的提交行,以便垃圾收集器能够清除旧的?除了手动更正提交对象以使它们指向新的父提交之外,还有其他选择吗?
答案 0 :(得分:2)
jthill已经给出了答案as a comment:git 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
的链)和branch2
(B
如果您真的想以这种方式重写提交历史记录,请分别在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。