【问题标题】:Why is git rebase discarding my commits?为什么 git rebase 会丢弃我的提交?
【发布时间】:2014-04-01 15:49:06
【问题描述】:

我正在尝试在 master 之上重新建立一个分支,我之前已经做过一千次了。但是今天,它不起作用:

> 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 提交相同的提交。

显而易见的结论是我的提交已经在某个地方的 master 中。但他们不是,我发誓。我已经回顾了历史。提交 在其他几个功能分支上,但它们不在任何地方的 master 历史记录中。 (而且我可以通过文件的状态来判断他们不在 master 中,当我有 master 签出时。)

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

奇怪的是,如果我一个接一个地挑选提交到 master 上,那是可行的。然后我可以将 mystuff 分支移动到最后,然后将 master 恢复到原来的位置。 (但为什么我需要那样做呢?)

编辑

git rebase 上的文档是这样说的:

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

然后将之前保存到临时区域的提交按顺序一一重新应用到当前分支。请注意,HEAD 中引入与 HEAD..&lt;upstream&gt; 中的提交相同的文本更改的任何提交都将被忽略(即,将跳过已在上游接受的具有不同提交消息或时间戳的补丁)。

这与我看到的行为一致如果提交实际上存在于上游......但他们没有。正如 cmets 中所述,git rebase master 可以正常工作并应用所有提交。但是没有mastergit rebase 不会,即使master 被设置为上游分支。

我的分支配置:

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

【问题讨论】:

  • git rebase 返回后的git status 是什么?
  • 当您执行git rebase mastergit rebase master &lt;branch&gt; 时会发生什么?
  • 是否可以共享整个目录?如果你确定你做的很好,它可能有助于找到一个错误或任何东西。
  • @Irineau,请参阅编辑。未跟踪的目录是因为未重新设置的提交之一是更改 .gitignore 以忽略该目录。
  • @Cupcake,好问题。 git rebasegit rebase --onto master 都像上面一样失败......但是 git rebase master 有效!但为什么?如果 master 是上游,那么 git rebase 应该在没有分支参数的情况下工作,不是吗?

标签: git git-rebase


【解决方案1】:

在某次 git 升级后,这至少让我痛苦了十几次。 git rebasegit rebase master 现在区别:前者改为使用相同的花哨的“叉点”机器。对这个问题有详细的解释:

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

我的场景:我在 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
    

...然后交互式变基屏幕出现了这个不祥的提交列表:

    # no-op

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

所以我想是什么触发了你:

  1. 升级 git

  2. 你在 master 分支上做了类似于我的步骤 2 的操作。

在我的外行的理解中,fork-point 机器在 reflog 中搜索并注意到这些提交已从上游分支中删除,并得出结论,使您的主题分支“更新”它们也应该在那里删除。

解决方案是,而不是:

git rebase

使用以下之一:

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

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

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

【讨论】:

  • 下一个问题:是否有 git 配置选项可以将行为改回来? (奖励积分:可能只针对当地分支机构?)搜索git help时我找不到提及。
  • 啊哈!是的,这的问题。它可以从“通知提交已从那里删除”重新表征为“通知提交以前与上游共享​​>,然后假设他们在那里被“改进的副本”取代”。没有自动的 --no-fork-point 选项,但您可以添加一个:查看 rebase 脚本,在第 454 行附近,使用 test "$fork_point" = auto &amp;&amp; fork_point=t。替换为“如果设置为自动,请检查配置以获取最终值”。
  • --no-fork-point 并没有改变我的结果,但这个解释仍然有帮助。在使用 reflog 恢复我的提交后,我最终只是挑选了我对我从 master 制作的新分支的提交。
  • 最后,我通过将 git reup 别名为 git rebase @{u} 来避免这种情况,并且在不调用该别名或指定不同的上游分支的情况下永远不会变基。
  • 你为什么要让master 成为topic 的上游? O.o
【解决方案2】:

运行git branch -vv 以查看上游分支。听起来mystuff 的上游不是你想的那样。也许您发生了编辑事故,导致配置中有多个 [branch "mystuff"] 条目?

【讨论】:

    【解决方案3】:

    我本来打算自己问这个问题,但是当我试图问的时候,找到了答案。

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

    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

    【讨论】:

      猜你喜欢
      • 2021-04-16
      • 2017-11-04
      • 1970-01-01
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      • 2011-07-03
      相关资源
      最近更新 更多