Foxtrot Merge:如何解决

Foxtrot Merge: How to solve it

我一直在做一个项目,并且一直在将代码推送到我在远程 git 存储库上的开发分支。同时,我需要获取的 master 分支中引入了一些更改。所以我使用了以下路线: 1 - git 收银员 2 - git 拉 3 - git 结帐 my_dev_branch 4 - git 合并主机

现在,当我将对拉取请求的更改提交到 master 时,它显示我执行的合并是 foxtrot 合并。我应该恢复或重置这个早期的合并提交,然后手动从 master 获取这些更改。或者有没有其他可用的解决方案。

目前,我一直在寻求恢复到合并前的提交,但此处的这篇文章“https://mirrors.edge.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt”呈现了一幅相当阴郁和复杂的画面。我不希望它成为一项复杂的任务。 这个答案(How to revert a merge commit that's already pushed to remote branch?)够好吧? 谢谢大家

首先,您列出的命令本身不会导致 "foxtrot merge"。 (另见 )如果你有其中之一,你必须有 运行 git merge 从你的 master 到你的 origin/master,可能通过 git pull,在 上提交你自己的 master。 (此 make-a-commit-on-your-own-master 部分 不会 在您的命令序列中显示。)

无论如何,您真的不想恢复合并。这样就保留了合并,并添加了另一个提交,该提交在对源的影响方面具有撤消合并的效果。正是这第二个 non-merge 提交导致链接的 kernel.org 页面出现后续混乱。如果你 做这个恢复,你最终得到的是一个狐步舞合并,然后是更多的提交,包括恢复,然后是你以后必须做的任何事情 re-merge无论如何合并。所以狐步舞合并还在!

总的来说,狐步舞合并并不是那么糟糕。他们只是让主线作为功能的 second 父级工作,而不是作为功能的 first 父级工作——但谁说了算哪一行首先是 main 行?如果你换个角度,也许你的特征毕竟是主线,而其他人一直称之为[=296=的东西] 实际上是侧面特征。狐步舞合并就是这样:换个角度,声称是主线,他们是副线。

但如果你不喜欢那样,唯一真正的治疗方法就是重写你自己的历史。因为它是 你的 历史——你的合并声明你的提交是你的主线——这 可能 没问题,不仅对你而且对其他人也。 如果获得您的提交(您的历史记录)的其他所有人尚未依赖于这些提交并在此基础上构建,那么就可以了。如果他们 依赖于这些提交并以此为基础,它可能 仍然 没问题:所有这些其他同事或同事是否都同意 重做他们的工作,在你重写自己的历史之后?

如果这一切都是真的——如果没有其他人依赖于你的历史,或者所有依赖于它的人都愿意做更多的工作,以便他们能够处理你对历史的重写——那么什么你要做的很明确:从你的 master 中移除 你的狐步舞合并。这需要仔细使用 git reset,并了解 Git 分支的真正工作方式、git merge 的作用以及分支 names 的工作方式并不是关键。关键是提交图

进行新的提交会添加到图表中,并更新您所在的分支名称 "on"——在 git status 表示 "on branch ___" 的意义上——以便该分支名称标识新提交。新提交的父项——在大多数提交的情况下是单数的——是之前分支尖端的提交。如果新提交是合并提交,则新提交的双亲复数形式是 分支之前的提交(像往常一样),然后 - 作为 second parent——你在说合并时指定的提交。

也就是说,Git 中的 分支 只是一个提交链:

... <-F <-G <-H   <--master

namemaster记住一个hash ID,在本例中,HH代表实际的hash ID,这是一个 160 位数字的十六进制表示的大而难看的字符串,它看起来是随机的,尽管它不是)。提交 H 是分支上的 last 提交。 Git 使用 master 中的哈希 ID 查找实际提交。提交 H 本身包含(即记住)父哈希 ID G,Git 使用它来查找提交 G。提交 G 记住 它的 F; Git 使用此哈希 ID 查找实际提交 F。这个过程重复,Git 向后走链,一次一个提交,找到所有 "on" 的提交——更准确地说, 可从 到达——分支名称。

当分支名称或提交包含其他提交的哈希 ID 时,我们说分支名称(或提交)指向 其哈希 ID 的提交他们包含。因此,分支名称 指向 a—one, single—commit。该提交 分支的尖端。这只是 Git 对 "branch".

的定义

如果您对某些文件进行了一些更改,然后 git commit 结果,新快照将获得一个新的且唯一的丑陋的大哈希 ID,我们可以将其称为 I.新提交 I 记录哈希 ID H,因此 I 指向回 H。然后Git将I的commit hash ID写入名字master,这样master指向I:

... <-F <-G <-H <-I   <--master

这就是树枝生长的方式。

当您使用 git reset(在第正确的方式),你告诉 Git: 更改 master 这样它就不会指向我的新提交 I,而是指向 H 提交 I 实际上并没有 消失: 它仍然存在,漂浮在 space 中。但是,如果 master 是唯一的 name,那么现在很难找到它的哈希 ID。 (你记住了吗?:-))现在 master 指向 II 指向 H,现在 你有:

             I
            /
