我一直在从事一个项目,并一直将代码推送到远程git存储库上的dev分支。同时,在我需要获取的master分支中引入了一些更改。所以我用下面的路线: 1-git checkout主 2-git pull 3-git checkout my_dev_branch 4-git merge master
现在,当我将对拉取请求的更改提交给master时,它表明我执行的合并是foxtrot合并。我应该还原还是重置此较早的合并提交,然后从主服务器手动获取那些更改。还是对此有其他解决方案。
当前,我一直希望仅在合并之前恢复到提交,但是本文“ 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?)足够好吗? 谢谢大家。
答案 0 :(得分:2)
首先,您列出的命令本身不会导致“ foxtrot合并”。 (另请参见GIT: How can I prevent foxtrot merges in my 'master' branch?)如果您有其中之一,则必须在完成以下操作后,可能已经通过git merge
从master
到origin/master
运行git pull
您自己的master
上的提交。 (这是您自己制作的master
部分,不会在您的命令序列中显示。)
无论如何,您确实不想还原合并。这样就使合并保持原样,并添加了另一个提交,该提交的作用是撤销合并对源的影响。正是第二次非合并提交导致链接的kernel.org页面中的后续混乱。如果您执行执行此还原操作,最终将得到的是狐步合并,然后是更多的提交(包括还原),然后是以后无论如何都要重新合并的内容。因此,狐步步合并仍然存在!
总的来说,狐步舞合并并不是那么糟糕。他们只是将主线作为功能的 second 父级,而不是作为功能的 first 父级,但是谁说的是 > main 行放在首位?如果您改变看法,也许您的功能毕竟是主线,而其他所有人都称之为“主线”的东西实际上就是辅助功能。狐步舞合并的全部就是:改变视角,声称 you 是主线,而 they 是辅助功能。
但是,如果您不喜欢那样,那么唯一的解决方法就是重写自己的历史记录。由于它是您的历史记录[您的合并声明您的提交是您的主线],因此可能可以,不仅与您而且对其他所有人一样。如果已获得您的提交的其他所有人(您的历史记录)尚未依赖并基于这些提交,则 可以。如果他们是依赖并基于这些提交,那么仍然可以:其他所有同事或同事都可以接受重做您重写自己的历史后,他们的工作?
如果所有这些都是真的-如果没有其他人依赖您的历史记录,或者所有依赖它的人都愿意做更多的工作,以便他们可以处理您对历史的重写,那么您就是要做的很清楚:从您的master
中删除您的狐步舞合并。这需要仔细使用git reset
,并了解Git分支的实际工作方式,git merge
的功能以及分支名称的方式不是关键。关键是提交图。
进行新的提交将添加到图形中,并更新您“在”上的任何分支名称(即git status
表示“在分支__上”),以便该分支名称标识新的提交。新提交的父级(对于大多数提交而言,是单数)是是之前分支尖端的提交。如果新提交是合并提交,则新提交的父级(复数)是是分支尖端的提交(通常),然后是 second 父级-您说要合并时指定的提交。
也就是说,Git中的分支只是一连串提交:
... <-F <-G <-H <--master
名称 master
会记住一个哈希ID,在这种情况下,H
(H
代表了实际的哈希ID,这很丑陋一串以十六进制表示的160位数字,虽然不是,但它看起来是随机的)。提交H
是分支上的 last 提交。 Git使用master
中的哈希ID来查找实际的提交。提交H
本身包含(即记住)Git用于查找提交G
的父哈希ID G
。提交G
记住其父F
; Git使用此哈希ID查找实际的提交F
。重复此过程,让Git往后走,一次提交一次,以查找所有“在”分支上的提交-更确切地说,从可到达到分支名称。
当分支名称或提交包含其他提交的哈希ID时,我们说分支名称(或提交)指向它们包含其哈希ID的提交。因此,分支名称指向一个(单个)提交。该提交 是分支的尖端。那只是Git对“分支”的定义。
如果您对某些文件进行了某些更改,然后git commit
进行了更改,则新快照将获得一个新的且唯一的大丑陋哈希ID,我们可以将其称为I
。新的提交I
记录哈希ID H
,因此I
指向H
。然后,Git将I
的提交哈希ID写入名称master
中,以便master
指向I
:
... <-F <-G <-H <-I <--master
这就是分支的成长方式。
以正确的方式使用git reset
时,您会告诉Git:更改master
,以便使其指向我的新提交I
,而不是指向H
。提交I
实际上并没有消失:它仍然存在,并像以前一样漂浮在太空中。但是,如果master
是您唯一的名称,那么现在很难找到其哈希ID。 (您已经记住了吗?:-))现在,您不再需要master
指向I
和I
指向H
,而是 now :
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
作为其第一父级(“主线”)共享,并将您的提交L
作为其第二父级(您的功能)。然后,将新的提交N
添加到侧分支中,以使其J
(即您的master
的{{1}})进入分支,因为您的origin/master
是主行:
M
他们的 K--L <-- feature/tall
/ \
...--F--G--H------M--N <-- master
\ /
I-----J <-- origin/master
现在是一个分支,就像您的J
一样,因为 main 行运行feature/tall
,N
,{{ 1}},M
,H
,...,直接沿着主线返回-第一对父母的顺序。
如果您想让主线回到G
,则必须完全消除您的合并F
。这需要使您的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
仍然返回到两个{{1} }和J
一样,但是没有 name 导致M
,因此无法找到H
或L
。因此,我们可以将它们从图形中删除:
N
现在您需要做的就是将N
向前和向下滑动到M
,您可以使用 K--L <-- feature/tall
/
...--F--G--H <-- master
\
I--J <-- origin/master
进行此操作:
master
我们现在可以重画甚至更少的内容:
J
您现在可以发出拉取请求,即,其他人为您运行git merge --ff-only origin/master
的请求,其中您建议进行新的合并-我们可以称之为如果需要,请再次 K--L <-- feature/tall
/
...--F--G--H
\
I--J <-- master, origin/master
-作为其第一父级,提交 K--L <-- feature/tall
/
...--F--G--H--I--J <-- master, origin/master
,作为其第二父级,提交git merge
:非狐步合并将主线称为“主线”,而将功能称为“功能”。
这些东西 很难。理解它的诀窍是,提交哈希ID 是通用的:它们在全局上是唯一的,并且每个存储库都会获取您的提交,并通过哈希ID来获取它们。您的Git和Joe的Git,Katherine的Git,Larry的Git等,都同意commit M
是commit J
。它永远不会有任何其他哈希ID。但是您的Git使用的名称(您的分支名称)是您的您的。它们不需要出现在任何其他存储库中。 他们使用的名称-他们的 L
,他们的 H
,依此类推-将显示在您的 Git就像您的 H
,master
,依此类推。正在运行:
develop
让您的Git调用其Git,一如既往地通过哈希ID来获取任何新提交,然后根据其名称更新您的您的 origin/master
名称。是origin/develop
获取他们的提交并更新您的远程跟踪名称,即git fetch
。
当您运行origin/*
或让Bitbucket或GitHub为您执行此操作时, 会创建一个新的提交-它会自动获取一个新的唯一哈希ID-这就是新的合并提交,它记录第一个父级(“主行”)和第二个父级(“功能”),均使用其哈希ID 。任何名称,无论它们是您的分支名称,例如{{1 }}或fetch
之类的远程跟踪名称,在您实际拥有并保留 commit 及其唯一的哈希ID后就变得无关紧要。
换句话说:名称查找提交。只有提交才是真正重要的。名称仅在找到提交的时间,时间和原因才重要。
(另请参见how to avoid foxtrot merge in git。尤其是评论。)
答案 1 :(得分:0)
在上图中,将分支 release/dev 合并到 GM-1166-SQS... 将导致 Foxtrot 合并。
要解决这个问题,只需首先在分支 GM-1166-SQS-... 上添加一个虚拟提交并推送它。
现在,如果您在 GM-1166-.. 中合并 release/dev,则不会出现 Foxtrot 错误。
这也是一种防止狐步舞错误的方法。
换句话说,如果分支 A 合并到分支 B,那么如果您需要合并 C 到 A,首先在分支 A 上做一个虚拟提交,然后才将 C 合并到 A。现在你不会得到 Foxtrot 合并时推。