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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4084868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 04:42:47  来源:igfitidea点击:

How do I recover/resynchronise after someone pushes a rebase or a reset to a published branch?

gitrebasegit-rebasegit-reset

提问by Aristotle Pagaltzis

We have all heard that one should never rebase published work, that it's dangerous, etc. However, I have not seen any recipes posted for how to deal with the situation in case a rebase ispublished.

我们都听过一个人应该永远不会变基发表的作品,它的危险,等等。但是,我还没有看到张贴如何处理的情况下衍合情况的任何食谱出版。

Now, do note that this is only really feasible if the repository is only cloned by a known (and preferably small) group of people, so that whoever pushes the rebase or reset can notify everyone else that they will need to pay attention next time they fetch(!).

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

One obvious solution that I have seen will work if you have no local commits on fooand it gets rebased:

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

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

This will simply throw away the local state of fooin favour of its history as per the remote repository.

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

But how does one deal with the situation if one has committed substantial local changes on that branch?

但是,如果在该分支上进行了大量本地更改,如何处理这种情况?

采纳答案by Aristotle Pagaltzis

Getting back in synch after a pushed rebase is really not that complicated in most cases.

在大多数情况下,在推送 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. first you set up a bookmark for where the remote branch originally was, then you use that to replay your local commits from that point onward onto rebased remote branch.

IE。首先,您为远程分支的原始位置设置了一个书签,然后您使用它从那时起将本地提交重放到重新定位的远程分支上。

Rebasing is like violence: if it doesn't solve your problem, you just need more of it. ?

Rebase 就像暴力:如果它不能解决你的问题,你就需要更多。?

You can do this without the bookmark of course, if you look up the pre-rebase origin/foocommit ID, and use that.

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

This is also how you deal with the situation where you forgot to make a bookmark beforefetching. Nothing is lost – you just need to check the reflog for the remote branch:

这也是您如何处理获取之前忘记制作书签的情况。什么都不会丢失——你只需要检查远程分支的引用日志:

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

This will print the commit ID that origin/foopointed to before the most recent fetch that changed its history.

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

You can then simply

然后你可以简单地

git rebase --onto origin/foo $commit foo

回答by Cascabel

I'd say the recovering from upstream rebasesection of the git-rebase man page covers pretty much all of this.

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

It's really no different from recovering from your own rebase - you move one branch, and rebase all branches which had it in their history onto its new position.

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

回答by VonC

Starting with git 1.9/2.0 Q1 2014, you won't have to mark your previous branch origin before rebasing it on the rewritten upstream branch, as described in Aristotle Pagaltzis's answer:
See commit 07d406band commit d96855f:

从 git 1.9/2.0 Q1 2014 开始,在重新编写的上游分支上重新建立基础之前,您不必标记以前的分支原点,如Aristotle Pagaltzis回答中所述
请参阅提交 07d406b提交 d96855f

After working on the topicbranch created with git checkout -b topic origin/master, the history of remote-tracking branch origin/mastermay have been rewound and rebuilt, leading to a history of this shape:

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

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

where origin/masterused to point at commits B3, B2, B1and now it points at B, and your topicbranch was started on top of it back when origin/masterwas at B3.

This mode uses the reflog of origin/masterto find B3as the fork point, so that the topiccan be rebased on top of the updated origin/masterby:

whereorigin/master曾经指向 commits B3, B2B1而现在它指向B,并且您的topic分支在它的顶部启动时回到了origin/masterat B3

此模式使用 reflogorigin/master来查找B3作为 fork 点,以便topic可以在更新origin/master顶部重新定位

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

That is why the git merge-basecommand has a new option:

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

--fork-point::

Find the point at which a branch (or any history that leads to <commit>) forked from another branch (or any reference) <ref>.
This does not just look for the common ancestor of the two commits, but also takes into account the reflog of <ref>to see if the history leading to <commit>forked from an earlier incarnation of the branch <ref>.

