假设我有两个分支,master
和dev
。
我从master
创建了一个分支,进行了更改并合并到master
。
现在,我从master
创建了另一个分支,但是将更改推送到dev
并尝试合并它们,但是当我发出请求请求时,合并到master
的第一个分支出现在我的请求中。如何避免这种情况?
(问题是我应该将第一个分支合并到dev
而不是master
,但是现在我不能再这样做了,因为相同的更改已与另一个第三分支合并到dev
。)
最佳答案
注意:如果这是TL; DR,则跳到最后一部分,绘制请求请求。
让我们画出您已经做过的事情。记住,在Git中,每个提交都“指向”其父提交,或者对于合并提交,指向其(两个)父级。分支名称(例如master
或dev
或newbranch
)仅指向一个提交,这些提交本身进行指向后方,以表示整个系列的提交。
我必须在这里做一些假设。 Bitbucket(自从Mercurial-only商店转换为Git以来,我一直没有使用过)和GitHub(我确实使用过)有所不同,但是由于它们都在下面使用了Git,因此有很多相似之处。我假设您正在使用Bitbucket网站称为“派生工作流”的网站。
我将尝试避免做出许多其他假设。这使得这个答案非常漫长而复杂。 (在问题中提供更多的信息可能对您很有帮助,因此答案可能会更短。)
画出你所拥有的,第1部分
因此,您从开始:
... <-B <-C <-D <-- master
其中commit
D
指向commit C
,C
指向B
,依此类推,而master
指向D
。请注意,这些单个大写字母中的每个字母都代表实际的提交哈希ID,这些不可理解的1badc0ffee4deadcab...
之一或其他任何东西。我们在这里使用这些字母是因为它们既简单又容易理解。 Git之所以使用哈希值,是因为这些字母仅在26次提交后就用完了。 :-)(还有其他更好的原因。)我们知道提交总是指向后方。他们必须:您不能指出尚未完成的承诺!因此,我们不要打扰内部箭头。但是分支名称会随着时间推移而移动,因此让我们继续绘制箭头:
...--B--C--D <-- master
现在,您创建了一个新分支,该分支也指向
D
:...--B--C--D <-- master, newbranch (HEAD)
(这是您的快速练习:这三个提交中的哪个在
master
上,哪个在newbranch
上??这是一个技巧问题,因为答案是所有三个提交都在两个分支上。 )我们添加了这种
HEAD
表示法,以便我们可以知道您在哪个分支上,例如,git status
说on branch newbranch
。现在,您进行新的提交E
。 Git使当前分支(标记为HEAD
的那个分支)指向我们刚刚进行的新提交,并使新提交点回到上一个分支提示:...--B--C--D <-- master
\
E <-- newbranch (HEAD)
让我们再作两次提交:
...--B--C--D <-- master
\
E--F--G <-- newbranch (HEAD)
现在,名称
newbranch
指向G
,该名称又指向F
,依此类推。画出您拥有的东西,第2部分:合并
newbranch
下一部分很棘手。您做了:
git checkout master; git merge newbranch
或许:
git checkout master; git merge --no-ff newbranch
但您不说自己做了哪些。
git checkout
将使HEAD
标签粘贴到master
。但是,考虑到我们在此处绘制的图形,您可以看到Git可以将名称master
向前滑动,即与分支中所有内部箭头相反的从左到右的方向,名称master
指向G
:...--B--C--D
\
E--F--G <-- master (HEAD), newbranch
Git将此标签滑动操作称为快速前进。关于这一点的关键一点是,快进操作不会进行合并提交。这使得在运行
master
之前更难查看git merge
的位置,因为我们可以弄清楚图形中的纽结并得到:...--B--C--D--E--F--G <-- master (HEAD), newbranch
鉴于
D
确实是4384e3cde2ce8ecd194202e171ae16333d241326
或这么大的丑陋哈希ID,您可能不记得它的真正含义。您怎么知道您完全合并了三个提交,而master
曾经是4384e3cde2ce8ecd194202e171ae16333d241326
?您可以使用
--no-ff
类型的合并来强制Git进行真正的合并,即使不是必须这样做。这有助于弄清楚发生了什么。 Git不仅会向前滑动名称master
,还会这样做:...--B--C--D---------H <-- master (HEAD)
\ /
E--F--G <-- newbranch
换句话说,Git在此处进行新的合并提交
H
。新的提交同时指向D
和G
。实际上,这就是使其成为合并提交的原因:它指向两个父级。为什么我们要进行所有这些图形绘制
后
git merge
命令图的精确形状不会影响您当前的问题。两个合并后的图形中的任何一个都将得到相同的结果。不过,重要的是要注意合并提交的时间以及执行的工作:它们记录这种实际的历史记录,以供将来探索。无论哪种情况,重要的是要能够绘制图形,以预测拉取请求将发生的情况。两个后merge
命令图都导致问题。有时,使此“合并气泡”提交
H
指向G
和D
*都是无用的。如果将来没有必要记住这组提交是通过合并一次完成的,则可以并且可能应该使用快速合并。如果要强制进行真正的合并,现在您知道--no-ff
可以使用此git merge
选项来执行此操作。如果您使用GitHub进行合并(可能通过拉请求),则可以使用GitHub的控件来决定是否强制进行真正的合并。这些控件不同于Git的控件(自然,因为它们是浏览器中的GUI clicky按钮)。 GitHub通常会尝试向您隐藏这些图,我认为这是一个错误-该图对于确定当您尝试各种事情时实际可能发生的情况以及将会发生的事情至关重要。例如,它直观地显示了您的拉取请求出现问题的原因。
(我尚未使用Bitbucket的Web界面,但我认为它的工作方式与GitHub的非常相似。)
绘制第二组更改
您现在说:
现在,我从
master
创建了另一个分支,但是将更改推送到dev
并尝试合并它们,但是当我发出请求请求时,合并到master
的第一个分支出现在我的请求中。让我们假设您进行了快速合并,因此首先要在您的Git存储库中进行合并。我假设您已删除
newbranch
或我们不在乎,并且将您“按入dev
”的新分支称为zaphod
:...--D--E--F--G <-- master, zaphod (HEAD)
现在,您进行了一些新的提交(让我们提交两个):
...--D--E--F--G <-- master
\
H--I <-- zaphod (HEAD)
然后,您需要做一些事情-对我来说尚不清楚究竟是什么系列的Git命令和/或Web和/或GUI clicky框-使您的Bitbucket存储库以名称命名为
dev
也指提交I
,或者一个新的合并提交J
指向I
。请注意,此时涉及到两个自己的Git存储库:一个在本地计算机上,一个在Bitbucket上。关于Git哈希ID的有趣之处在于,在每个存储库的所有副本中,它们都是相同的,我们一直在这里将它们绘制为单个大写字母。 (分支名称不是这样!)因此,在Bitbucket上,我们用名称
zaphod
而不是dev
指向提交J
。我们也确实不需要HEAD
表示法,因为您没有直接在Bitbucket上提交:...--D--E--F--G <-- master
\
H--I <-- dev
同时,我们需要引入第三个存储库。该第三个存储库是您在Bitbucket“分叉工作流”中从中分叉的存储库。
现在,我将假设第三个存储库开始如下所示:
...--B--C--D <-- master, dev
(如果不是这种情况,则需要绘制图形在第三个存储库中的适当部分,这是您的工作。)
绘制您的拉取请求
综上所述,让我们看看真正的拉取请求。
拉取请求只是一个请求(因此而得名),它是其他人使用与您特定存储库相关联的名称(例如您的
dev
)向您的存储库中的一组提交中添加的一组提交。以及与其存储库相关联的名称,通常又是相同的名称。在这种情况下,您发出提取请求的存储库就是您存储在Bitbucket上的存储库,即存储库的分支。这就是为什么我们需要在您的Bitbucket存储库中绘制图形的原因:
...--B--C--D--E--F--G <-- master
\
H--I <-- dev
我们还需要知道它们在存储库中的内容。我假设这样:
...--B--C--D <-- master, dev
请注意,您的
D
和它们的D
实际上是相同的提交。您要他们执行的操作是将其
dev
设置为指向提交I
。为此,他们必须将您的提交
E--F--G--H--I
放入其存储库中,从而导致:...--B--C--D <-- master
\
E--F--G--H--I <-- dev
但这不是您真正希望他们现在做的。您希望他们进行一些类似于
H
和I
的提交,并执行以下操作:...--B--C--D <-- master
\
H2--I2 <-- dev
为了使这种情况发生,您必须首先自己制作
H2
和I2
。要创建
H2
和I2
,您将需要将原始提交H
和I
复制到新提交。在提交D
之后,您将需要这些新的提交。因此,现在您需要找到他们的dev
提交提示。我一直将此作为提交
D
绘制,但这可能是不正确的。我没有您的存储库,并且不得不做出各种猜测(包括您选择的Bitbucket工作流程)。如果您将其Bitbucket存储库设置为上游,则可以运行
git fetch remote-name
在本地计算机上获取自己的Git,以从其dev
获取其最新提交并将其记录为remote-name/dev
。 (通常,您的Bitbucket分叉将被命名为origin
,以便origin/dev
在Bitbucket上的存储库中记录您的分叉的dev
。您可以将其Bitbucket存储库命名为upstream
,以便upstream/dev
将提交的名称命名为他们的dev
分支。)确切地说,假设他们的Bitbucket存储库实际上包含以下内容:您可以在本地Git存储库中以名称
upstream
引用它们的Bitbucket存储库:...--B--C--D <-- master
\
N--O <-- dev
在这里,您将运行
git fetch upstream
,现在您的存储库(本地存储库,而不是Bitbucket上的副本)将具有: H--I <-- zaphod (HEAD), origin/dev
/
...--B--C--D--E--F--G <-- master
\
N--O <-- upstream/dev
然后,您需要创建自己的分支,该分支指向提交
O
。要在本地执行此操作,可以将git checkout
与-b
和--track
结合使用:$ git checkout -b for-upstream-dev --track upstream/dev
H--I <-- zaphod, origin/dev
/
...--B--C--D--E--F--G <-- master
\
N--O <-- upstream/dev, for-upstream-dev (HEAD)
现在,您可以使用
H
将提交I
和H2
复制到提交I2
和git cherry-pick
: $ git cherry-pick zaphod~2..zaphod
H--I <-- zaphod, origin/dev
/
...--B--C--D--E--F--G <-- master
\
N--O <-- upstream/dev
\
H2--I2 <-- for-upstream-dev (HEAD)
在本地存储库中进行此设置后,需要获取Bitbucket存储库以将其
dev
设置为指向提交I2
。为此,您必须强制按下。通常,强制推送是不好的,因为它告诉另一个Git存储库丢失一些提交,但是在这里,您希望Bitbucket存储库丢失其H--I
提交。所以:$ git push --force origin for-upstream-dev:dev
将让您的(本地)Git调用
origin
Git(这是您的Bitbucket Git)并告诉它:我给您我的I2
,当然还有我的H2
和O
和N
他们;现在,您将dev
设置为我的I2
!假设他们遵守此命令,Bitbucket现在将更改其fork的副本,如下所示:
...--B--C--D--E--F--G <-- master
\
N--O--H2--I2 <-- dev
(并且,当
origin
Git接受此推送时,您自己的Git将您的origin/dev
指向I2
,以记住origin
现在其dev
指向I2
。)现在将此图与上游Bitbucket存储库中的图(您的Git调用
upstream
的图)进行比较:...--B--C--D <-- master
\
N--O <-- dev
这两个图之间的区别是,您将要求他们-您的
upstream
-接受origin
的H2
和I2
并将它们添加到它们的dev
...在你的问题。请注意,您自己的(本地)存储库仍然具有名为
zaphod
的分支的复杂,混乱的混乱局面,依此类推。一旦上游接受了对它们的dev
所做的更改(使用了H2
和I2
),则可能是时候清理本地存储库了,使它看起来至少有点像(如果不是很多的话),例如您的< cc>存储库。为此,您可以完全删除自己的origin
分支: H--I [abandoned]
/
...--B--C--D--E--F--G <-- master
\
N--O <-- upstream/dev (maybe)
\
H2--I2 <-- for-origin-dev, origin/dev, upstream/dev (maybe)
请注意,一旦删除了分支标签,从分支标签中查找以前要查找的提交的唯一方法就是使用其他名称(例如原始哈希ID)。如果某个提交的所有名称都消失了,从而无法找到它们,那么它们最终将被垃圾回收,即被完全扔掉。原来的
zaphod
会被丢弃,这就是原来的情况。上面的
H--I
是因为您自己的Git不会知道(maybe)
已经接受了您的拉取请求,直到您运行upstream
来让(本地)Git调用上游Git并找到其最新的git fetch upstream
。
关于git - 分支的请求请求包含其他分支的提交,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45728766/