Git rebase一个没有遥控器的本地分支

时间:2015-09-28 20:30:53

标签: git git-rebase

我创建了一个本地分支(git checkout -b),其中我做了一些脏提交(" WIP"等等)。我从没推过遥控器。我现在想在推送之前重命名/压缩一些提交:

$ git rebase -i
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details

    git rebase <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> feature/position-preview
然后我按照建议做了:

$ git branch --set-upstream-to=origin/feature/my-feature feature/my-feature
error: the requested upstream branch 'origin/position-preview' does not exist
hint:
hint: If you are planning on basing your work on an upstream
hint: branch that already exists at the remote, you may need to
hint: run "git fetch" to retrieve it.
hint:
hint: If you are planning to push out a new local branch that
hint: will track its remote counterpart, you may want to use
hint: "git push -u" to set the upstream config as you push.

问题是,我不想原样push -u,因为这会导致糟糕的历史。

如何重新定位新的本地分支?

2 个答案:

答案 0 :(得分:2)

Git的文档是much derision的主题......有充分的理由:

  

git rebase [-i | --interactive] [options] [--exec&lt; cmd&gt;] [--onto&lt; newbase&gt;] [&lt; upstream&gt;并[d分支&GT;]]

此处的参数标记为upstream,命令的有用(?)输出引用git branch --set-upstream-toorigin/<branch>,这意味着您的本地分支必须具有远程分支像origin/my-feature这样的工作。但事实并非如此。

git rebase做的是复制一系列提交,每个副本都会发生,就像您在一个特定提交上运行git cherry-pick一样。通过在提交图中的某些线性提交链中复制每个提交,您可以获得复制开发分支的效果。 1 这表示更有意义我认为是图表形式。例如,假设你有这个序列:

... <- o <- A <- B    <-- master
         \
           C <- D <- E   <-- my-feature