...--F--G--H   <-- master

这也是您摆脱狐步舞合并的方法。假设您开始于:

...--F--G--H   <-- master

其他人添加了 他们的 新的 IJ 提交给 他们的 master (你的 git fetch 记得你的 origin/master):

...--F--G--H   <-- master
            \
             I--J   <-- origin/master

你向你的主人添加一个新的提交,甚至可能是一整串,也许通过合并你自己的 feature/tall:

             K--L   <-- feature/tall
            /    \
...--F--G--H------M   <-- master
            \
             I--J   <-- origin/master

在这里,您的合并提交 M 共享 H 作为其第一个父项("main line"),您的提交 L 作为其第二个父项(您的功能) .然后你添加一个新的提交N,使他们的J,他们的master,也就是你的origin/master,进入一个分支,因为你的M是你的主线:

             K--L   <-- feature/tall
            /    \
...--F--G--H------M--N   <-- master
            \       /
             I-----J   <-- origin/master

他们的 J 现在是一个分支,就像你的 feature/tall,因为 main 行 运行s N, M, H, G, F, ..., 沿主线直回——first-parents.[=119=的序列]

如果您想将主线视为通过 J 返回,则必须完全消除 您的 合并 M。这需要让您的 master 再次指向 H。你可以这样做:

git checkout master
git reset --hard <hash-of-H>

然后我们可以(乱七八糟地)画成:

               L   <-- feature/tall
              / \
             K ,-M-----N
            /_/       /
...--F--G--H   <-------- master
            \       /
             I-----J   <-- origin/master

请注意,所有提交仍然存在——N 仍然像以前一样回到 MJ,并且 M 仍然回到两个 HL 和以前一样——但是没有 name 导致 N,因此无法找到 NM。因此,我们可以将它们从绘图中删除:

             K--L   <-- feature/tall
            /
...--F--G--H   <-- master
            \
             I--J   <-- origin/master

现在您需要做的就是将 master forward-and-down 滑动到 J,您可以使用 git merge --ff-only origin/master:

             K--L   <-- feature/tall
            /
...--F--G--H
            \
             I--J   <-- master, origin/master

我们现在可以重新绘制 even-less-messily:

             K--L   <-- feature/tall
            /
...--F--G--H--I--J   <-- master, origin/master

您现在可以提出拉取请求,即请求 其他人 运行 git merge 为您提出建议进行新的合并——如果我们愿意,我们可以再次称它为 M——它的 first 父级,提交 J,作为它的第二个父级,提交 L:调用主线 "the main line" 的 non-foxtrot 合并和你的功能 "feature".

这东西有点难。理解它的技巧是提交 哈希 ID 是通用的:它们是全局唯一的,并且 每个 获取您的提交的存储库,通过他们的哈希 ID。你的 Git 和乔的 Git、凯瑟琳的 Git、拉里的 Git 等等,都同意提交 H 是提交 H。它从来没有任何其他哈希 ID。但是你的 Git 使用的名称——你的分支名称——是你的。它们不需要出现在任何其他存储库中。 他们 使用的名字——他们 master他们 develop,以及依此类推——将在 your Git 中显示为 your origin/master, origin/develop, 等等. 运行:

git fetch

让你的 Git 调用他们的 Git,获取任何新提交——一如既往地通过哈希 ID——然后更新 你的 origin/* 名字基于 他们的 名字。 fetch 获取他们的提交并更新您的 remote-tracking 名称,您的 origin/*.

当您 运行 git merge --no-ff,或让 Bitbucket 或 GitHub 为您执行此操作时,that 创建一个新的提交——这自动获得一个新的、唯一的哈希 ID——它是新的合并提交,它记录了第一个父项——"main line"——和第二个父项,“特征,全部由它们的 哈希 ID。任何名称,无论它们是你的分支名称,如 master 还是你的 remote-tracking 名称,如 origin/master,一旦你实际拥有并保持 提交,就变得无关紧要 及其唯一的哈希 ID。

换句话说:名称查找提交。只有承诺才是真正重要的。名称只有在找到提交时、何时以及因为他们找到提交才重要。

(另见 how to avoid foxtrot merge in git。尤其是评论。)

branches

在上图中,将分支 release/dev 合并到 GM-1166-SQS... 将导致狐步舞合并。 要解决这个问题,只需首先在分支 GM-1166-SQS-... 上添加一个虚拟提交并推送它。 现在如果你在 GM-1166-.. 中合并 release/dev,它不会给出 foxtrot 错误。
这也是防止狐步舞错误的一种方法。

换句话说,如果分支A合并到分支B,那么如果你需要将C合并到A,首先在分支A上进行虚拟提交,然后才将C合并到A。现在你不会得到狐步舞推送时合并。