在有人推送rebase或重置到已发布的分支后,如何恢复/重新同步?

Ari*_*zis 84 git rebase git-rebase git-reset

我们都听过一个人应该重订,从未发表的作品,它的危险,等等.但是,我还没有看到张贴如何处理的情况下衍合情况的任何食谱出版.

现在,请注意,只有存储库仅由已知(并且最好是很小)的人群克隆,这才真正可行,这样无论谁推送rebase或重置,都可以通知其他人他们下次需要注意的时候取(!).

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

git fetch
git checkout foo
git reset --hard origin/foo
Run Code Online (Sandbox Code Playgroud)

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

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

Ari*_*zis 73

在推动变基之后恢复同步在大多数情况下并不那么复杂.

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
Run Code Online (Sandbox Code Playgroud)

IE浏览器.首先,您为远程分支最初的位置设置了一个书签,然后使用它来重播从该点开始的本地提交到重新定位的远程分支.

重新定位就像暴力:如果它不能解决你的问题,你只需要更多.☺

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

这也是您如何处理获取之前忘记创建书签的情况.什么都不会丢失 - 你只需要检查远程分支的reflog:

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'
Run Code Online (Sandbox Code Playgroud)

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

你可以简单地说

git rebase --onto origin/foo $commit foo
Run Code Online (Sandbox Code Playgroud)

  • 快速注意:我认为它非常直观,但是如果你不清楚awk ......那个单行只是查看`git reflog show origin/foo`的输出,第一行说"fetch:forced-更新"; 当抓取导致远程分支执行除快进之外的任何操作时,这就是git记录的内容.(您也可以手动完成 - 强制更新可能是最近的事情.) (11认同)
  • @iolo真的,变形总是很有趣. (5认同)
  • 这与暴力无关.暴力偶尔会很有趣 (2认同)
  • 好吧,避免推动其他人受到影响的地方. (2认同)

Cas*_*bel 11

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

这与从你自己的rebase中恢复完全没有什么不同 - 你移动了一个分支,并将历史记录中的所有分支重新定位到新的位置.

  • 啊,它确实如此.但是,虽然我现在明白了它的内容,但在我自己解决这个问题之前,我以前没有.并且没有食谱配方(在这样的文档中也许是正确的).我还要提出,强烈称之为"硬案件"的是FUD,我认为重写的历史在大多数内部开发的规模上是可以轻易管理的.这个主题总是被迷信的迷信方式让我很烦. (4认同)
  • @Aristotle:鉴于所有开发人员都知道如何使用git,以及您可以有效地与所有开发人员进行通信,这是非常可管理的.在一个完美的世界里,这就是故事的结局.但是很多项目都足够大,上游的基础确实是一件可怕的事情.(然后有一些地方,比如我的工作场所,大多数开发人员甚至从来没有听说过.)我认为"迷信"只是提供最安全,最通用的建议的一种方式.没有人愿意成为在别人的回购中造成灾难的人. (4认同)
  • 是的,我理解动机.我完全赞同.但是,"如果你不理解后果,不要试试这个"和"你永远不应该这样做,因为它是邪恶的"之间存在着天壤之别,而我自己就是这个问题.教导总是比灌输恐惧更好. (2认同)

Von*_*onC 10

从2014年第一季度的git 1.9/2.0开始,您不必在重写的上游分支上重新标记之前的分支原点,如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)
Run Code Online (Sandbox Code Playgroud)

其中,origin/master用于指向承诺B3,B2,B1和现在的点B,你的topic分支开始在它的后面,当顶origin/masterB3.

此模式使用reflog origin/master来查找B3作为分叉点,以便topic可以在更新origin/master的基础上重新定位:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic
Run Code Online (Sandbox Code Playgroud)

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

--fork-point::
Run Code Online (Sandbox Code Playgroud)

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


" git pull --rebase"命令使用base分支工作所基于的" "分支(通常是远程跟踪分支)的reflog条目来计算正在重新分配的分支的fork point ,以便处理"base"的情况.分支已被重绕并重建.

例如,如果历史记录看起来像:

  • " base"分支的当前提示是B,但早期的提取观察到它的提示曾经B3然后B2然后B1 再进入当前提交,并且
  • 在最新的"基础"之上重新分配的分支基于提交B3,

它试图找到B3通过的输出去" git rev-list --reflog base"(即B,B1,B2,B3),直到找到一个提交,它是当前尖端的祖先" Derived (topic)".

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


Git的2.1(Q3 2014)将增加使这个功能更强大的这样:看到提交1e0dacd约翰·行动(johnkeeping)

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

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master
Run Code Online (Sandbox Code Playgroud)

哪里:

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

的正确结果git rebase master dev是,B被识别为的叉点devmaster,从而C,D,E是需要被重放到提交master; 但CD是补丁与相同C*D*等都可以被丢弃,从而使最终结果是:

o --- B' --- C* --- D* --- E  <- dev
Run Code Online (Sandbox Code Playgroud)

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