Git Rebase 冲突: 谁是 HEAD?

我有这样一个项目,其中远程回购有主要的开发分支,我有一个包含实验分支的分支。在进入我的分支之前,我需要将 rebase从开发分支更改为实验分支。就像这样:

git checkout experimentalbranch
git fetch remoterepo
git rebase remoterepo/developmentbranch

这个时候,我遇到了冲突。但是,我对这些变化一无所知(我正在重新调整数周的更改,因为它们没有立即合并我的更改)。还有,这是我第一次做 rebase。我更习惯于 merge

在混合,它通常是像 <<LOCAL||REMOTE>>merge,这听起来非常直观。但在 rebase里,是 <<HEAD||COMMIT MESSAGE>>。谁是 HEAD?是开发部门的 HEAD吗?它是开发分支中的最新代码还是其他地方的代码?

40516 次浏览

DR (2018年5月增加)

整个事情从根本上来说至少有点令人困惑,因为 Git 让它的内部工作直接显示给您看。

注意,我们在这里关注的情况发生在您运行:

git checkout somebranch; git rebase origin/their-branch

或类似的。Rebase 已经暂时停止,以迫使您解决合并冲突,在此之后,您应该 git add已解决的冲突并运行 git rebase --continue。(如果你使用一些合并工具与 git mergetool,或一个奇特的 GUI 接口,该接口可能做一些或所有这一切为您的其他方式,但在下面,它的 git adding 解析文件和运行 git rebase --continue。)

一开始,HEAD提交是 他们的分支,所以如果你使用 git checkout --oursgit checkout --theirs--ours意味着 他们的ーー origin/their-branch的最后提交,而 --theirs意味着 你的,你正在重新定位的第一个提交。这是日常生活中常见的 Git 混淆(参见 “我们的”和“他们的”在 git 中的确切含义是什么?) ,并不是导致最初问题的原因。

然而,之后的 HEAD提交实际上是 一种混合物。是 在最新的提交上复制一些您的提交的结果。您现在得到了自己部分构建的 新的系列提交与自己的 原创的提交之间的冲突。这种冲突的根源是 通常“他们”所做的事情(在 origin/their-branch中一路改变的事情)。你还是得解决这个矛盾。当您这样做时,您可能会在以后的提交中看到同样的冲突重现。

同样,HEADlocal--oursRebase 通过组合您的更改和它们的更改构建的提交,另一个提交(remote>>>>>>>--theirs)是您自己的提交,rebase 试图在 HEAD之上复制它。

更久

当合并时(包括重定基,这是内部重复“合并”的特殊情况) ,涉及到两个“头”(两个特定的分支提示)。让我们把这些称为 your-branchorigin/their-branch:

              G - H --------      <-- HEAD=your-branch
/               \
... - E - F                   M   <-- desired merge commit [requires manual merge]
\               /
I - J - K - L       <-- origin/their-branch

这一点通常是令人困惑的(并不令人惊讶) ,尽管这样的标签已经足够清楚了。

不过,更糟糕的是,git 使用 --ours--theirs来指代合并过程中的两个头部提交,“ our”是你运行 git merge时所在的那个头部提交(提交 H) ,而“ their”是,嗯,他们的头部提交(提交 L)。但是当你重新定位时,两个头是相反的,所以“ our”就是你重新定位的头-to-i。例如,他们更新的代码ーー而“他们的”是你目前正在重新定基的提交,也就是你自己的代码。

这是因为 rebase 实际上使用了一系列樱桃选择操作:

              G - H           <-- HEAD=your-branch
/
... - E - F
\
I - J - K - L   <-- origin/their-branch

这里 git 需要对 收到执行提交 GH的效果,即 git cherry-pick提交 G,然后用提交 H再次执行。但是要做到这一点,git 必须首先在内部(使用“分离的 HEAD”模式)提交 L:

              G - H           <-- your-branch
/
... - E - F
\
I - J - K - L   <-- HEAD, origin/their-branch

现在它可以通过比较提交 FG的树来启动 rebase 操作(看看你修改了什么) ,然后比较 FL(看看你的一些工作是否已经在 L中完成了) ,并获取任何在 L中没有完成的修改并添加它。这是内部的“合并”行动。

              G - H           <-- your-branch
/
... - E - F                   G'   <-- HEAD
\               /
I - J - K - L   <-- origin/their-branch

