Git改变rebase

时间:2017-04-06 23:22:03

标签: git rebase

我有3个分支:master,feature,bugfix ...并且提交看起来像这样:

    4-5-6(feature)
    |
1-2-3(master)
    |
    7(bugfix)

我做了" git rebase bugfix功能"用bugfix测试我的功能

    1-2-3(master)
        |
        7(bugfix)-4-5-6(feature)

现在我需要为我的功能分支创建一个不带错误修复的拉取请求,所以我做了#34; git rebase master feature"期待:

1-2-3(master)-4-5-6(feature)
    |
    7(bugfix)

相反,它说功能是与主人最新的。这是真的,但我不想在那里合并提交7。 我可以做rebase交互并删除提交,但我想知道是否有更好的方法来做到这一点。 我认为rebase只能将1个分支中的提交带到另一个分支,但看起来并非如此。

2 个答案:

答案 0 :(得分:2)

要意识到的是,rebase不会重写历史记录或移动提交,Git中的提交无法更改。相反,它创造了新的历史,并说它一直都是这样。例如,当你开始:

    4-5-6(feature)
    |
1-2-3(master)
    |
    7(bugfix)

然后git rebase bugfix feature真正发生的是:

    4-5-6
    |
1-2-3(master)
    |
    7(bugfix)-4A-5A-6A(feature)

提交了三个新提交,4A,5A和6A。原始提交仍然存在,但没有任何指向他们。他们最终会被清理干净,但他们会留在那里好几天。

这意味着你可以撤消一个rebase,这正是你想要做的。你需要在rebase之前找到feature的位置。这可以通过每次git reflog移动时跟踪的HEAD来完成。这种情况发生在checkoutcommitresetrebasegit reflog可能类似于:

65e93ca (HEAD -> feature) HEAD@{0}: rebase finished: returning to refs/heads/feature
65e93ca (HEAD -> feature) HEAD@{1}: rebase: 3 feature
6d539a3 HEAD@{2}: rebase: 2 feature
3cd634f HEAD@{3}: rebase: 1 feature
b84924b (bugfix) HEAD@{4}: rebase: checkout bugfix
a9fd2f1 HEAD@{5}: commit: 3 feature
29136bc HEAD@{6}: commit: 2 feature
60543b0 HEAD@{7}: commit: 1 feature
c487530 (master) HEAD@{8}: checkout: moving from master to feature

这告诉我a9fd2f1是在重新定位之前最后一次提交功能。我可以重新移动功能,而不是重做rebase。

git checkout feature
git reset --hard a9fd2f1

将来,如果在执行rebase之前git tag功能的原始位置,这种事情会变得容易多了。然后,您可以git reset返回该标记,而无需搜索reflog。

关于您的具体问题,问题是在rebase之后您的存储库现在看起来像这样:

6A [feature]
|
5A
|
4A
|
7 [bugfix]
|
3 [master]
|
2
|
1

当你问git rebase master feature时,Git注意到master已经是一个功能的祖先,什么都不做。 bugfix介于两者之间并不重要。

相反,你需要告诉Git你想要仅修改4A,5A和6A并忽略7.这是使用--onto语法完成的。

git rebase --onto master bugfix feature

这就是说要将错误修正,但不包括在主人身上。

我建议使用git reset而不是尝试重做rebase。不能保证第二个篮板会出现同样的情况,特别是如果有冲突的话。而使用git reset,您将明确地移回存储库的旧状态。

答案 1 :(得分:2)

  

我认为rebase只会将1个分支中的提交带到另一个分支但看起来不是。

这是关键:您的图表中的提交7在分支feature。它也在分支bugfix中。提交1-2-3所有三个分支中。

Git的分支与大多数其他版本控制系统截然不同。分支“包含”提交只是因为能够“到达”从分支名称指向的提交提交。像masterbugfixfeature这样的分支名称只是指向一个特定的提交,Git调用该分支的提示 。通过让每个提交“回头”到它的前身,它就是构成链的提交本身。

因此,git rebase实际副本提交:您来自:

        4--5--6   <-- feature
       /
1--2--3      <-- master
       \
        7      <-- bugfix

为:

        4--5--6   [abandoned - used to be feature]
       /
1--2--3      <-- master
       \
        7      <-- bugfix
         \
          D--E--F   <-- feature

其中D是原始4副本E5的副本,而F6的副本(我在这里使用了第4,第5和第6个字母,所以我们可以将7复制到G,例如,如果我们想要,但这种技术即将耗尽。)

但你仍然可以得到你想要的东西。你只需要复制 D-E-F 再次,或者 - 对于这个特殊情况,这可能更好 - 只需回到被遗弃的原始4-5-6

当您使用git rebase复制提交时,原件会一直存在。您可以通过两个名称找到它们:ORIG_HEAD reflog 名称。名称ORIG_HEAD被其他各种命令覆盖,但您可以检查它是否仍然指向提交6

git log ORIG_HEAD

你可能会认出你的原件。

reflog名称的格式为name@{number},例如feature@{1}。每次更改 number 部分指向的提交时, name 部分都会递增,因为Git只会保存的当前值在reflog中name ,将其余部分推到一个档次。

因此:

git log feature@{1}

应该向您显示与git log ORIG_HEAD相同的提交,但feature@{1}更长时间(可能会变成feature@{2}feature@{3}等等,随着时间推移)。默认情况下,每个名称的先前值至少保存30天,因此应该有足够的时间将其恢复。

要将其恢复,请使用git reflog feature查看@{...}部分中的哪个号码,然后 on featuregit checkout feature ),运行:

git reset --hard feature@{1}

或任何数字(虽然首先用git log再次验证是个好主意)。

(这假设您没有要检查的内容,即git status表示一切都很干净,因为git reset --hard消除了尚未检入的索引和工作树更改。)