找到一个分支(或任何导致 的历史<commit>)从另一个分支(或任何引用)分叉的点<ref>
这不仅会查找两个提交的共同祖先,还会考虑 reflog<ref>以查看导致<commit>分支的较早版本分叉的历史记录<ref>



The "git pull --rebase" command computes the fork point of the branch being rebased using the reflog entries of the "base" branch (typically a remote-tracking branch) the branch's work was based on, in order to cope with the case in which the "base" branch has been rewound and rebuilt.

git pull --rebase”命令使用base分支工作所基于的“ ”分支(通常是远程跟踪分支)的reflog条目来计算被重新定位的分支的分叉点,以应对“基础”的情况分支已被倒带和重建。

For example, if the history looked like where:

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

  • the current tip of the "base" branch is at B, but earlier fetch observed that its tip used to be B3and then B2and then B1before getting to the current commit, and
  • the branch being rebased on top of the latest "base" is based on commit B3,

it tries to find B3by going through the output of "git rev-list --reflog base" (i.e. B, B1, B2, B3) until it finds a commit that is an ancestor of the current tip "Derived (topic)".

Internally, we have get_merge_bases_many()that can compute this with one-go.
We would want a merge-base between Derivedand a fictitious merge commit that would result by merging all the historical tips of "base (origin/master)".
When such a commit exist, we should get a single result, which exactly match one of the reflog entries of "base".

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

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

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



Git 2.1 (Q3 2014) will add make this feature more robust to this: see commit 1e0dacdby John Keeping (johnkeeping)

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

correctly handle the scenario where we have the following topology:

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

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

where:

在哪里:

  • B'is a fixed-up version of Bthat is not patch-identical with B;
  • C*and D*are patch-identical to Cand Drespectively and conflict textually if applied in the wrong order;
  • Edepends textually on D.
  • B'B与补丁不相同的修复版本B
  • C*和分别与 和D*补丁相同,C并且D如果以错误的顺序应用,则会在文本上发生冲突;
  • E文字上取决于D.

The correct result of git rebase master devis that Bis identified as the fork-point of devand master, so that C, D, Eare the commits that need to be replayed onto master; but Cand Dare patch-identical with C*and D*and so can be dropped, so that the end result is:

的正确结果git rebase master devB被识别为的叉点devmaster,从而CDE是需要被重放到提交master; 但是CandDC*and补丁相同,D*所以可以删除,因此最终结果是:

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

If the fork-point is not identified, then picking Bonto a branch containing B'results in a conflict and if the patch-identical commits are not correctly identified then picking Conto a branch containing D(or equivalently D*) results in a conflict.

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



The "--fork-point" mode of "git rebase" regressed when the command was rewritten in C back in 2.20 era, which has been corrected with Git 2.27 (Q2 2020).

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

See commit f08132f(09 Dec 2019) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster--in commit fb4175b, 27 Mar 2020)

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

rebase: --fork-pointregression fix

Signed-off-by: Alex Torok
[jc: revamped the fix and used Alex's tests]
Signed-off-by: Junio C Hamano

"git rebase --fork-point master" used to work OK, as it internally called "git merge-base --fork-point" that knew how to handle short refname and dwim it to the full refname before calling the underlying get_fork_point()function.

This is no longer true after the command was rewritten in C, as its internall call made directly to get_fork_point()does not dwim a short ref.

Move the "dwim the refname argument to the full refname" logic that is used in "git merge-base" to the underlying get_fork_point()function, so that the other caller of the function in the implementation of "git rebase" behaves the same way to fix this regression.

rebase--fork-point回归修复

签字人:Alex Torok
[jc:修改了修复并使用了 Alex 的测试]
签字人:Junio C Hamano

git rebase --fork-point master” 过去工作正常,因为它在内部称为“ git merge-base --fork-point”,它知道如何处理短引用名称并将其转换为完整引用名称,然后再调用底层get_fork_point()函数。

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

将“git merge-base”中使用的“dwim the refname argument to the full refname”逻辑移动到底层get_fork_point()函数中,以便“git rebase”实现中该函数的另一个调用者行为相同的方式来修复这种回归。