其中每个字母代表某个特定的提交 - 其中一个大的丑陋的40个字符的SHA-1号码 - my-feature的提交历史记录与提交时master的提交历史记录一起加入o。这是您在my-feature上创建新分支master时获得的图表,然后返回分支master,进行两次提交,然后转到{ {1}}并做了三次提交(你提交每次提交的实际顺序并不重要:我只需要你在my-feature上做两次,然后我把它们拉出来 / em>作为第一个master和第二个A,以及B上的三个。

现在,正如在开发过程中发生的那样,您可能会认为根据my-feature的当前提示C - 通过 - E更有意义,即拥有master的父提交为C

实现这一目标的一种非常手动的方法是:

B

这会产生一个新分支$ git checkout master $ git checkout -b new-my-feature $ git cherry-pick <id of commit C> $ git cherry-pick <id of commit D> $ git cherry-pick <id of commit E> ,并复制三个提交, 2 ,结果如下:

new-my-feature

git构造新提交 C' - D' - E' <-- new-my-feature / ... o - A - B <-- master \ C - D - E <-- my-feature 的方法是将树 - 相关的源快照 - C'与其父提交C进行比较,将相同的更改应用于树对于o,并进行重新使用B的提交消息的新提交。换句话说,提交C是&#34;副本&#34; C'。来自C的{​​{1}}和来自D'的{​​{1}}也是如此。

在任何情况下,如果您拍摄第三张照片然后删除D分支标签并将E'重命名为E,则会得到:

my-feature

new-my-feature上的原始三次提交已丢失&#34; (他们通过&#34; reflog&#34;条目保留30天,但这些通常是不可见的,以避免混淆你的观点。)

my-feature做的是自动执行此过程:它找到要复制的提交序列,将序列复制到其他提交,然后移动分支标签。

您可以使用所有非常好的文档记录选项指定的是分支名称 - 默认值是您当前的分支 - 并且通常在某种程度上间接地指示停止复制。 &#34;目标&#34; commit,rebase调用 C' - D' - E' <-- my-feature / ... o - A - B <-- master ,默认为你为refase调用my-feature提供的参数(如果有的话)。

这种方式依赖于理解其他相当重要的东西,这会影响双点符号。

让我们再次回到原始图表,然后再进行樱桃采摘和分支标签移动,即我们在变形之前所拥有的内容:

git rebase

现在让我们问一下--onto <newbase>中的修改内容:

<upstream>

(我做了这些,但事实上这个设置你真的只有三个SHA-1)。

我认为解释这个问题的好方法是填充颜色/突出显示(当然我在这里可以做到这一点,所以你必须想象一下)。

想象一下,您从... - o - A - B <-- master \ C - D - E <-- my-feature 的提示开始,即提交master..my-feature。将此提交描绘为绿色:它将被使用&#34;。然后,按照其父指针返回提交$ git rev-list master..my-feature e59f6c2d348d465e3147b11098126d3965686098 8413a79e67177d026d2d8e1ac66451b80bb25d62 1f9e0a53489aaca7859722e037a47e93858cbc42 。绘制此提交绿色也是如此。然后按照my-feature绘制绿色;然后按照E并将其绘制为绿色,并在整个图表中一直继续D的父级。

然后,从C的提示开始,即提交o。将此提交描绘为红色:&#34;停止&#34;。然后,按照其父级提交o,并将其绘制为红色。然后按照master,用红色油漆涂在绿色油漆上:&#34;停止&#34;。一直跟着B的父母,用红色油漆覆盖他们所有的绿色油漆。

(如果您愿意,只要您用绿色覆盖任何红色,您可以先做红色,然后再做绿色。)

结果 - A应该列出的提交 - 正是那些(仍然)绿色的提交,这只是我们想要重新设置的三个提交。即使我们启动了红色列表&#34;来自提交oo的提示。

所有这一切的简短版本是,为了进行变基,我们想要的 - 至少现在 - 是git rev-listB视为&#34;上游&# 34 ;. &#34;上游&#34; rebase的论据提供了&#34; red paint&#34;参数,master修订说明符的左半部分, git rebase参数:我们想要复制提交,以便它们在{{{ 1}}。

事实上,您可以使用mastermaster..my-feature(在您自己的私人存储库中)设置为&#34;上游&#34; --onto。上游不必是master之类的远程跟踪分支。你自己当地的分支机构在这里工作正常。 (但请记住,如果您开始共享此分支 - 将其推送到远程git存储库,如git branch --set-upstream-to - 您可能希望在此时开始使用远程跟踪分支作为&#34;上游& #34;从那以后,提醒自己你正在分享,并获得更典型的rebase流程。你可以通过做一个新的{{1}来改变&#34;上游&#34; }。)

1 这个词&#34;分支&#34;是git文档问题的另一个例子。它至少有两种不同的含义。在图表中,这是脚注的段落的一部分,master是一个&#34;分支&#34; (我喜欢称之为&#34;分支标签&#34;或&#34;分支名称&#34;)。同样,my-feature是一个分支。在这种情况下,这两个都只分别标记一个特定的提交 - origin/branchorigin。但是,每次提交都有自己的父提交指针:git branch --set-upstream-to指回mastermy-feature指回B,依此类推。通过遵循这些父指向箭头产生的提交链也是一个&#34;分支&#34;。

停止呼叫这样一个连锁店的确切地点&#34;分支&#34;取决于您的观点:E可能会在提交B处停止,而A可能会继续通过E。 Git在这里并没有多大帮助,尽管反正总是没有正确答案:它确实取决于你想要包含的内容。

2 这样做的方法稍微容易一些,因为D可以将多个提交作为参数:

my-feature
例如,

或:

o

使用the gitrevisions documentation中描述的说明符来获取git以找到三个ID。

答案 1 :(得分:1)

问题是,默认情况下,不带参数的rebase建议从推送的最后一次提交中进行rebase。然而,我们可以朝着正确的方向推动它:

git rebase -i <hash>

之前选择之前的<hash>到你想要在历史记录中看到的那个。这些哈希值可以通过git log获得。