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,在本例中,H
(H
代表实际的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
指向 I
和 I
指向 H
,现在 你有:
I
/
...--F--G--H <-- master
这也是您摆脱狐步舞合并的方法。假设您开始于:
...--F--G--H <-- master
其他人添加了 他们的 新的 I
和 J
提交给 他们的 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
仍然像以前一样回到 M
和 J
,并且 M
仍然回到两个 H
和 L
和以前一样——但是没有 name 导致 N
,因此无法找到 N
或 M
。因此,我们可以将它们从绘图中删除:
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。现在你不会得到狐步舞推送时合并。
我一直在做一个项目,并且一直在将代码推送到我在远程 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,在本例中,H
(H
代表实际的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
指向 I
和 I
指向 H
,现在 你有:
I
/
...--F--G--H <-- master
这也是您摆脱狐步舞合并的方法。假设您开始于:
...--F--G--H <-- master
其他人添加了 他们的 新的 I
和 J
提交给 他们的 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
仍然像以前一样回到 M
和 J
,并且 M
仍然回到两个 H
和 L
和以前一样——但是没有 name 导致 N
,因此无法找到 N
或 M
。因此,我们可以将它们从绘图中删除:
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。现在你不会得到狐步舞推送时合并。