Git rebasing无法正常工作?

时间:2015-10-13 22:55:12

标签: git mercurial rebase atlassian-sourcetree

根据我的观点,Git rebase似乎没有像我期望的那样工作 对变基的理解,以及我是如何看待变革工作的 水银。我已经生成了一个例子来说明奇怪的行为,而且我 希望有人能解释为什么Git的行为方式如此。考虑一下 发展议程集团的状况:

  

Starting State

在这种情况下,我已经在master上提交了f7和f8,但我想移动 这些新提交改为功能分支。即我做了提交 错误的分支,并想纠正错误。我可以从中执行rebase SourceTree:

  

Choose Rebase

SourceTree证实了我的意图:

  

Rebase Confirm

但结果根本不是我所期望的:

  

Rebase Result

虽然节点位于DAG中的正确位置,但分支头是 不正确!通过将f7和f8重新定位到f6,我希望看到主机重置为它 在原点上的位置,并期望功能/ AAA-1前进到f8。像这样:

  

Rebase Corrected

这是我期望的行为,基于Mercurial的变基,而且只是 通常基于变基所做的事情。为什么Git这样做,我该怎么做 让它表现得正确吗?

4 个答案:

答案 0 :(得分:4)

来自Mercurial,您预计提交永久附加到特定分支。也就是说,如果您设法单独提取某些提交,那么您可以说:

I am a commit on branch foo.
I change file bar.

Git不会这样工作:提交独立于任何分支(名称),事实上,分支名称 - 标签,如果你愿意 - 可以剥离并卡在其他地方威利-nilly。他们没有使用 1 ,只有人类试图解释这个烂摊子。

在Mercurial,当你" rebase"一些变更集,你(实际上至少)将它们作为差异转储到它们的基础上,然后你转换到你想要它们的另一个分支,并在另一个分支上进行新的提交。 Mercurial曾经(可能仍然如此)称之为第一步,#34;嫁接"。这些新的提交现在永久地附加到(并且仅限于)另一个不同的分支:

master:    f1 - f2 - f3 - f4 - f7 - f8
                             \
feature/AAA-1:                 f5 - f6

变为:

master:    f1 - f2 - f3 - f4 - f7 - f8
                             \
feature/AAA-1:                 f5 - f6 - 9 - 10

此时你可以安全撤消" f7和f8,将它们从master行中取出,你的rebase只在另一个分支上完成。

请注意,我在这里画左边的分支标签。这是安全的,因为所有提交都永久地粘在它们的分支上,所以一旦变更集在其分支的行上,它就总是在其分支的行上。唯一一次违反"变更集的行是在其分支的(单)行上进行的#34;规则用于合并,当变更集附加到(确切地)两个分支时:它位于其主分支上,但绘制与另一个分支的连接。

另一方面,在git中,提交可以被视为" on" 零个或多个分支(没有"正好是1或2"约束),而分支的一组提交是" on&#34 ; 是动态的,因为可以随时添加或删除分支名称。 (另请注意,单词" branch"有at least two meanings in git。)

Git的rebase与Mercurial的工作方式非常相似:它实际上复制提交。但是开始有一个重要的区别:副本不是特定的" on"任何分支(事实上,rebase进程在 no 分支上运行,使用git调用"分离的HEAD")。然后,最后会有一个更重要的区别。

和以前一样,我们可以从图形绘制开始,但这一次我会画出一点不同的颜色:

                     f7 <- f8   <-- master
                    /
f1 <- f2 <- f3 <- f4
                    \
                     f5 <- f6   <-- feature/AAA-1

这次,标签位于右侧,带箭头。名称master实际上直接指向提交f8,而它的f8指向f7f7指向{{1}等等。

这意味着,现在,f4f1的提交是&#34;&#34;&#34; 两个分支。使用git,最好说这些提交包含在&#34; (历史)两个分支。在这些提交中没有任何内容可以说明它们最初是哪个分支&#34;在#34;它们带有父指针,源树ID,作者和提交者名称(以及时间戳等),但是no&#34;源分支名称&#34;。 (我认为,来自hg的git的新人经常发现这非常令人沮丧。)

如果您现在要求git将f4f7重新绑定到f8,git将复制这两个提交:

feature/AAA-1

f7 <- f8 / f1 <- f2 <- f3 <- f4 \ f5 <- f6 <- f7' <- f8' 标记,或f7prime和f8prime,表示这些是原件的副本 - ' s,类似于hg的移植物)。但是现在我们得到了关键的区别,即绊倒你的那个:git now&#34;剥离&#34;原始git cherry-pick标签,并指向最尖端的新提交。这意味着最终图表如下所示:

master

Mercurial 不能执行此操作:分支标签无法剥离并在其他地方重新粘贴并重新粘贴。所以它没有,这就是为什么它的rebase工作方式不同。

在git中,你想在这里做的只是将两个提交选入 f7 <- f8 [abandoned -- was master] / f1 <- f2 <- f3 <- f4 \ f5 <- f6 <-- feature/AAA-1 \ f7' <- f8' <-- master 分支,然后将它们从feature/AAA-1分支中删除:

master

这里的想法是,你根本没有重新定位$ git checkout feature/AAA-1 $ git cherry-pick master~2..master # copy the commits $ git checkout master $ git reset --hard master~2 # back up over the originals ,而且你甚至都没有真正重新定义你的功能分支:相反,你只是将两个提交复制到您的功能分支,然后将其从主服务器中删除。

1 这有点夸大其词,因为在存储库之间传输 - mastergit fetch - 也使用分支和标记标签。此外,您需要对提交的一些引用以使它们保持活动状态,否则git的垃圾收集器最终会将它们作为“无法访问的”#34;。

答案 1 :(得分:0)

看起来你对变基不是很感兴趣。你只想移动分支指向新头。在这种情况下,这很容易。我不使用SourceTree,但在CLI中这很简单:

git checkout feature/AAA-1
git reset --hard master
git checkout master
git reset --hard origin/master

答案 2 :(得分:0)

您的期望是正确的,但我认为这种混淆只是在术语中。来自git-scm

  

在此示例中,您将运行以下命令:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
     

它的工作方式是去两个分支的共同祖先(你所在的那个分支和你正在重新定位的分支)......

注意在这种情况下使用“on”和“to”。在git中,你要重新定位的分支是在rebase之后将成为子树的分支。我怀疑你对SourceTree的使用只是git的反向。

答案 3 :(得分:0)

git rebase存在的目的是:您向feature/AAA提交了一些内容,同时其他人将更改推送到origin/feature/AAA。现在feature/AAA(当前)和origin/feature/AAA分歧。您可以合并它们,但项目规则声明历史应该是线性的。因此,您运行git rebase,当前分支feature/AAA在最新origin/feature/AAA上变为

你的情况不同。也许,可能有命令将提交从一个分支移动到另一个分支,明确命名两者,但它不是一个rebase。