对分支的拉取请求包含来自其他分支的提交

Nah*_*pet 4 git merge bitbucket

说我有两个分支,master并且dev.

我创建了一个分支master,进行了更改并合并到了master.

现在我创建了另一个分支master,但是将我的更改推送到dev并尝试合并它们,但是当我发出pull请求时,第一个分支合并到master我的请求中.我怎么能避免这个?

(问题是我应该将第一个分支合并到dev没有,master但现在我不能再这样做了,因为相同的更改被合并到dev另一个第三个分支.)

tor*_*rek 7

注意:如果这是TL; DR,请跳到上一部分,绘制拉取请求.

让我们画出你已经做过的事情.请记住,在Git中,每次提交都会"回退"到其父提交或合并提交,以及它的两个父母; 和分支名状masterdevnewbranch仅指向一个承诺,用自己提交做着指点回,以代表整个系列的提交.

我必须在这里做一些假设.Bitbucket(我从没有使用它,因为他们从Mercurial专卖店转换为Git)和GitHub(我使用它)有点不同,但由于它们都使用Git,所以有很多相似之处.我假设你正在使用Bitbucket的网站称之为"分叉工作流程".

我将尽力避免做出许多其他假设.这使得这个答案非常漫长而复杂.(在您的问题中提供更多信息可能对您有所帮助,因此答案可能更短.)

绘制你拥有的东西,第1部分

所以,你开始:

... <-B <-C <-D   <-- master
Run Code Online (Sandbox Code Playgroud)

其中提交D点返回提交C,C指回B,等等,并master指向D.请注意,这些单个大写字母中的每一个都代表实际的提交哈希ID,其中一个是难以理解的1badc0ffee4deadcab...或任何东西.我们在这里使用这些字母,因为它们简单易懂,易于理解; Git使用哈希值,因为字母仅在26次提交后耗尽.:-)(还有其他更好的理由.)

我们知道承诺总是倒退.他们必须:你不能指出你尚未做出的承诺!所以,让我们不要打扰内部箭头.但是分支名称随着时间的推移而移动,所以让我们继续绘制他们的箭头:

...--B--C--D   <-- master
Run Code Online (Sandbox Code Playgroud)

现在您创建了一个分支,它还指向D:

...--B--C--D   <-- master, newbranch (HEAD)
Run Code Online (Sandbox Code Playgroud)

(这是一个快速练习:这三个提交中的哪一个正在启用master,哪些启用newbranch?这是一个小技巧问题,因为答案是所有三个提交都在两个分支上.)

我们已经添加了这个HEAD符号,以便我们可以分辨你所在的分支,就像在git status说的那样on branch newbranch.现在你做一个新的提交E.Git将当前分支 - 一个标记HEAD点 - 转换为我们刚刚创建的新提交,并将新提交点返回到上一个分支提示:

...--B--C--D   <-- master
            \
             E   <-- newbranch (HEAD)
Run Code Online (Sandbox Code Playgroud)

让我们再做两次提交吧:

...--B--C--D   <-- master
            \
             E--F--G   <-- newbranch (HEAD)
Run Code Online (Sandbox Code Playgroud)

现在这个名字newbranch指向G,指向的F,依此类推.

绘制你拥有的东西,第2部分:合并 newbranch

下一部分很棘手.你做了:

git checkout master; git merge newbranch
Run Code Online (Sandbox Code Playgroud)

或者可能:

git checkout master; git merge --no-ff newbranch
Run Code Online (Sandbox Code Playgroud)

但你没有说你做了哪些.git checkout将使HEAD标签贴master.但是,考虑到我们在这里绘制的绘图,你可以看到Git可以master向前滑动名称,从左到右的方向,与分支中所有内部箭头相反,因此名称master指向G:

...--B--C--D
            \
             E--F--G   <-- master (HEAD), newbranch
Run Code Online (Sandbox Code Playgroud)

Git称这种标签滑动操作是快进的.关键是这一点是快进操作不会进行合并提交.这使得master在你跑步之前更难看到它在哪里git merge,因为我们可以理顺图形中的扭结并得到:

...--B--C--D--E--F--G   <-- master (HEAD), newbranch
Run Code Online (Sandbox Code Playgroud)

鉴于这D是真的4384e3cde2ce8ecd194202e171ae16333d241326或一些如此丑陋的哈希ID,你可能不记得它到底是什么.你怎么知道你合并了三个提交,那master曾经是4384e3cde2ce8ecd194202e171ae16333d241326