如果合并不顺利,HEAD仍然留在提交 L时(因为提交 G'还不存在)。因此,是的,HEAD是他们开发部门的负责人ーー至少现在是这样。

然而,一旦 G的副本存在,HEAD移动到 G',git 尝试以相同的方式从 H复制变化(区分 GH,然后区分 FG',并合并结果) :

              G - H           <-- your-branch
/
... - E - F                   G' - H'   <-- HEAD
\               /
I - J - K - L   <-- origin/their-branch

同样,如果合并失败并需要帮助,那么只剩下 HEAD指向 G'而不是 H',因为 H'还不存在。

一旦合并成功并提交了 G'H' ,git 就会从提交 H中移除标签 your-branch,并使其指向提交 H':

              G - H
/
... - E - F                   G' - H'   <-- HEAD=your-branch
\               /
I - J - K - L   <-- origin/their-branch

你现在是重新基础和 HEAD再次是你所期望的。但是在重建基础的过程中,HEAD要么是他们的分支提示(提交 L) ,要么是被复制并附加到分支提示后面的一个新提交; 而 --ours意味着分支在 L的末尾增长,而 --theirs意味着提交被复制(上面的 GH)。

(这基本上就是 git 暴露了 怎么做的原始机制,它完成了自己的工作,这在 git 中经常发生。)

定义

在本节中,我们将看到我们被要求回答的缺陷:

谁是校长?

HEAD : 当前提交您的回购是在.大多数情况下,HEAD 指向您的分支中的最新提交,但是情况并非如此。 HEAD实际上只是意味着“我的回购目前指向什么”。

如果提交 HEAD引用的不是任何分支的提示,则称为“ detached head”。

是开发部门的头儿吗?

在进行合并或重新建基的时候,HEAD 立即传递到指向创建的或重构的提交因此将指向发展分支

git bash中,我们可以看到 HEAD的情况,列表提交:

# Normal commit list
git log
# List of commit in a single line
git log --oneline
# All commits graphically-linear (Recommended as alias)
git log --all --graph --decorate --oneline

练习

在本节中,我们将看到 _ how _ works 用户执行的一些操作

当用户执行以下操作时:

# Command to change from the branch to the current one to experimentalbranch
git checkout experimentalbranch
# Command that traverses the typical workflow to synchronize its local repository with the main branch of the central repository (remoterepo)
git fetch remoterepo
# git fetch origin
# git fetch origin branch:branch
# With the command git rebase, you can take all the changes confirmed in one branch (remoterepo), and reapply them over another developmentbranch
git rebase remoterepo/developmentbranch

这个时候,我遇到了冲突。然而,我对这些变更并不熟悉(我正在重新调整数周的变更,因为它们没有立即合并我的变更)。还有,这是我第一次做 rebase。我更习惯合并。

分支机构的联合有两种方式:

  • Git 合并

  • Git rebase.

:

对于示例,我们将使用以下树 :

* a122f6d (HEAD -> remoterepo) Commit END
* 9667bfb Commit MASTER
| * b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
| * 110b2fb Commit 2
| * e597c60 Commit 1
|/
* 0e834f4 (origin/remoterepo) First commit

Git 合并

最著名的形式是 git merge,它在每个分支的最后两个快照和两个分支的共同祖先之间执行三个波段的融合,创建一个具有混合更改的新提交。

例如:

git checkout remoterepo
git merge experimentalbranch

它会产生我们:

*   003e576 (HEAD -> remoterepo) Merge branch 'experimentalbranch' in remoterepo
|\
| * b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
| * 110b2fb Commit 2
| * e597c60 Commit 1
* | a122f6d Commit END
* | 9667bfb Commit MASTER
|/
* 0e834f4 (origin/remoterepo) First commit

Git rebase

git rebase基本上和 是逐一收集在一个分支中确认的变更,然后在另一个分支上重新应用它们一样。

使用 rebase 可以帮助我们 在应用于本地提交且未上载到任何远程存储库时避免冲突。 如果您不小心使用后者,并且合作伙伴使用受影响的更改,那么您肯定会在 这种类型的冲突通常很难修复之后遇到问题。

例如:

git checkout remoterepo
git rebase experimentalbranch




* f8a74be (HEAD -> remoterepo) Commit END
* 4293e9d Commit MASTER
* b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
* 110b2fb Commit 2
* e597c60 Commit 1
* 0e834f4 (origin/remoterepo) First commit

起源是什么?

origin: Git 为主远程回购提供的默认名称.你的盒子有自己的回购,你最有可能推出一些远程回购,你和所有的同事推到。这种远程回收几乎总是被称为起源,但它不一定是。