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
Git Rebase Conflict: Who is HEAD?
提问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 rebase
changes 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 HEAD
of 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 add
the 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 add
ing 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 HEAD
commit is theirbranch, so that if you use git checkout --ours
or git checkout --theirs
, --ours
means theirs—the final commit of origin/their-branch
—while --theirs
means 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 --ours
or git checkout --theirs
,--ours
意味着他们的——最后一次提交origin/their-branch
——而--theirs
意味着你的,你正在变基的第一个提交。这是正常的日常 Git 混淆(请参阅 Git中“我们的”和“他们的”的确切含义是什么?),而不是导致最初问题的原因。
Later, however, the HEAD
commit 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, HEAD
or local
or --ours
is a commit that rebase has built by combining your changes and their changes, and the other commit (remote
or >>>>>>>
or --theirs
) is your own commit, which rebase is trying to copy atop HEAD
.
再次,HEAD
orlocal
或--ours
是rebase 通过组合您的更改及其更改构建的提交,而另一个提交(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-branch
and origin/their-branch
:
合并时(包括变基,这是内部重复“合并”的一种特殊情况),涉及两个“头”(两个特定的分支提示)。让我们称这些your-branch
和origin/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 --ours
and --theirs
to 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 G
and H
, i.e., git cherry-pick
commit G
, then do it again with commit H
. But to do that, git has to switchto commit L
first, internally (using "detached HEAD" mode):
git在这里需要做的是复制commitsG
和的效果H
,即git cherry-pick
commit 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 F
and G
(to see what you changed), then comparing F
vs L
(to see if some of your work is already in L
) and taking any changes not already in L
and add it. This is a "merge" operation, internally.
现在它可以通过比较提交F
和G
(查看您更改的内容)的树,然后比较F
vs 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, HEAD
is still left at commit L
(because commit G'
does not yet exist). Thus, yes, HEAD
is the head of their development branch—at least, it is right now.
如果合并不顺利,HEAD
仍然留在提交L
(因为提交G'
尚不存在)。因此,是的,HEAD
是他们开发部门的负责人——至少,现在是。
Once the copy of G
exists, though, HEAD
moves to G'
and git attempts to copy the changes from H
, in the same manner (diff G
vs H
, then diff F
vs G'
, and merge the results):
G
但是,一旦存在的副本HEAD
移动到G'
git 尝试从 复制更改H
,以相同的方式(diff G
vs H
,然后 diff F
vs 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 HEAD
pointing 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-branch
from 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 HEAD
is once again what you would expect. But during the rebase, HEAD
is either their branch-tip (commit L
), or one of the new commits copied and appended past their branch-tip; and --ours
means the branch being grown at the end of L
while --theirs
means the commit being copied-from (G
or H
above).
您现在已重新定位,并且HEAD
再次符合您的期望。但是在变基期间,HEAD
要么是他们的分支提示(commit L
),要么是复制并附加到他们的分支提示之后的新提交之一;and--ours
意味着分支在L
while结束时增长--theirs
意味着提交被复制(G
或H
更高版本)。
(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.
HEAD
really just means "what is my repo currently pointing at".
HEAD
:当前提交您的回购。大多数时候 HEAD 指向您分支中的最新提交,但情况并非如此。
HEAD
真的只是意味着“我的回购目前指向的是什么”。
In the event that the commit HEAD
refers 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 HEAD
immediately passes to point to the created or refactorized commitand therefore will be pointing to the development branch.
在进行合并或变基的那一刻,HEAD
立即传递指向已创建或重构的提交,因此将指向开发分支。
In git bash
we can see theHEAD
situation, 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 rebase
basically 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,但它不一定是。