【问题标题】: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 可以正常工作并应用所有提交。但是没有master 的git rebase 不会,即使master 被设置为上游分支。

我的分支配置:

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

【问题讨论】:

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

标签: git git-rebase


【解决方案1】:

在某次 git 升级后,这至少让我痛苦了十几次。 git rebase 和 git 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 现在指向与 master 和 origin/master 相同的提交。

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

  1. 升级 git

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

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

解决方案是,而不是:

git rebase

使用以下之一:

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

但我怀疑你不会像我一样每次都这样做。 (我的意思是,我们设置上游分支是有原因的,对吗?)所以您只需学会识别灾难何时发生,使用 git reflog 和 git 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
      相关资源
      最近更新 更多