Git Rebase 冲突:谁是 HEAD?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23207791/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 18:00:51  来源:igfitidea点击:

Git Rebase Conflict: Who is HEAD?

gitmeld

提问by Joseph

I have this project where the remote repo has the main development branch, and I have a fork containing the experimental branch. I'm required to rebasechanges from the development branch to my experimental branch before I push to my fork. So it goes like:

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

git checkout experimentalbranch
git fetch remoterepo
git rebase remoterepo/developmentbranch

By this time, I hit conflicts. However, I'm not familiar with any of these changes(I'm rebasing weeks worth of changes, because they didn't merge my changes immediately). Also, it's my first time doing rebase. I'm more accustomed to merge.

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

In meld, it usually is like <<LOCAL||REMOTE>>for merge, which sounds very intuitive. But in rebase, it's <<HEAD||COMMIT MESSAGE>>. Who is HEAD? Is it the HEADof the development branch? Is it the latest code in the development branch or somewhere else?

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

回答by torek

TL;DR (added May 2018)

TL;DR(2018 年 5 月添加)

The whole thing is fundamentally at least a little bit confusing because Git lets its internal workings show right through to you.

整个事情从根本上说至少有点令人困惑,因为 Git 让其内部工作直接向您展示。

Note that the cases we are concerned with here occur when you run:

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

git checkout somebranch; git rebase origin/their-branch

or similar. The rebase has halted temporarily to force you to resolve a merge conflict, after which you're supposed to git addthe resolved conflict and run git rebase --continue. (If you use some merge tool with git mergetool, or a fancy GUI interface, that interface may do some or all of this for you some other way, but underneath, it's git adding the resolved files and running git rebase --continue.)

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

At the very beginning, the HEADcommit is theirbranch, so that if you use git checkout --oursor git checkout --theirs, --oursmeans theirs—the final commit of origin/their-branch—while --theirsmeans yours, the first commit you're rebasing. This is the normal everyday sort of Git confusion (see What is the precise meaning of "ours" and "theirs" in git?) and is not what led to the original question.

一开始,HEAD提交是他们的分支,所以如果你使用git checkout --oursor git checkout --theirs--ours意味着他们的——最后一次提交origin/their-branch——而--theirs意味着你的,你正在变基的第一个提交。这是正常的日常 Git 混淆(请参阅 Git中“我们的”和“他们的”的确切含义是什么),而不是导致最初问题的原因。

Later, however, the HEADcommit is actually a kind of mixture. It's the result of copying some number of your commits atop their latest commit. You're now getting a conflict between your own partly-built newseries of commits, and your own originalcommits. The source of this conflict is usuallysomething "they" did (something that changed along the way in origin/their-branch). You still have to resolve this conflict. When you do, you may see the very same conflict recur in later commits.

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

Again, HEADor localor --oursis a commit that rebase has built by combining your changes and their changes, and the other commit (remoteor >>>>>>>or --theirs) is your own commit, which rebase is trying to copy atop HEAD.

再次,HEADorlocal--oursrebase 通过组合您的更改及其更改构建的提交,而另一个提交(remote>>>>>>>--theirs)是您自己的提交,rebase 正试图复制该提交HEAD

Longer

更长

When merging (including rebasing, which is a special case of repeated "merging" internally), there are two "heads" (two specific branch-tips) involved. Let's call these your-branchand origin/their-branch:

合并时(包括变基,这是内部重复“合并”的一种特殊情况),涉及两个“头”(两个特定的分支提示)。让我们称这些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

This point is commonly (and unsurprisingly) confusing, although when labeled like this it's clear enough.

这一点通常(并且不出所料)令人困惑,尽管这样标记时就足够清楚了。

Making it worse, though, git uses --oursand --theirsto refer to the two head commits during a merge, with "ours" being the one you were on (commit H) when you ran git merge, and "theirs" being, well, theirs (commit L). But when you're doing a rebase, the two heads are reversed, so that "ours" is the head you're rebasing on-to—i.e., their updated code—and "theirs" is the commit you're currently rebasing, i.e., your own code.

然而,更糟糕的是,git 使用--ours--theirs来指代合并期间的两个主要提交,其中“我们的”是H您运行时所在的(提交)git merge,而“他们的”是他们的(提交L)。但是当你在做 rebase 时,两个 head 是相反的,所以“ours”是你要rebase 的 head——即他们更新的代码——而“theirs”是你当前正在 rebase 的提交,即,您自己的代码。

This is because rebase actually uses a series of cherry-pick operations. You start with much the same picture:

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

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

What git needs to do here is to copythe effect of commits Gand H, i.e., git cherry-pickcommit G, then do it again with commit H. But to do that, git has to switchto commit Lfirst, internally (using "detached HEAD" mode):

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

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

