来自Loeliger 2ed的Git版本控制,关于git pull中的合并或重组步骤:
在拉动操作的第二步中,Git执行a 合并(默认)或rebase操作。
关于git pull中的合并步骤:
在这个例子中,Git合并了内容 远程跟踪分支,origin / master,进入您的本地跟踪 branch,master ,使用一种称为a的特殊类型的合并 快进
但Git是如何知道合并这些特定分支的呢?答案 来自配置文件:
[branch "master"] remote = origin merge = refs/heads/master
转述,这给了Git两个关键信息:当掌握时 是当前的签出分支,使用origin作为默认远程 在获取(或拉取)期间从中获取更新。 此外,在git pull的合并步骤中,使用refs / heads / master 从远程作为默认分支合并到此,主人 分支。
一般来说,合并步骤是否将远程跟踪分支合并到master
或当前分支?我的猜测是当前分支,即由HEAD
指向的分支,并且不一定是master
。
请注意我的猜测来自https://git-scm.com/docs/git-pull
将来自远程存储库的更改合并到当前 分支即可。在默认模式下,git pull是git fetch的简写 然后是git merge FETCH_HEAD。
更确切地说,git pull使用给定的参数运行git fetch 调用git merge将检索到的分支头合并到当前 分支
git pull
是否始终将远程跟踪分支合并到当前分支中? (我的猜测是肯定的)。
如果没有,是否有git pull
的参数在合并步骤中指定目标分支?
如果我是正确的,refspec
的{{1}}参数不会指定合并步骤的目标分支。
为什么合并是“快进合并”?
关于git pull中的rebase步骤:
命令git pull --rebase将导致Git重组(而不是 合并)您的本地跟踪分支到远程跟踪分支上 只是在拉动期间。
git pull
的rebase步骤将当前分支(即git pull
指向的分支)重新绑定到远程跟踪分支上是否正确?
如果是,为什么报价会说“你的本地跟踪分支”而不是当前的分支?答案 0 :(得分:2)
一般来说,合并步骤是否将远程跟踪分支合并为主分支或当前分支?
这两个是相同的:master
是你当前的分支。远程跟踪分支是origin/master
。
(其中origin
只是用于远程的默认名称,而不是任何特殊的Git"关键字",而master
是默认分支。实际名称可能在实际上有所不同场景。)
rebase
或merge
操作的目标(结果更改的目标)是当前分支。
checkout origin/master
那样做一些愚蠢的事情来试图让追踪分支成为现实,那么Git会打你。
因此,合并的目标是master
。问题是,什么被合并?如果您提取一些新的上游材料,最终可能会出现(例如)这种情况:
YOURS (master)
*
\ UPSTREAM (origin/master)
* *
\ /
* <--- "git merge-base master origin/master"
|
*
master
和origin/master
分歧,分别有2次和1次新提交。
使用git rebase
,YOURS
下的两个本地提交将重写在UPSTREAM之上:
YOURS (master)
* <-- rewritten: SHA changes
\
ghost of YOURS * <-- rewritten: SHA changes
* ---- * \
\ * UPSTREAM (origin/master)
\ /
*
|
现在情况是&#34;本地分支master
领先于origin/master
2次提交&#34;。您已准备好(尝试)git push
。
你原来的unrewritten提交仍然存在(用#34表示;你的幽灵和#34;在ASCII图中)。它们在git reflog
中引用。当它们从那里到期或被手动清除时,它们变成垃圾,最终被垃圾收集。
使用git merge
,您可以执行其他操作:您的更改将与origin/master
合并,并创建一个包含两个父项的新提交:
* YOURS-merged (master)
/ \
YOURS / \
* |
\ | UPSTREAM (origin/master)
* *
\ /
* <--- "git merge-base master origin/master"
|
*
现在,您的master
提前origin/master
提交1次。如果您现在成功推送,远程存储库的master
也将采用上述形式,双父提交沿着一条路径进行更改,而前一个上游提交沿另一条路径进行。没有你自己的幽灵&#34;:你的改变从未改写过。
为什么合并是&#34;快进合并&#34;?
快速合并或重组在两种情况下发生。一个是origin/master
没有任何平行变化:
YOURS (master)
*
\
*
\
* UPSTREAM (origin/master)
|
*
在上面的例子中,没有任何事情要做:一切都是最新的,你提前两次提交origin/master
。因此,这不是一个快进的东西&#34 ;; git merge
或git rebase
根本不会做任何事情。如果您对这些提交感到满意,可以尝试git push
。
如果您没有任何本地更改,则会发生真正的快进场景:
* UPSTREAM (origin/master)
/
YOURS (master) *
|
*
在这种情况下,如果您执行git rebase
或git merge
,则默认情况下,master
HEAD
指针只会向前滑动&#34;指向与origin/master
相同的提交,此幻灯片是&#34;快进&#34;:
YOURS (master) * UPSTREAM (origin/master)
/
* <- master slid forward from here
|
*
&#34;快进&#34;术语可能指的是只有HEAD
指针移动的想法(以及重写本地文件系统树以匹配)。不得写入或重写新的提交。
在分歧场景中不可能进行快进(除非你丢弃本地更改)。必须生成新的合并提交,否则rebase必须重写一些更改。在进行这些操作之前,不存在master
可以向前滑动的任何提交。
有些人以某种方式使用Git,他们希望将所有本地工作提交合并。当上游没有新工作要合并时,你可以强制Git用git merge --no-ff
进行合并提交(没有快进)。从这开始:
YOURS (master)
*
\
*
\
* UPSTREAM (origin/master)
|
*
你得到这样的东西:
----* YOURS-merge (master)
YOURS / |
* |
\ |
* |
\ |
* UPSTREAM (origin/master)
|
*
这个想法是,在历史的主线路径中,这两个变化似乎凝聚成一步。可以按照另一个父级查看具有多次提交的详细谱系。
为什么报价会说&#34;您的本地跟踪分支&#34;而是当前的分支?
这是一个错误。涉及的所有分支都是本地的(在您的存储库中)。有工作分支(如master
)及其远程跟踪分支(如origin/master
)。两者都是本地的。
你工作的分支不称为跟踪分支;它没有跟踪任何事情。
某些本地分支机构未与远程跟踪分支机构配对。没有远程的repo中的所有分支(例如由git init
新创建的东西)都是纯粹本地的。除非推送到远程仓库,否则任何本地创建的分支都是纯本地的。
远程跟踪分支是一个类似分支的对象,它与本地分支配对。它跟踪上游正在做什么。每次执行git fetch
时,都可能会重写它以指向某个不同的提交。本地分支不受影响。这两者可能会分歧,这可以通过rebase或merge来解决。当您执行git push
时,本地分支会更新上游存储库中的实际远程。当此操作成功(远程端未拒绝)时,本地跟踪分支也会更新为指向同一个提交,因此它们保持同步。
在正常情况下,没有纯粹的本地操作会改变origin/master
:它会跟踪远程仓库中相应分支的状态。
答案 1 :(得分:1)
正如我最近的其他评论一样,我想避免使用术语&#34;本地跟踪分支&#34;完全并且只关注本地分支是否有上游集合,如果是这样,上游设置的内容(作为您引用笔记的书,分为两部分,&#34;远程&#34;和&#34;合并&#34;字符串)。
关于你的第一个问题:
一般来说,合并步骤是否将远程跟踪分支合并为主分支或当前分支?
总是当前分支。 git pull
命令(以前是一个脚本但最近在C中重写)实际运行git merge
,而git merge
始终以当前分支(和当前工作树)开始。< / p>
合并的内容有点棘手。再次,这是你引用的一点;我将强调重点:
...
git pull
...调用git merge
将检索到的分支头合并到当前分支中。
一旦你习惯了&#34;总是合并到当前分支&#34;,这里剩下的关键问题是:检索了哪些分支头?
答案有点复杂,因为它取决于您传递给git pull
的参数。一般形式是git pull remote [options] refspec [refspec ...]
,即第二个单词是文字字符串pull
,第三个单词(解析任何选项后)是遥控器的名称,第四个和更多的单词是&#34; refspecs&#34;
您可以指定refspec或多个refspec,在这种情况下git fetch
使用这些来引入某些特定的分支头或头,这些是合并的。或者,您可以将它们排除在外,在这种情况下,git pull
会指示git fetch
将当前分支机构带到上游。 1
这导致了一个常见的错误。如果您的本地分支A
和B
分别设置为跟踪origin/A
和origin/B
,则有人会输入命令git pull A B
,认为这会更新origin/A
和origin/B
- 它会! 2 - 然后还认为git会将origin/A
合并到A
中,并{{1进入origin/B
,它就赢了。相反,它带来了两个分支头,然后运行一个章鱼合并&#34;进入当前分支(无论当前分支是什么)。
这个多部分问题的答案是:
git pull总是将远程跟踪分支合并到当前分支中吗? (我的猜测是肯定的)。如果没有,是否有一个参数git pull指定合并步骤中的目标分支?如果我是正确的,git pull的refspec参数不会为合并步骤指定目标分支。
(A)不,或者至少,它不能得到保证:它合并了检索到的分支头(B
中记录的git fetch
;以及FETCH_HEAD
检索了额外的头部,有时它会检查git fetch
到&#34;隐藏&#34;它们是否来自合并步骤),它可能与远程相同或不同跟踪分支设置为当前分支的上游。根据我的经验,当大多数用户不是当前分支机构的上游时,他们会感到惊讶;这通常不是他们想要的。
(B)是:&#34; refspecs&#34;指定(如果有)确定检索哪些分支头。获取refspecs与push refspecs略有不同,我不会在这里详细介绍(详见this answer),但简短版本是如果你省略冒号not-for-merge
,这些是存入:
进行合并的头部。如果需要,FETCH_HEAD
代码会提供默认的refspec,以便pull
中有一些内容。
这个问题的答案也有点复杂,因为这个问题有两个假设可能是错误的:
为什么合并是&#34;快进合并&#34;?
首先,它不一定是快进合并。
第二,&#34;快进合并&#34;是一种用词不当,或至少是一个简短的名称,掩盖了更深层的真相。 &#34;快进&#34;实际上是标签移动的属性,而不是合并的属性。 Git称合并为&#34;快进&#34;当不需要实际合并时,可以改为执行简单的标签移动。
请记住,在git中,单词&#34; branch&#34;至少有两个含义:分支名称,如FETCH_HEAD
或master
或feature
或其他,然后就是这样底层提交图数据结构。数据结构是永久性的,但名称是短暂的,可以更改。 3 当您要求git将名为 bug2.71828
的分支合并到当前分支中时,它首先将名称 X
解析为提交ID。然后,它检查当前X
提交(当前分支的提示)是否是目标提交ID的祖先。如果是这样,则不需要实际合并。 4
在&#34;不需要实际合并&#34; git通常会使用简单的标签移动替换合并操作:如果HEAD
是对分支名称的符号引用(即,如果您在分支上&#34;&#34;在{{}; HEAD
1}} term),分支名称 - 标签 - 简单地#34;向前移动&#34;指向目标提交。 (如果你在&#34;分离的HEAD&#34;模式git做同样的事情,但是将新ID写入git status
本身,而不是写入当前分支的文件。)< / p>
如果您提供HEAD
标志,即使可以进行快速转发,也会强制--no-ff
进行实际的合并提交。 (以相同但相反的方式,git merge
强制git 而不是进行合并,但如果需要真正的合并,则不使一个失败,因此--ff-only
在这种情况下将失败,而--ff-only
在&#34;合并所需&#34;以及&#34;不需要合并&#34;案例中成功。 )
这个答案已经足够长(或太长了:-))所以我要说的关于添加--no-ff
的所有内容是--rebase
步骤的工作方式相同,但是fetch
代码然后使用pull
(使用git rebase
,并使用最近版本的git,使用&#34; fork points&#34;)进行重新绑定到引出的头部的一些额外复杂性(如果你获取,则必须输出错误)多个头,因为你不能在不止一个头上转折。)
1 对于当前分支的上游有一个文字点--onto
作为其&#34;远程&#34;的情况,有一个特殊的例外情况。名称。在这种情况下,&#34;合并&#34;设置是指另一个本地分支,而不是远程跟踪分支,而.
会跳过git pull
步骤,因为无需从您自己获取。< / p>
2 这假设你有git版本1.8.4或更新版本。在较旧版本的git中,git fetch
步骤会带来分支头,但无法更新git fetch
和origin/A
,原因是故意但结果不好(因此1.8中的更改) 0.4)。
3 据我所知,git在这方面是独一无二的。其他VCS,包括Mercurial,不会以这种方式实现分支。分支仍然可以重命名,但名称更深入嵌入,无法完全删除,就像它们在git中一样。
4 或者,我们可以说git首先计算merge-base,如果merge-base是当前提交,则不需要实际的合并。
答案 2 :(得分:1)
一般来说,合并步骤是否合并了远程跟踪 分支成主或当前分支?我的猜测是当前的 分支,即HEAD指向的分支,并不一定 主
是的,pull
操作不会更改分支,您将在同一分支上结束(可能指向不同的提交)。
git pull将远程跟踪分支始终合并到当前 科? (我的猜测是肯定的)。如果没有,是否存在git pull的争论 在合并步骤中指定目标分支?如果我是对的, git pull的refspec参数没有指定目标分支 合并步骤。
是的,你是对的,git pull
没有签出不同的分支,并且没有争论。拉动后,手动使用checkout
命令切换到另一个分支。
为什么合并是&#34;快进合并&#34;?
A&#34;快进&#34; merge是主题分支的fork-point是原始基本分支的tip。 (master
没有target
这是一个重要的概念,请查看this article。 In-a-nut-shell FF合并不会创建合并提交。 (在各种情况下可能或可能不合适)。
git的rebase步骤是否正确使当前变为正确 分支(即HEAD指向的分支)到远程跟踪上 科?如果是,为什么报价会说&#34;您的本地跟踪分支&#34; 而是当前的分支?
git pull rebase
会使用branch that it is tracking来修改HEAD。
答案 3 :(得分:0)
关于git pull --rebase
还有另一个混淆的原因:做一个合并快进的基础。
git fetch
之后(pull --rebase
的一部分)
-x--x (master)
\
o--o--o (origin/master, local tracking branch now up-to-date)
这里的rebase不需要发生。您无法在“master
之上重播origin/master
”,因为master
的祖先中已包含origin/master
。
这里所有git pull --rebase
应该是一个快进合并:
-x--x--o--o--o (master, origin/master)
这就是Git 2.12(2017年第一季度)提出的建议:
commit 33b842a查看Junio C Hamano (gitster
)(2016年6月29日)。
(Junio C Hamano -- gitster
--合并于commit 2fb11ec,2016年12月19日)
pull
:快进“pull --rebase=true
”“
git pull --rebase
”在获取后始终运行“git rebase
” 承诺作为新基地,即使新基地是一个 当前HEAD的后代,即我们没有做任何工作。在这种情况下,我们可以在没有的情况下快进到新基地 调用rebase过程。