88

我们都听说过永远不应该对已发布的作品进行变基,这很危险,等等。但是,我还没有看到任何有关如何处理变基发布的情况的食谱

现在,请注意,只有当存储库仅由已知(最好是小)的一群人克隆时,这才是真正可行的,所以无论谁推送 rebase 或 reset 都可以通知其他人他们下次需要注意拿来(!)。

如果您没有本地提交foo并且它被重新定位,我见过的一个明显的解决方案将起作用:

git fetch
git checkout foo
git reset --hard origin/foo

foo根据远程存储库,这将简单地丢弃本地状态以支持其历史记录。

但是,如果一个人在该分支上进行了大量的本地更改,那么如何处理这种情况呢?

4

3 回答 3

75

在大多数情况下,在推送 rebase 后恢复同步实际上并没有那么复杂。

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

IE。首先,您为远程分支最初所在的位置设置一个书签,然后使用它从该点开始将本地提交重播到重新定位的远程分支上。

变基就像暴力:如果它不能解决你的问题,你就需要更多。☺</p>

当然,如果您查找 pre-rebaseorigin/foo提交 ID 并使用它,您可以在没有书签的情况下执行此操作。

这也是您在获取之前忘记制作书签的情况的处理方式。什么都没有丢失——你只需要检查远程分支的 reflog:

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

origin/foo这将打印在最近一次获取更改其历史记录之前指向的提交 ID 。

然后你可以简单地

git rebase --onto origin/foo $commit foo
于 2010-11-03T07:08:40.390 回答
11

我想说从 git-rebase 手册页的上游 rebase 部分恢复几乎涵盖了所有这些。

这实际上与从您自己的变基中恢复没有什么不同——您移动一个分支,并将其历史记录中的所有分支变基到其新位置。

于 2010-11-03T15:39:19.630 回答
11

从 git 1.9/2.0 Q1 2014 开始,您不必在将其重新基于重写的上游分支之前标记之前的分支来源,如Aristotle Pagaltzis的回答中所述:
请参阅提交 07d406b提交 d96855f

在使用topic创建的分支上工作后git checkout -b topic origin/master,远程跟踪分支的历史origin/master可能已经被倒带和重建,导致这种形状的历史:

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

whereorigin/master曾经指向 commits B3, B2B1现在它指向B,并且您的topic分支origin/masterB3.

此模式使用 reflog作为分叉点,以便origin/master可以在更新B3topicorigin/master的顶部重新建立:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

这就是该git merge-base命令有一个新选项的原因:

--fork-point::

找到一个分支(或任何导致的历史<commit>)从另一个分支(或任何参考)分叉的点<ref>
这不仅寻找两个提交的共同祖先,而且还考虑了 reflog<ref>以查看导致<commit>分支的历史是否来自分支的早期化身<ref>


" git pull --rebase" 命令使用分支工作所基于的 " " 分支 (通常是远程跟踪分支) 的 reflog 条目来计算要重新设置的分支的分支点base,以应对 "base"分支已经倒带和重建。

例如,如果历史看起来像这样:

  • base" " 分支的当前尖端是 at B,但之前的 fetch 观察到它的尖端曾经是B3然后B2然后B1 在到达当前提交之前,并且
  • 基于最新“基础”的分支基于 commit B3

它试图B3通过“ git rev-list --reflog base”的输出(即B, B1, )进行查找B2B3直到找到作为当前提示“ Derived (topic)”的祖先的提交。

在内部,我们get_merge_bases_many()可以一次性计算。
我们想要一个合并基础Derived和一个虚构的合并提交,这将通过合并“ base (origin/master)”的所有历史提示而产生。
当这样的提交存在时,我们应该得到一个结果,它与 " base" 的 reflog 条目之一完全匹配。


Git 2.1(2014 年第三季度)将添加使此功能更加强大:请参阅John Keeping的提交 1e0dacd ( )johnkeeping

正确处理我们具有以下拓扑的场景:

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master

在哪里:

  • B'是与;B不完全相同的补丁版本。B
  • C*如果以错误的顺序应用,则与补丁相同且分别与文本D*冲突CD
  • E文字上取决于D.

的正确结果git rebase master devB被标识为 和 的分叉点devmaster因此C,DE需要重播到 的提交master;但是Cand 和 andD是一样的C*D*所以可以去掉,所以最终的结果是:

o --- B' --- C* --- D* --- E  <- dev

如果未识别分支点,则选择B包含的分支B'会导致冲突,如果未正确识别补丁相同的提交,则选择C包含D(或等效地D*)的分支会导致冲突。


当命令在 2.20 时代用 C 重写时,“ ”的“ --fork-point”模式倒退了,该问题已在 Git 2.27(2020 年第二季度)中得到纠正。git rebase

请参阅Junio C Hamano ( ) 的提交 f08132f(2019 年 12 月 9 日(由Junio C Hamano 合并 -- --提交 fb4175b中,2020 年 3 月 27 日)gitster
gitster

rebase--fork-point回归修复

签字人:Alex Torok
[jc:改进了修复并使用了 Alex 的测试]
签字人:Junio C Hamano gitster@pobox.com

" git rebase --fork-point master" 以前可以正常工作,因为它在内部调用了 " git merge-base --fork-point",它知道如何处理短 refname 并将其缩小为完整的 refname,然后再调用底层get_fork_point()函数。

在用 C 重写命令后,这不再是正确的,因为它的内部调用直接调用get_fork_point()不会缩小一个短的 ref。

将“git merge-base”中使用的“dwim refname 参数到完整的refname”逻辑移动到底层get_fork_point()函数,以便在“git rebase”的实现中函数的其他调用者行为相同的方式来修复这种回归。


在 Git 2.31(2021 年第一季度)中,“ git rebase --[no-]fork-pointman “获得了一个配置变量rebase.forkPoint,这样用户就不必一直指定非默认设置。

请参阅Alex Henrie ( ) 的提交 2803d80(2021 年 2 月 23 日(由Junio C Hamano 合并——提交 682bbad中,2021 年 2 月 25 日)alexhenrie
gitster

rebase: 添加一个配置选项--no-fork-point

签字人:Alex Henrie

一些用户(包括我自己)宁愿默认关闭此功能,因为它可以静默放弃提交。

git config现在在其手册页中包含:

rebase.forkPoint

如果默认设置为 false 设置--no-fork-point选项。

于 2013-12-06T11:43:04.413 回答