Git 改变变基

Git Changing rebase

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

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

我 "git rebase bugfix feature" 是为了用错误修复测试我的功能

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

现在我需要在没有错误修复的情况下为我的功能分支创建拉取请求,所以我做了 "git rebase master feature" 并期望:

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

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

需要注意的是,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。原始提交仍然存在,但没有任何指向它们。他们最终会被清理干净,但他们会在那里呆几天。

这意味着您可以撤消变基,这正是您想要做的。您需要找到 feature 在变基之前的位置。这可以通过 git reflog 来完成,它会在每次 HEAD 移动时进行跟踪。 checkoutcommitresetrebase 都会发生这种情况。 git 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

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


至于你的具体问题,问题是在变基之后你的存储库现在看起来像这样:

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

当您询问 git rebase master feature 时,Git 注意到 master 已经是 feature 的祖先并且什么都不做。介于两者之间的错误修复并不重要。

相反,您需要告诉 Git 您只想变基 4A、5A 和 6A 而忽略 7。这是使用 --onto 语法完成的。

git rebase --onto master bugfix feature

这表示要从但不包括错误修复改基到 master 上。

我建议使用 git reset 而不是尝试重做变基。不能保证第二次变基会产生相同的结果,尤其是在存在冲突的情况下。而使用 git reset,您将明确返回到存储库的旧状态。

I thought rebase would only carry the commits in 1 branch to another but looks like it's not.

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

Git的分支与大多数其他版本控制系统有很大不同。分支 "contains" 仅凭借能够 "reach" 从分支名称指向的提交提交。 masterbugfixfeature 等分支名称仅指向 一个特定的 提交,Git 调用 tip 的分支。通过让每个提交 "point back" 到其前身,提交本身形成了一条链。

因此,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,例如,如果我们愿意的话,但是这种技术即将 运行失去动力)。

虽然你仍然可以得到你想要的。你只需要 copy D-E-F again,或者——这对于这种特殊情况可能更好——回到废弃的,原文4-5-6.

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

git log ORIG_HEAD

您可能会认出您的原件。

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

因此:

git log feature@{1}

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

要取回它,请使用 git reflog feature 查看哪个数字进入 @{...} 部分,然后 on feature ( git checkout feature), 运行:

git reset --hard feature@{1}

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

(假设您没有任何要签入的内容,即 git status 表示一切都是干净的,因为 git reset --hard 清除了尚未签入的索引和工作-树改变了。)