73

我有这个项目,其中远程仓库有主要的开发分支,我有一个包含实验分支的叉子。rebase在推送到我的 fork 之前,我需要从开发分支更改为我的实验分支。所以它是这样的:

git checkout experimentalbranch
git fetch remoterepo
git rebase remoterepo/developmentbranch

到了这个时候,我遇到了冲突。但是,我对这些更改中的任何一个都不熟悉(我正在重新调整数周的更改,因为它们没有立即合并我的更改)。另外,这是我第一次做rebase。我比较习惯merge

在融合中,它通常类似于<<LOCAL||REMOTE>>for merge,这听起来很直观。但在rebase,它是<<HEAD||COMMIT MESSAGE>>。谁是HEAD?它是HEAD开发分支的吗?它是开发分支中的最新代码还是其他地方的最新代码?

4

2 回答 2

123

TL;DR(2018 年 5 月添加)

整个事情从根本上来说至少有点令人困惑,因为 Git 让它的内部运作直接向你展示。

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

git checkout somebranch; git rebase origin/their-branch

或类似的。rebase 暂时停止以强制您解决合并冲突,之后您应该git add解决冲突并运行git rebase --continue. (如果您使用一些带有 的合并工具git mergetool,或花哨的 GUI 界面,该界面可能会以其他方式为您完成部分或全部操作,但在下面,它正在git add解析已解析的文件并正在运行git rebase --continue。)

一开始,HEAD提交是他们的分支,所以如果你使用git checkout --oursor git checkout --theirs--ours表示他们的——最后一次提交origin/their-branch——而--theirs表示的,你要变基的第一个提交。这是日常生活中常见的 Git 混淆(请参阅What is the exact meaning of "ours" and "theirs" in git?),并不是导致原始问题的原因。

然而,后来,HEAD提交实际上是一种混合。这是在最新提交上复制一些提交的结果。您现在在自己部分构建的系列提交和您自己的原始提交之间遇到冲突。这种冲突的根源通常是“他们”所做的事情(在 的过程中发生了变化origin/their-branch)。你仍然需要解决这个冲突。当您这样做时,您可能会在以后的提交中看到相同的冲突再次出现。

再次,HEADor localor--oursrebase 通过组合您的更改及其更改构建的提交,而另一个提交 ( remoteor>>>>>>>--theirs) 是您自己的提交,rebase 正在尝试复制 atop HEAD

更长

合并时(包括变基,这是内部重复“合并”的一种特殊情况),涉及两个“头”(两个特定的分支提示)。让我们称它们为your-branchand origin/their-branch

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

这一点通常(并且不足为奇)令人困惑,尽管当这样标记时,它已经足够清楚了。

更糟糕的是,git 在合并期间使用--oursand--theirs来引用两个头部提交,其中“我们的”是您在H运行时(提交)的那个git merge,而“他们的”是他们的(提交L)。但是当你做一个变基时,两个头是相反的,所以“我们的”是你要变基的头——即他们更新的代码——而“他们的”是你当前变基的提交,即,您自己的代码。

这是因为 rebase 实际上使用了一系列的樱桃挑选操作。您从几乎相同的图片开始:

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

git在这里需要做的是复制Gcommits的效果H,即git cherry-pickcommit G,然后用commit再做一次H。但要做到这一点,git 必须先在内部切换到提交L(使用“分离的 HEAD”模式):

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

现在它可以通过比较提交的树FG(查看您更改的内容),然后比较Fvs L(查看您的某些工作是否已经在L)来启动 rebase 操作,并进行任何尚未在的更改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相同的方式从 复制更改(diff Gvs H,然后 diff Fvs G',并合并结果):

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

同样,如果合并失败并需要帮助,您将只剩下HEAD指向G'而不是H'asH'尚不存在。

一旦合并全部成功并提交G'并且H' 确实your-branch存在,git从 commit中删除标签H,并使其指向 commit H'

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

你现在被重新定位并且HEAD再次是你所期望的。但是在变基期间,HEAD要么是他们的分支提示(提交L),要么是复制并附加到他们的分支提示之外的新提交之一;并且意味着在while--ours结束时增长的分支意味着从(或更高版本)复制的提交。L--theirsGH

(这基本上是 git 暴露了它如何做它所做的事情的原始机制,这在 git 中发生了很多。)

于 2014-04-22T00:00:05.330 回答
5

定义

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

HEAD是谁?

HEAD您的 repo 正在进行的当前提交。大多数时候 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_ 工作原理

当用户继续:

# 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 变基。

注意

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

* 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 给你的主要远程仓库的默认名称。你的盒子有自己的仓库,你很可能会推送到你和你所有同事推送到的某个远程仓库。那个远程仓库几乎总是被称为起源,但它不是必须的。

于 2018-06-14T10:08:09.967 回答