在重新定基时有许多冲突,为什么PR不产生任何冲突?

时间:2018-08-09 07:27:10

标签: git commit rebase

在创建PR之前,我想确保我的代码与master保持最新,因此我尝试git rebase master。这就产生了可怕的无限冲突情况,据此,基准服务器将报告冲突,我将解决该冲突,将其添加,点击git rebase --continue,然后循环将重复-似乎永远如此。

但是,当我创建PR时,发现它没有冲突。我检查了它与我的分支机构和master之间的比较视图,并且可以肯定的是,没有冲突。

我对重定基的了解告诉我,重定基仅应将分支提交移动到比master中的最新提交更新的位置。因此,我不完全理解为什么会发生冲突-除非我也许不知道提交包含的内容是什么?

1 个答案:

答案 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这样做)到同一起始提交*他们的分支的尖端,我叫RR代表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)。

如何通过Cherry-pick理解变基

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,并且
  • 它从{em> {em}中的快照 R开始,而不是H中的快照。

J'L'都一样:它们都必须做出与IJ,{ {1}}和K,但他们必须在LR和{{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列出个提交,从公共起点到您自己的分支提示,然后尝试将其复制,一次一次,每个副本都是一个樱桃选择,这实际上是一个合并-因此您会得到一个合并每个复制的提交。即使您已经安排在最后一刻将所有问题都解决,这也为您提供了很多冲突的机会。