您可以使用--no-ff合并类型强制 Git进行真正的合并,即使它没有必要.这有助于弄清楚发生了什么.Git不会只是向前滑动名称 master,而是这样做:

...--B--C--D---------H   <-- master (HEAD)
            \       /
             E--F--G   <-- newbranch
Run Code Online (Sandbox Code Playgroud)

换句话说,这里Git进行了一次新的合并提交 H.新的提交指向两者 D G.这实际上是使它成为合并提交的原因:它指向两个父母.

为什么我们要做所有这些绘图

git merge命令图的精确形状不会影响您当前的问题.您可以使用两个合并后的图形中的任何一个来获得相同的结果.但是,重要的是要注意合并提交何时进入,以及他们做什么:他们记录这种实际历史,以供将来探索.在任何一种情况下,能够绘制图形,预测拉动请求会发生什么是很重要的.两个后merge命令图都会导致问题.

有时,让这个"合并泡沫"提交H指向两者G 并且 D*只是无用的混乱.如果没有将来需要记住这组提交通过合并同时进行,你可以并且可能应该使用快进合并.如果你想强制进行真正的合并,你现在知道有这个--no-ff选项git merge可以做到这一点.

如果您使用GitHub进行合并(可能通过拉取请求),您可以使用GitHub的控件来决定是否强制进行实际合并.这些控件与Git不同(当然,因为它们是浏览器中的GUI clicky按钮).GitHub通常试图隐藏这些图形,我认为这是一个错误 - 图形对于确定实际可行的内容以及尝试各种事情时会发生什么至关重要.例如,它表明,在视觉上,为什么你拉的请求有问题.

(我没有使用Bitbucket的网络界面,但我认为它的工作原理与GitHub类似.)

绘制第二组更改

你现在说:

现在我创建了另一个分支master,但是将我的更改推送到dev并尝试合并它们,但是当我发出pull请求时,第一个分支合并到master我的请求中.

让我们假设您进行了快进合并,因此在您的Git存储库中有这个开头.我假设你已经删除了newbranch或者我们不关心它,并且你的"推送到"的新分支dev被称为zaphod:

...--D--E--F--G   <-- master, zaphod (HEAD)
Run Code Online (Sandbox Code Playgroud)

现在你做了一些新的提交(让我们放两个):

...--D--E--F--G   <-- master
               \
                H--I   <-- zaphod (HEAD)
Run Code Online (Sandbox Code Playgroud)

然后你做了一些事情 - 我不清楚究竟是什么系列的Git命令和/或Web和/或GUI点击框 - 让你的Bitbucket存储库使这个名称dev也引用提交I,或者也许是一个新的合并提交J指向回到I.

请注意,此时您自己涉及两个 Git存储库:一个位于本地计算机上,另一个位于Bitbucket上.关于Git哈希ID的有趣之处在于我们在这里作为单个大写字母绘制,它们在每个存储库的所有副本中都是相同的.(分支名称不是这样!)所以在Bitbucket上zaphod,我们将名称dev指向commit J.我们也不需要这个HEAD表示法,因为你没有直接在Bitbucket上提交:

...--D--E--F--G   <-- master
               \
                H--I   <-- dev
Run Code Online (Sandbox Code Playgroud)

同时,有一个第三,我们需要引进版本库,这第三收纳库为你,在你的到位桶"分叉工作流程"分叉的一个.

我现在假设第三个存储库的开头看起来像这样:

...--B--C--D   <-- master, dev
Run Code Online (Sandbox Code Playgroud)

(如果不是这种情况下,你的工作就是画,他们图的相应部分在这第三收纳库).

绘制您的拉取请求

完成以上所有操作后,让我们看看拉取请求到底是什么.

所有这一切拉入请求,是请求(因此得名),其别人加,他们的仓库,一些集从一个提交的你的仓库,使用与您的特殊关联的名称资源库,如你的dev-和一个名字与其存储库相关联,通常再次使用相同的名称.

在这种情况下,库让你拉入请求是你已经存储在到位桶的一个是你自己的资源库叉.这就是为什么我们需要绘制Bitbucket存储库中的图形:

...--B--C--D--E--F--G   <-- master
                     \
                      H--I   <-- dev
Run Code Online (Sandbox Code Playgroud)

我们还需要知道他们在他们的存储库中有什么.我假设这个:

...--B--C--D   <-- master, dev
Run Code Online (Sandbox Code Playgroud)

