在创建PR之前,我想确保我的代码与master保持最新,因此我尝试git rebase master
。这就产生了可怕的无限冲突情况,据此,基准服务器将报告冲突,我将解决该冲突,将其添加,点击git rebase --continue
,然后循环将重复-似乎永远如此。
但是,当我创建PR时,发现它没有冲突。我检查了它与我的分支机构和master
之间的比较视图,并且可以肯定的是,没有冲突。
我对重定基的了解告诉我,重定基仅应将分支提交移动到比master
中的最新提交更新的位置。因此,我不完全理解为什么会发生冲突-除非我也许不知道提交包含的内容是什么?
答案 0 :(得分:1)
我对重定基的了解告诉我,重定基应该只是将我的分支提交移动到比master中的最新提交更新的时间。
是的,有点。但是提交不能移动,这意味着这并不简单。实际上,关于任何现有提交的所有内容都不能更改。这是一个非常有力的保证,可以使提交的大部分价值:如果您在今天,明天,或者从现在开始的十年后或将来的任何时间都提交了a123456...
,那么您会发现提交a123456...
再次,您可以确定它与现在完全相同。
因此,我不完全理解为什么应该发生[rebase]冲突[如果没有请求请求]
拉动请求并不完全是Git。更准确地说,Git具有git request-pull
,但这仅构建了一些适合通过电子邮件发送补丁程序的东西。您正在谈论的请求类型是GitHub和Bitbucket等网站在Git之上添加的,它们是由运行git merge
的网站本身构建的。
另一方面,运行git rebase
要求您自己的 Git重复调用git cherry-pick
,每次重新提交一次都会被重新调用。您可以通过使用git rebase -i
而不是直接使用git rebase
来看到这一点。这里有一些奇怪的情况-有时Git确实不运行git cherry-pick
,有时会运行-但这是基本原理。
一个托管站点上的拉请求,我们称其为S,表示:嘿,S:为我运行git merge
,看看是否有冲突,如果没有,请要求其他人重复git merge
。
您自己运行的 git rebase
表示:嘿,我自己的Git:反复挑选某些提交,以便将它们复制到新的和改进的提交中
git merge
命令可能会做很多事情,但是我们在这里关心的是作为动词合并或合并。这种合并动作,是在Git中进行的合并,是关于合并更改。也就是说,您在分支上有一系列的提交(我将在其中提交四个),它们相互链接,最后一个是您的分支名称标识的提交:
o--o--o--L <-- your-branch
/
...--o--o--* (you started with this)
这里的每个回合o
代表一次提交。其中之一加注了*
,因为它特别重要。对于另一个分支(分支的 tip 提交),我使用了字母L
,以便可以再次参考。 L
代表Left或Local或--ours
。
与此同时,其他人从相同的提交*
开始并进行了一些提交:
o--o--o--L <-- your-branch
/
...--o--o--* (you *both* started with this)
\
o--o--R <-- their-branch
它们具有一系列提交,这些提交也链接(向后,Git这样做)到同一起始提交*
。 他们的分支的尖端,我叫R
。 R
代表Right或Remote或--theirs
。
在这一点上,如果您自己运行git merge their-branch
,或者如果让网站S进行等效操作,则您的Git(或其Git)会发现{em> 提交*
自动为您服务。然后,它在逻辑上等效:
git diff --find-renames <hash-of-*> <hash-of-L> # find out what YOU changed
git diff --find-renames <hash-of-*> <hash-of-R> # find out what THEY changed
,并至少进行 try 组合这些更改,如从提交*
中的快照所见。如果您更改了某些内容(某些文件中的某些行)而没有更改,则Git会进行您的更改。如果他们更改了某些内容而您没有更改,则Git会进行他们的更改。如果您都对同一文件的相同行进行了完全相同的更改,则Git将获得一份副本。如果您对同一文件的同一行进行了不同的更改,则Git会声明合并冲突,合并将失败。 (使用常规git merge
,您可以自己清理混乱。通过对合并请求进行自动合并,网站S会进行自己的清理。)
如果没有冲突,则合并成功:Git接受合并的更改,将其应用于提交*
中的内容,并与两个父级进行新的 merge commit :< / p>
o--o--o--L
/ \
...--o--o--* M
\ /
o--o-----R
如果您自己运行此git merge
,则提交M
将成为您自己分支的新提示。如果网站正在执行此操作,则提交M
会隐藏在网站上(例如,以特殊名refs/pull/number/merge
)。
git cherry-pick
所做的是一次复制一次 次提交。为此,Git必须将快照变成一组更改。为了做到这一点,Git需要将提交与父对象进行比较。让我们以与之前相同的顺序开始,但绘制方式有所不同:
I--J--K--L <-- your-branch
/
...--o--o--H--P--Q--R <-- their-branch
您最终想要得到的是这样:
I--J--K--L [abandoned]
/
...--o--o--H--P--Q--R <-- their-branch
\
I'-J'-K'-L' <-- your-branch
其中I'
是原始I
的副本,但是有些不同:
H
,而是提交R
,并且R
开始,而不是H
中的快照。 J'
到L'
都一样:它们都必须做出与I
,J
,{ {1}}和K
,但他们必须在L
和R
和{{1}中对其进行修改时,在I'
中使它们具有相同的起始基数。 }。
为此,Git首先在临时分支上检出提交J'
。然后,Git通过将提交内容K'
与提交内容R
中的内容进行比较,将提交内容I
(您要复制的第一个提交内容)转换为一组更改。现在,Git必须将这些更改从H
更改为H
。
此合并使用的是与R
相同的合并机制,但不是直接查看git merge
vs H
,而是查看L
vs {{1 }}。如果您在H
中所做的任何事情与他们在{{1} -vs-I
中所做的任何事情发生冲突,即使您稍后在I
中进行修复。
如果一切顺利-如果没有冲突-Git将H
的副本作为新的普通(非合并)提交R
:
L
,然后继续选择I
。为了选择I'
,Git再次使用 merge 动词,但是这次 I--J--K--L <-- your-branch
/
...--o--o--H--P--Q--R <-- their-branch
\
I' <-- HEAD (temporary branch)
是“基础”,因此您所做的就是更改的内容在J
-vs-J
中。提交I
是右侧的提交。
如果存在合并冲突,则必须自己解决。如果没有,或者您完成后,Git进行提交I
:
J
现在对I'
和J'
重复此操作。因此,如果有四个提交要复制,则有四个要合并的动作发生,有四个合并冲突的机会,即使您已经在上一次提交中仔细地修复了所有这些< / em>。
这种 I--J--K--L <-- your-branch
/
...--o--o--H--P--Q--R <-- their-branch
\
I'-J' <-- HEAD (temporary branch)
和K
之间的主要区别在于,合并从一个合并基础提交(共同的起点)和两个提示提交进行合并,并将这些更改合并一次,但是L
列出每个提交,从公共起点到您自己的分支提示,然后尝试将其复制,一次一次,每个副本都是一个樱桃选择,这实际上是一个合并-因此您会得到一个合并每个复制的提交。即使您已经安排在最后一刻将所有问题都解决,这也为您提供了很多冲突的机会。