分支A是从母服务器派生的,分支B是从分支A分支的。一旦创建了B,A便对自己进行了一些提交。Master可能具有其他分支的其他提交,这些提交与A和B无关。下图说明了这种情况:
您的问题不能完全按照要求回答。要意识到的是,Git不太在乎分支名称。它关心的是提交,特别是可到达的提交。要理解这一点,您需要了解提交图(并且您的绘图表明您在这里很顺利)。
最终的答案是,无论如何它可能并不重要。
这个问题无法回答,部分是因为在Git中,分支没有父子关系,部分是因为没有合并请求请求。
您画了这个(比我做的更好,但这是为了显示与您的图表相同的东西):
commit --> commit --> commit --> commit --> commit --> master
|
branchA --> commit --> commit --> commit
|
branchB --> commit --> commit --> ...
Run Code Online (Sandbox Code Playgroud)
这是一个有点不正确的绘图,它将使您误入歧途。(让我问一个主要是修辞性的问题:为什么master在右边画出名称,而在左边画出两个分支的名称?)
让我们再次绘制它,使用单个大写字母作为实际提交哈希ID的替代,这种方法更准确:
A <-B <-C <-D <-E <-F <--master
\
G <-H <-I <-J <--branchA
\
K <-L <-M <--N <--O <--branchB
Run Code Online (Sandbox Code Playgroud)
这里有两个主要区别:第一个也是最明显的,但实际上最不重要的一点是,我把所有箭头都向后了。这是因为Git实际上是向后工作的。
提交确实具有父/子关系,但是父提交完全不知道自己的孩子。(这是因为一旦提交,提交将被永久冻结。它的子级(如果有的话)将不存在。一旦创建,子级是在父级确实存在时创建的,因此它们冻结在自身中他们父母的身份证。)
但是,我的图形中的主要区别在于分支名称位于图的右侧,可以在其中添加新的提交。每个名称都指向分支上的最后一次提交。Git将此分支的最后提交称为提示提交。添加新提交的过程始于通过检出分支提示提交来检出分支。该技巧提交将成为当前提交。然后,您可以执行通常的工作git add,然后运行git commit。这git commit部分进行一个新的提交,其父作为当前提交。
正是在这一点上,神奇的事情发生了:Git 更改了当前分支名称,使其指向我们刚刚进行的新提交。
让我们更紧凑地绘制上面的内容,并添加一个新的分支名称branchC,指向与相同的提交master。我们还将git checkout branchC使我们将HEAD附加到它:这样,Git知道要更改的分支名称。
A--B--C--D--E--F <-- master, branchC (HEAD)
\
G--H--I--J <-- branchA
\
K--L--M--N--O <-- branchB
Run Code Online (Sandbox Code Playgroud)
我们可以P使用parent 进行新的提交,F并且Git现在将更改, branchC因为它HEAD是附加的名称:
P <-- branchC (HEAD)
/
A--B--C--D--E--F <-- master
\
G--H--I--J <-- branchA
\
K--L--M--N--O <-- branchB
Run Code Online (Sandbox Code Playgroud)
换句话说,提交这是尖branchC刚才是现在家长的承诺即是尖branchC了。分支名称已移动。
现有提交未更改!除了增加了新的提交外,图没有任何改变。但分支名称移动。就Git本身而言,分支名称本身基本无关。1 对Git重要的是提交。正常情况下,Git的日常工作是添加新的提交。但是提交哈希ID大,丑陋且难以记住,因此Git为我们提供了名称(例如分支名称)来记住特定的提交。
我们希望分支名称记住的特定提交是分支的尖端提交。这留下了一个有趣的问题:“分支”到底是什么意思? 在这种情况下,我们可能意味着两种不同的东西在同一时间:
(作为一个方面说明,我会提到,所有的名字都是可移动有可能根除标签名称为好,例如分支机构名称和标记名称是变量名不之间的主要区别。应该以招:它应在进行一次提交时标识一个特定的提交,并永远标识该同一提交。)
1分支名称在保持提交可访问性方面也起着重要作用。图形中存在的提交,但没有可用来访问它的外部名称,最终将被垃圾回收并删除。不过,我们不必为此担心。
我们只是看到分支名称仅标识一个特定的提交- 分支的尖端。当我们有两个标识相同提交的不同分支名称时,这特别有趣(或者令人烦恼或令人困惑),如下所示:
A--B--C--D--E--F <-- master, branchC (HEAD)
Run Code Online (Sandbox Code Playgroud)
这六个提交在哪个分支上?Git的第一个答案是:他们都在这两个分支。的尖端master是提交F,的尖端branchC也是F。从开始F,我们可以走回(通过其父链接)进行提交E,然后一直到D等等,一直到A。
请注意,无法通过这种方式到达commit G和更高版本:
A--B--C--D--E--F <-- master, branchC (HEAD)
\
G--H--I--J <-- branchA
Run Code Online (Sandbox Code Playgroud)
因为当我们进行这种图行走时,我们必须始终从孩子向父母后退。但是,我们可以看到,提交J背透G 是对branchA,和这里的棘手的部分,所以是B和A。
实际上,,A这是我们有史以来的第一个提交,因此Git称之为根提交,它存在于每个分支上。(有可能使Git图具有多个根提交,但这里没有意义。)
在使用Git时,询问一个正在进行中的提交(或“包含在其中”是一个更好的短语)是非常普遍的,因为某些分支可以通过该分支名称提示提交来访问,但不是处于/包含在其中。在其他名称中。例如,提交A并B都在master 和 branchA,但我们可能想看看那些在提交时branchA 不包括任何已还上master。这将使我们能够J重新安排工作G,这正是我们在这里想要的。
简短的形式是您将在其他地方看到的内容:
git log master..branchA
Run Code Online (Sandbox Code Playgroud)
稍长的形式实际上更有意义:
git log branchA ^master
Run Code Online (Sandbox Code Playgroud)
这意味着可以到达的提交,不包括()可以到达的提交 branchA^ master。
既然我们已经正确掌握了使用分支名称来标识提示提交的含义以及如何绘制分支的方法,我们就可以了解Git如何实现合并。合并操作从我们签出一些提交(通常是一些分支名称)开始,因此HEAD附加到分支名称上:
A--B--C--D--E--F <-- master (HEAD)
\
G--H--I--J <-- branchA
\
K--L--M--N--O <-- branchB
Run Code Online (Sandbox Code Playgroud)
然后git merge,我们使用通常是另一个分支名称的参数运行。它不具备成为一个分支名-任何标识的任何承诺就足够了!我们可以使用commit的原始哈希ID J代替使用word branchA。此时,Git将J在提交图中定位提交。或者,我们可以git merge branchB选择提交O作为其他提交。我们甚至git merge <hash-of-K>可以选择commit K作为另一个commit,而不管它不是任何分支的尖端。
现在,魔术的第一部分发生了。Git使用该图来查找合并基础提交。合并基础是两个分支上的某个提交,但不仅仅是任何这样的提交,它还是最佳提交(对于最佳定义)。从技术上讲,这是最低的共同祖先,但是在上面的图中,有一个明显的最佳祖先:
master和branchA,合并的基础是commit B:这是在两个分支上都涉及两个commit F和的最少向后行程的J那个。master和branchB,合并基础仍然是commit B。master和K,合并基础仍然是commit B。实际上,对于不在顶部行的任何提交,合并基础将为B。(对于最上面一行的提交,例如commit D,合并基础将是该提交-但此类提交已被合并,Git会说无事可做。)
一旦Git找到了合并基础提交,其余的就很容易了!好吧,也许不是那么容易。:-)但是这个过程现在已经很好地定义了。将当前提交称为L,以表示Left或Local或--ours。调用另一个提交R,代表Right或Remote或--theirs。调用合并基础乙为基础。Git现在相当于:
git diff --find-renames B L > /tmp/ours
git diff --find-renames B R > /tmp/theirs
找出自合并基础提交以来我们所做的更改,以及自同一合并基础以来所做的更改。在我们的例子中,如果我们合并branchA,则合并的基础实际上是B,L(eft | ocal)提交实际上是F,而R(ight | emote)提交是J。Git查找各种更改,组合这些更改,将结果应用于合并库的内容,如果一切顺利,则提交结果快照。
这个新提交有两个父母。通常,第一个父级是现有分支的尖端。在第二父仅仅是其他承诺。因此,在本例中,我们得到以下信息:
A--B--C--D--E--F--P <-- master (HEAD)
\ /
G--H--I----J <-- branchA
\
K--L--M--N--O <-- branchB
Run Code Online (Sandbox Code Playgroud)
P我们的新合并提交在哪里。
假设我们执行了上述操作,然后运行git merge branchB。我们已经知道合并是通过查找合并基础提交开始的。但是现在合并基础不再提交B!
查找合并基础的说明说,我们应该遍历所有可到达的提交,从每个分支提示开始,然后像往常一样向后工作。但是master现在的提示是P,它有两个父母。第一父母带我们去F,第二父母带我们去J。从F和反向进行工作J,我们到达E和I,然后再D和H。
同时,从O–的尖端– 开始,branchB我们回到N,然后M,然后L,然后K,然后H。因此,如果现在合并,则在进行合并提交后P,我们的合并基础为commit H。
请注意,如果我们不branchA首先合并而是branchB首先合并,那么我们将还没有提交P,并且合并基础将是commit B。假设一切顺利,结果将是这样的:
A--B--C--D--E--F----------P <-- master (HEAD)
\ /
G--H--I--J <-----/----- branchA
\ /
K--L--M--N--O <-- branchB
Run Code Online (Sandbox Code Playgroud)
一个拉要求是,当你问别人采取一些您提交的,做自己的合并。
无论“他们”是谁,他们都会做自己的git merge事情,或者做一些不同的事情,使他们将您的提交复制到新的不同提交中。 他们将创建是否合并提交P。
一旦他们完成了此操作,如果他们使用了它,git merge以便他们具有使用原始提交的实际合并,那么他们首先执行哪个拉取请求,然后再执行哪个无关紧要。他们将有一个实际的合并,该合并实际上合并了具有这些哈希ID的原始提交,因此,他们的 Git将在其提交图中看到与您自己进行合并时所看到的相同提交历史。然后,他们的Git将找到正确的新合并基础,并自动执行正确的操作。因此,使用什么顺序都没关系。
但是,如果他们根本不使用合并,并且/或者在合并之前先复制您的提交,那么GitHub Web界面会调用这些根本不合并的操作rebase和merge,或者压榨并合并也不会合并的操作在所有-那么你和他们将留下一个小的混乱:他们会有一些其他的承诺或纳入您在提交所做的工作,你有两个你的提交branchA和branchB。他们的Git不一定能够解决这个问题。
如果是这种情况,他们可能仍会要求您为您的一个拉取请求重新设置基准。他们首先选择哪一个并不重要:您仍然必须解开混乱。两种方式的混乱程度大致相同。
长话短说:这取决于您的流程。
B 在签出之前拥有 A 的所有提交,但是,由于 A 具有不在 B 中的提交(在签出之后提交),因此它们是不同的分支,应该这样对待。
将 B 合并到 Master 会将 B 的所有提交以及结帐前A 的提交添加到 Master 中。
将 A 合并到 Master 会添加 A 的所有提交,而将 B 合并到 Master 时,唯一要添加的提交是两个分支之间的差异(B 中不在 A 中的提交)。
根据您在做什么,每个分支的更改是什么以及实际有意义的内容,您在哪里合并哪个分支,只需记住上述内容即可。
祝你好运!