请注意,您D和他们D的字面意思相同.

你要求他们做的是将他们 设置dev为提交I.

要让他们这样做,他们必须将您的提交E--F--G--H--I带入他们的存储库,从而导致:

...--B--C--D   <-- master
            \
             E--F--G--H--I   <-- dev
Run Code Online (Sandbox Code Playgroud)

但那不是你现在真正想让他们做的.你希望他们采取一些承诺(s)表示,有点像 HI,并做到这一点:

...--B--C--D   <-- master
            \
             H2--I2   <-- dev
Run Code Online (Sandbox Code Playgroud)

为了让这样的事情发生,你必须先H2I2你自己.

要制作H2I2,您需要复制原始提交HI新提交.在提交之后,您将需要这些新的提交D.所以现在你需要找到他们dev提交提示.

我一直把它作为提交D,但这可能不正确.我没有你的存储库,不得不做出各种猜测(包括你选择的Bitbucket工作流程).

如果你将他们的Bitbucket存储库设置为上游,你可以运行在你的本地计算机上获得自己的Git,从他们那里获取他们的最新提交并将其记录为.(通常你的 Bitbucket fork会被命名,所以你可以在你的Bitbucket存储库中记录你的 fork .你可以命名他们的 Bitbucket存储库,以便在它们的分支顶端命名提交.)git fetch remote-namedevremote-name/devoriginorigin/devdevupstreamupstream/devdev

让我们说,具体来说,他们的 Bitbucket存储库,您可以通过upstream本地Git存储库中的名称引用它,实际上有这个:

...--B--C--D   <-- master
            \
             N--O   <-- dev
Run Code Online (Sandbox Code Playgroud)

在这里你运行git fetch upstream,现在你的存储库 - 你的本地存储库,而不是你在Bitbucket上的副本 - 会有:

                      H--I   <-- zaphod (HEAD), origin/dev
                     /
...--B--C--D--E--F--G   <-- master
            \
             N--O   <-- upstream/dev
Run Code Online (Sandbox Code Playgroud)

然后,您需要创建自己的指向提交的分支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)
Run Code Online (Sandbox Code Playgroud)

现在您可以复制提交HI提交,H2I2使用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)
Run Code Online (Sandbox Code Playgroud)

一旦你有了这个设置在你的本地仓库,你需要得到你的到位桶仓库设置 dev指向承诺I2.要做到这一点,你必须强行推动.通常,强制推送是不好的,因为它告诉另一个Git存储库丢失一些提交,但是在这里你希望你的Bitbucket存储库丢失它的H--I提交.所以:

$ git push --force origin for-upstream-dev:dev
Run Code Online (Sandbox Code Playgroud)

将有你的(本地)的Git调出origin的Git,这是你的到位桶的Git,并告诉它:我给你我I2,当然我H2ON,如果你需要他们.现在你设置你dev的我I2!

假设他们遵守这个命令,Bitbucket现在会改变你的fork的副本,如下所示:

...--B--C--D--E--F--G   <-- master
            \
             N--O--H2--I2   <-- dev
Run Code Online (Sandbox Code Playgroud)

(而且,当originGit接受这​​个推动时,你自己的Git会让你origin/dev指向I2,记住它origin现在dev指向它I2.)

现在将此图与上游Bitbucket存储库中的图表进行比较,即Git调用的图表upstream:

...--B--C--D   <-- master
            \
             N--O   <-- dev
Run Code Online (Sandbox Code Playgroud)

这两个图之间的区别是,你会问他们,你upstream-to把你originH2,并I2和它们加入自己的dev...这是你在你的问题问什么.

请注意,您自己的(本地)存储库仍然存在复杂,混乱的分支命名zaphod等等.一旦你的上游已经接受了您更改其dev-taken你H2I2-它也许是时候清理你的本地库,使它看起来至少一点点,如果不是很多,喜欢你的origin资料库.为此,您可以zaphod完全删除自己的分支:

                      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)
Run Code Online (Sandbox Code Playgroud)

请注意,一旦删除分支标签,找到您从分支标签中查找的提交的唯一方法是使用其他名称(如原始哈希ID).如果某些提交的所有名称都消失了,那么就无法找到它们,它们最终将被垃圾收集,即完全被抛弃.这是原始的H--I,现在他们被抛弃了.

(maybe)以上地方是因为你自己的Git不会知道upstream已接受您的拉请求,直到你运行git fetch upstream有你的(本地)的Git调用上游Git和找到其最新的dev.