为什么git rebase会丢弃我的提交?

时间:2014-04-01 15:49:07

标签: git git-rebase

我试图在主人之上改变一个分支,这是我以前做过一千次的事情。但是今天,它没有工作:

> git status
On branch mystuff
Your branch and 'master' have diverged,
and have 6 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

nothing to commit, working directory clean

> git rebase
First, rewinding head to replay your work on top of it...

> git status
On branch mystuff
Your branch is up-to-date with 'master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    [a directory from the project]

nothing added to commit but untracked files present (use "git add" to track)

>

一切都像平常一样开始,但是Git完成了rebase而没有在我那里提交任何提交;我的分支mystuff最终与master相同。

显而易见的结论是,我的提交已经在某处掌握。但我发誓他们不会 。我已经回顾了历史。提交在其他几个功能分支上,但它们并不在任何地方的主历史中。 (而且我可以告诉他们,当我签出主人的时候,文件的状态还不是主人。)

那么,如果提交已不在我的上游历史记录中,那么为什么git rebase会拒绝将我的提交堆叠在最顶层?

奇怪的是,如果我一个接一个地将这些提交给主人,那就行了。然后我可以移动我的mystuff分支到最后,然后回到原来的位置。 (但为什么我需要这样做?)

修改

git rebase上的文档说明了这一点:

  

如果提供了<upstream>选项,则当前分支将重置为<newbase>--onto。这与git reset --hard <upstream>(或<newbase>)具有完全相同的效果。 ORIG_HEAD设置为在重置之前指向分支的顶端。

     

然后,先前保存到临时区域的提交将按顺序逐个重新应用于当前分支。请注意,HEAD中引入与HEAD..<upstream>中的提交相同的文本更改的任何提交都将被省略(即,将跳过已使用不同提交消息或时间戳的上游接受的修补程序)。

这与我看到如果提交实际存在于上游的行为一致......但他们不会这样做。并且如评论中所述,git rebase master正常工作并应用所有提交。但git rebase没有master的情况也没有,即使将master设置为上游分支。

我的分支机构的配置:

[branch "master"]
    remote = origin
    merge = refs/heads/master
[branch "mystuff"]
    remote = .
    merge = refs/heads/master

3 个答案:

答案 0 :(得分:4)

在某次git升级后,这已经让我至少被咬了十几次。 现在是git rebasegit rebase master之间的差异:前者被更改为使用相同的花式“fork-point”机制。回答这个问题有一个详细的解释:

今天我第一次想出了重现它的具体步骤。

我的情景:master我有4次提交我已经决定现在应该进入topic分支,我想重新排序它们。如果我这样做......

  1. 创建新的topic分支,跟踪当前分支(master

    git checkout -b topic -t
    
  2. 快退master

    git checkout master
    git reset --hard origin/master
    
  3. topic

    上的提交重新排序
    git checkout topic
    git status  # "Your branch is ahead of 'master' by 4 commits" Good!
    git rebase --interactive
    
  4. ...然后交互式rebase屏幕出现了这个不祥的提交列表:

        # no-op
    

    呃 - 哦......我保存文件并继续。是的,看起来git再次抛弃了我的工作。 topic现在指向与masterorigin/master相同的提交。

    所以我猜想是什么触发了这个:

    1. 升级git

    2. 您在master分支上执行了类似我的第2步的操作。

    3. 在我的伙计们的理解中,fork-point机制通过reflog搜索并注意到这些提交已从上游分支中删除,并得出结论:使您的主题分支“更新“他们也应该被移除。

      解决方案是,而不是:

      git rebase
      

      使用以下其中一项:

      git rebase --no-fork-point
      git rebase master
      

      但我怀疑和我一样,你不会每次都这样做。 (我的意思是,我们设置一个上游分支是有原因的,对吗?)所以你只需要学会识别灾难发生的时间,使用git refloggit reset --hard进行恢复,然后然后< / em>使用上面的命令。

      但是,你现在需要小心 - 我认为这是非常危险的。我曾经有一段时间我重新组建了一个大型分支机构,并且一些提交从一开始就默默地消失了,我几天都没注意到!我很乐意挖掘git reflog进行灾难恢复,但我不确定每个人都是。我想知道git是否已经开始聪明了。

答案 1 :(得分:0)

运行git branch -vv以查看上游分支。听起来mystuff的上游不是你想象的那样。也许您遇到了编辑事故并最终在配置中出现了多个[branch "mystuff"]条目?

答案 2 :(得分:0)

我本来想自己问这个问题,但当我试着问的时候,找到了答案。

当您需要变基时,您可能会遇到两种情况。考虑以下历史记录:

A---B---C master

协作者 #1 更改提交 C,添加提交 D 并执行 git push -f

A---B---C'---D master

合作者 #2 添加提交 E 并执行 git fetch

A---B---C'---D origin/master
     \
      C---E master

您可能会说,“B 是最后一个共同祖先,C 和 C' 不同,我想保留所有更改。”那么您预期的历史记录是:

A---B---C'---D---C"---E" master

另一种思考方式是,“另一个人改变了 C 提交,一定是有原因的,我想要这个新版本的 C 来代替我的。”然后你想到的是:

A---B---C'---D---E" master

这意味着当提交在上游被删除时,当您进行 rebase 时会在本地发生。

当您明确指定上游时选择前一种模式:

$ git rebase origin/master

后者(“--fork-point”模式):

$ git -c pull.rebase=true pull
$ git pull --rebase
$ git rebase
$ git rebase --fork-point origin/master

给出了更详细的描述here。重现问题的 script