Now it can start the rebase operation by comparing the trees for commits Fand G(to see what you changed), then comparing Fvs L(to see if some of your work is already in L) and taking any changes not already in Land add it. This is a "merge" operation, internally.

现在它可以通过比较提交FG(查看您更改的内容)的树,然后比较Fvs L(查看您的某些工作是否已经在 中L)并进行任何尚未发生的更改L并添加它来启动变基操作。这是内部的“合并”操作。

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

If the merge does not go well, HEADis still left at commit L(because commit G'does not yet exist). Thus, yes, HEADis the head of their development branch—at least, it is right now.

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

Once the copy of Gexists, though, HEADmoves to G'and git attempts to copy the changes from H, in the same manner (diff Gvs H, then diff Fvs G', and merge the results):

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

Again, if the merge fails and needs help, you're left with HEADpointing to G'instead of H'as H'does not yet exist.

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

Once the merges all succeed and commits G'and H'doexist, git removes the label your-branchfrom commit H, and makes it point to commit H'instead:

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

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

and you are now rebased and HEADis once again what you would expect. But during the rebase, HEADis either their branch-tip (commit L), or one of the new commits copied and appended past their branch-tip; and --oursmeans the branch being grown at the end of Lwhile --theirsmeans the commit being copied-from (Gor Habove).

您现在已重新定位,并且HEAD再次符合您的期望。但是在变基期间,HEAD要么是他们的分支提示(commit L),要么是复制并附加到他们的分支提示之后的新提交之一;and--ours意味着分支在Lwhile结束时增长--theirs意味着提交被复制(GH更高版本)。

(This is basically git exposing the raw mechanism of howit does what it does, which happens rather a lot in git.)

(这基本上是 git 公开了它如何做的原始机制,这在 git 中发生得相当多。)

回答by Nicolás Alarcón Rapela

Definitions

定义

In this section we are going to see the deficiones that we are asked in response:

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

Who is HEAD?

谁是头?

HEAD: the current commit your repo is on. Most of the time HEAD points to the latest commit in your branch, but that doesn't have to be the case. HEADreally just means "what is my repo currently pointing at".

HEAD当前提交您的回购。大多数时候 HEAD 指向您分支中的最新提交,但情况并非如此。 HEAD真的只是意味着“我的回购目前指向的是什么”。

In the event that the commit HEADrefers to is not the tip of any branch, this is called a "detached head".

如果提交HEAD所指的不是任何分支的尖端,这称为“ detached head”。

Is it the HEAD of the development branch?

是开发分支的HEAD吗?

At the moment in which a merge or rebase is made the HEADimmediately passes to point to the created or refactorized commitand therefore will be pointing to the development branch.

在进行合并或变基的那一刻,HEAD立即传递指向已创建或重构的提交因此将指向开发分支

In git bashwe can see theHEADsituation, listing commit:

git bash我们可以看到的HEAD情况下,列出 commit:

# 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

Practice

实践

In this section we will see the _how_ works some actions performed by the user

在本节中,我们将看到 _how_ 是用户执行的一些操作

When the user proceeds to :

当用户继续:

# 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

By this time, I hit conflicts. However, I'm not familiar with any of these changes (I'm rebasing weeks worth of changes, because they didn't merge my changes immediately). Also, it's my first time doing rebase. I'm more accustomed to merge.

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

The union of branches is done in two ways:

分支的联合通过两种方式完成:

  • git merge

  • git rebase.

  • 合并

  • git rebase。

Note:

注意

For the examples we will use the following tree:

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

* 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 merge

合并

The best known form is git merge, which performs a fusion to three bands between the last two snapshots of each branch and the common ancestor to both, creating a new commit with mixed changes.

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

For example :

例如 :

git checkout remoterepo
git merge experimentalbranch

It would produce us :

它会产生我们:

*   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

git rebasebasically what it does is to collect one by one the changes confirmed in one branch, and reapply them on another.

git rebase基本上它所做的就是将一个分支中确认的更改一个一个地收集起来,然后在另一个分支上重新应用它们

Using rebase can help us avoid conflicts whenever it is applied to commits that are local and have not been uploaded to any remote repository. If you are not careful with the latter and a partner uses affected changes, sure you will have problems since these types of conflicts are usually difficult to repair.

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

For example :

例如 :

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

What is origin?

什么是起源?

origin: the default name that git gives to your main remote repo. Your box has its own repo, and you most likely push out to some remote repo that you and all your coworkers push to. That remote repo is almost always called origin, but it doesn't have to be.

origin: git 给你的主远程仓库的默认名称。你的盒子有自己的仓库,你很可能推送到一些你和你所有同事推送到的远程仓库。那个远程仓库几乎总是被称为 origin,但它不一定是。