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

发布于 2024-09-30 09:25:51 字数 385 浏览 9 评论 0原文

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

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

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

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

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

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

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 is published.

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(!).

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

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风吹短裙飘 2024-10-07 09:25:51

在大多数情况下,在推送变基后恢复同步实际上并不那么复杂。

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。首先,您为远程分支最初所在的位置设置一个书签,然后使用它来重播从该点开始的本地提交到重新定位的远程分支。

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

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

这也是您处理在获取之前忘记创建书签的情况的方法。不会丢失任何内容 - 您只需要检查远程分支的引用日志:

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

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

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.

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

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

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

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

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

You can then simply

git rebase --onto origin/foo $commit foo
对风讲故事 2024-10-07 09:25:51

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

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

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

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.

帅冕 2024-10-07 09:25:51

从 git 1.9/2.0 Q1 2014 开始,在重写的上游分支上重新建立基础之前,您不必标记以前的分支源,如 亚里士多德 Pagaltzis答案
请参阅 提交 07d406b提交 d96855f

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

<前><代码> o---B1
/
---o---o---B2--o---o---o---B(来源/主站)
\
B3
\
派生(主题)

其中 origin/master 曾经指向提交 B3B2B1,现在它指向B,当 origin/master 位于 B3 时,您的 topic 分支就在其之上启动。< /p>

该模式利用origin/master的reflog找到B3作为分叉点,从而可以将topic在更新后的 origin/master 之上重新建立:

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

这就是为什么 git merge-base 命令有一个新选项:

--fork-point::

查找分支(或导致的任何历史记录)从另一个分支(或任何引用)分叉的点。

这不仅会查找两个提交的共同祖先,而且还会考虑 的引用日志,以查看历史记录是否导致 < ;commit> 从分支 的早期版本分叉出来。


git pull --rebase”命令使用“base”分支(通常是远程-跟踪分支)该分支的工作是基于的,以应对“基础”分支被倒带重建的情况。

例如,如果历史记录如下所示:

  • base”分支的当前尖端位于 B,但之前的 fetch 观察到其尖端曾经是 B3,然后B2 然后 B1
    在到达当前提交之前,并且
  • 在最新“基础”之上重新建立的分支基于提交 B3

它尝试通过“git rev-list --reflog base”的输出来查找B3(即B,< code>B1、B2B3),直到找到当前提示“派生(主题)”。

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



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

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

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

其中:

  • B'B 的修复版本,与 B 补丁不同;
  • C*D* 分别与 CD 补丁相同,如果应用于顺序错误;
  • E 在文本上依赖于 D

git rebase master dev 的正确结果是 B 被识别为 devmaster 的分叉点,因此 CDE 是需要重播到 master 上的提交;但 CDC*D* 补丁相同,因此可以删除,这样最终结果是:

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

如果未识别分叉点,则将 B 选取到包含 B' 的分支上会导致冲突,并且如果补丁相同的提交不存在正确识别,然后将 C 选择到包含 D(或等效的 D*)的分支上会导致冲突。


git rebase”的“--fork-point”模式在 2.20 时代用 C 重写命令时出现了退化,已在 Git 2.27(Q2)中得到纠正。 2020)。

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

变基--fork -点回归修复

签字人:Alex Torok
[jc:改进了修复并使用了 Alex 的测试]
签署人:Junio C Hamano [电子邮件受保护]

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

在用 C 重写该命令后,情况不再如此,因为直接对 get_fork_point() 进行的内部调用不会显示短引用。

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


对于 Git 2.31(2021 年第一季度),“< code>git rebase --[no-]fork-point"(man)" 获得了一个配置变量rebase.forkPoint,这样用户就不必继续指定非默认设置,

请参阅 commit 2803d80(2021 年 2 月 23 日)< a href="https://github.com/alexhenrie" rel="nofollow noreferrer">亚历克斯·亨利 (alexhenrie)。
(由 Junio C Hamano -- gitster -- 合并于 提交 682bbad,2021 年 2 月 25 日)

rebase:为<添加一个配置选项代码>--无叉点

签字人:Alex Henrie

一些用户(包括我自己)希望默认关闭此功能,因为它可以默默地删除提交。

git config 现在包含在其 手册页

rebase.forkPoint

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

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 07d406b and commit d96855f :

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

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

where origin/master used to point at commits B3, B2, B1 and now it points at B, and your topic branch was started on top of it back when origin/master was at B3.

This mode uses the reflog of origin/master to find B3 as the fork point, so that the topic can be rebased on top of the updated origin/master by:

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

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

--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>.


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.

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 B3 and then B2 and then B1
    before 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 B3 by 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 Derived and 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".


Git 2.1 (Q3 2014) will add make this feature more robust to this: see commit 1e0dacd by John Keeping (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 B that is not patch-identical with B;
  • C* and D* are patch-identical to C and D respectively, and conflict textually, if applied in the wrong order;
  • E depends textually on D.

The correct result of git rebase master dev is that B is identified as the fork-point of dev and master, so that C, D, E are the commits that need to be replayed onto master; but C and D are patch-identical with C* and D* and so can be dropped, so that the end result is:

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

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


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).

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

rebase: --fork-point regression fix

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

"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.


With Git 2.31 (Q1 2021), "git rebase --[no-]fork-point"(man)" gained a configuration variable rebase.forkPoint so that users do not have to keep specifying a non-default setting.

See commit 2803d80 (23 Feb 2021) by Alex Henrie (alexhenrie).
(Merged by Junio C Hamano -- gitster -- in commit 682bbad, 25 Feb 2021)

rebase: add a config option for --no-fork-point

Signed-off-by: Alex Henrie

Some users (myself included) would prefer to have this feature off by default because it can silently drop commits.

git config now includes in its man page:

rebase.forkPoint

If set to false set --no-fork-point option by default.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文