在这种情况下,我已经在master上提交了f7和f8,但我想移动 这些新提交改为功能分支。即我做了提交 错误的分支,并想纠正错误。我可以从中执行rebase SourceTree:
SourceTree证实了我的意图:
但结果根本不是我所期望的:
虽然节点位于DAG中的正确位置,但分支头是 不正确!通过将f7和f8重新定位到f6,我希望看到主机重置为它 在原点上的位置,并期望功能/ AAA-1前进到f8。像这样:
这是我期望的行为,基于Mercurial的变基,而且只是 通常基于变基所做的事情。为什么Git这样做,我该怎么做 让它表现得正确吗?
答案 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
指向f7
和f7
指向{{1}等等。
这意味着,现在,f4
到f1
的提交是&#34;&#34;&#34; 两个分支。使用git,最好说这些提交包含在&#34; (历史)两个分支。在这些提交中没有任何内容可以说明它们最初是哪个分支&#34;在#34;它们带有父指针,源树ID,作者和提交者名称(以及时间戳等),但是no&#34;源分支名称&#34;。 (我认为,来自hg的git的新人经常发现这非常令人沮丧。)
如果您现在要求git将f4
和f7
重新绑定到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 这有点夸大其词,因为在存储库之间传输 - master
和git 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。