从多个git rebase中恢复“旧提交”

5

我知道这个问题,但不确定如何将其映射到我的当前情况。(Rebase很可怕,撤销rebase更加可怕!)

我最初从我的主分支创建了几个不同的特性分支:

master    x-x-x-x-x-x-x-x-x-x
             \       \    \
FeatureA      1-2-3   \    \
FeatureB               A-B  \
FeatureC                     X-Y-Z

我想把它们合并在一起,并在合并回主分支之前检查它们是否工作正常,因此我执行了以下操作:

git checkout FeatureB
git rebase FeatureA
git mergetool //etc
git rebase --continue

那么

git checkout FeatureC
git rebase FeatureB
git mergetool //hack hack
git rebase --continue

这让我只剩下

master    x-x-x-x-x-x-x-x-x-x
             \
FeatureA      1-2-3 
                   \
FeatureB            A'-B'
                         \ 
FeatureC                  X'-Y'-Z'

然后我纠正了一些编译不正确的部分,并将整个功能集合调整到了可接受的状态:

master    x-x-x-x-x-x-x-x-x-x
             \
FeatureA      1-2-3 
                   \
FeatureB            A'-B'
                         \ 
FeatureC                  X'-Y'-Z'-W

我的问题是,我的同事告诉我我们还没有准备好使用 FeatureA。

有没有办法让我保留所有工作,但同时回到一个可以将 FeatureC 变基到 Feature B 的情况?


我其实很困惑你为什么要进行变基。你应该从主分支或featureC创建一个新的分支(比如叫featuresABC),并将每个分支合并到它上面,保留各个特性分支的独立历史记录是很好的做法。 - Cascabel
1
@Jefromi,因为我还在学习中... - Benjol
@Jefromi,我已经根据您的评论添加了我的理解作为答案,如果您想检查(甚至认领它),请查看。 - Benjol
不用了,就接受你自己的回答吧。我的意图是帮助将托比亚斯的答案转换为被接受的答案,但他似乎没有回来检查过,所以这全归你了。你写得很好。 - Cascabel
3个回答

5

这是我根据评论得出的答案理解:

当您进行rebase操作时,当前分支上的提交将被“撤消”,然后“重新应用”,但实际上它们并没有被撤消,而是被“记忆”*,并使用新的ID重新应用。例如,如果我查看git reflog show FeatureB,我会得到类似于以下内容:

7832f89 FeatureB@{0} rebase finished: refs/heads/FeatureB onto f4df3
efd3fed FeatureB@{1} commit: B
f3f3d43 FeatureB@{2} commit: A
2f32fed FeatureB@{3} branch: Created from HEAD

正如@Jefromi所说,原始数据仍然存在(reflog中A和B提交的SHA与git log中对应的A'和B'不同)。

同样地,git reflog show FeatureC的输出如下:

32873ef FeatureC@{0} commit: W
17dafb3 FeatureC@{1} rebase finished: refs/heads/FeatureC onto 89289fe
893eb78 FeatureC@{2} commit: Z
a78b873 FeatureC@{3} commit: Y
e78b873 FeatureC@{4} commit: X
378cbe3 FeatureC@{5} branch: Created from HEAD

再次强调,原始的Z、Y和X提交记录仍然存在。

因此,解决我的问题的方法是从master的HEAD创建一个新的分支FeaturesBC,然后挑选FeatureB{2 & 1}和FeatureC{4, 3, 2}的提交记录,并且(可能)还有W:

git checkout master
git checkout -b FeaturesBC
git cherry-pick f3f3d43 
git cherry-pick efd3fed 
//etc...

看起来已经成功了,我不得不重新做一些相同的合并,但情况并不太糟糕。

编辑,来自Jefromi:

可能并不需要挑选。您也可以简单地在rebase之前重新创建分支:

git branch FeatureB-old efd3fed
git branch FeatureC-old 893eb78

或者,如果您想放弃FeatureB和FeatureC的变基位置,回到它们之前的位置:

git branch -f FeatureB efd3fed
git branch -f FeatureC 893eb78

最后,需要注意的是,如果您愿意,可以使用reflogs提供的其他表示方法 - 例如,使用FeatureC@{2}而不是使用893eb78。这意味着“FeatureC”的上一个位置是倒数第二个。但是请注意,只有在查看完reflog之后立即使用它,因为一旦再次更新分支(移动它,向其提交...),FeatureC@{2}将指向17dafb3。

正如@Jefromi在我的问题中评论的那样:

您应该从主分支或featureC创建一个新分支(称为featuresABC),并将每个分支合并到其中,保留各个功能分支的独立历史记录。

*准确地说,旧的提交对象仅保留在存储库中。它们最终将被修剪,因为您不希望存储库充满旧的悬空提交;这将在第一次运行git gc且提交至少两周后(由gc.pruneExpire配置)时发生。


1

如果其他方法都失败了,你可以从主分支重新开始,并使用 git cherry-pick 来重建那些分支的所有 B 或 C 的提交。希望如果这是唯一的解决方案,有人已经写了一个脚本...


说实话,在开始之前,我备份了整个代码库,所以我并不完全挂了,但我还是想避免再次重做整个变基的事情... - Benjol
我的理解(来自这里https://dev59.com/mnE85IYBdhLWcg3wnU6p)是B和C的提交已经被rebase修改了,所以那样做行不通,或者我漏掉了什么? - Benjol
@Benjol:你说得部分正确:rebased提交确实是原始提交的修改版本(具体来说,它们具有不同的父提交和可能略有不同的补丁),但原始提交仍然存在。最简单的方法可能是使用git reflog show FeatureA找到它们 - 这比查看HEAD的reflog并确定哪些行属于哪个分支要容易一些。 - Cascabel
@Benjol cherry-pick 只会应用差异,因此除非几个功能包含相同的更改,否则差异(而不是提交)仍然相同。 - Tobias Kienzler

1

你可以使用git rebase --onto <来自B的旧合并基础> A C将从C到A的所有内容rebase到主分支上的某个点。 这将留下:

master    x-x-x-x-x-x-x-x-x-x
             \       \
FeatureA      1-2-3   \
                       \
FeatureB                A'-B'
                            \ 
FeatureC                     X'-Y'-Z'-W

要找到您想要变基的点,您可以使用git的reflog和git merge-base的组合 - 但您也可以重新基于A的合并基础,以获得类似以下历史记录:

master    x-x-x-x-x-x-x-x-x-x
            |\ 
FeatureA    | 1-2-3
             \
FeatureB       A'-B'
                    \ 
FeatureC             X'-Y'-Z'-W

(git rebase --onto $(git merge-base A master) A C)

git rebase --onto $(git merge-base A master) A C


当然,这一切都能正常工作,但只要旧的未基于重置的提交仍然存在于代码库中,就没有必要重新进行重置。(这只会增加出错的机会,并且不必要地刷新提交日期。) - Cascabel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接