带refspec的Git pull
我读了这篇文章,现在我对git pull如何与refpec一起工作产生了疑问:带refspec的Git pull,git,Git,我读了这篇文章,现在我对git pull如何与refpec一起工作产生了疑问: Step 1 : I am on branchA. Step 2 : I do `git pull origin branchB:branchC` . Step 3: I notice : a) commits from branchB on remote comes and update `remotes/origin/branchC` b) Then a merge happened. `branchC
Step 1 : I am on branchA.
Step 2 : I do `git pull origin branchB:branchC` .
Step 3: I notice :
a) commits from branchB on remote comes and update `remotes/origin/branchC`
b) Then a merge happened. `branchC` was updated with `remotes/origin/branchC`
c) The `branchC` was merged into `branchA`.
现在,我很困惑,既然git pull=git fetch+git merge,那么2个合并是如何发生的呢?步骤b)和步骤c)都是合并。步骤2不是真正的合并,而是合并。快进是非当前(即当前未签出)分支唯一可能的合并类型。如果无法实现快速转发,git将中止
获取/拉取
;在这种情况下,您可以执行真正的合并(签出branchC并运行git pull origin branchB
),或者执行强制更新(git fetch origin+branchB:branchC
),从而在branchC的头部丢失本地提交。第2步不是真正的合并,而是一个错误。快进是非当前(即当前未签出)分支唯一可能的合并类型。如果无法实现快速转发,git将中止获取/拉取
;在这种情况下,您可以执行真正的合并(签出branch并运行git pull origin branchB
),或者执行强制更新(git fetch origin+branchB:branchC
),从而在branchC的头部丢失本地提交。是正确的。将git pull
命令分解为两个组件:
git获取来源branchB:branchC
。在相同的设置上运行此命令,即,将branchC
设置为指向在git pull
命令之前指向的提交git合并
。实际哈希ID取自.git/FETCH\u HEAD
,其中git FETCH
保留它。在相同的设置上运行此命令,将branchA
设置为指向在git pull
命令之前指向的提交git合并
,对引用branchC
没有影响。它确实对当前分支名称有一些影响,即,refs/heads/branchA
。因为它运行的是gitmerge
,所以它可以进行快进合并,或者真正的合并,或者什么都不做
让我们深入研究fetch
步骤,这确实是一个更有趣,或者至少更具挑战性的步骤
git-ls-remote
在运行git fetch origin branchB:branchC
之前,请运行git ls remote origin
。下面是我在Git存储库上为Git运行它时得到的结果(有很多片段被截断):
您可以看到,他们的Git为我的Git提供了一长串引用名和散列ID
我的Git可以从中选择它喜欢的名称和/或ID,然后进入下一阶段的Git fetch
:询问他们可以给我哪些哈希ID,例如提交e144d126d74f5d2702870ca9423743102eec6fcd
(他们的主机的哈希ID
)。如果我告诉Git将它们的master
或refs/heads/master
作为refspec的左侧,我的Git就会这样做,因为这些名称字符串与它们的refs/heads/master
匹配
(如果没有refspec,我的Git将请求所有分支。标记更为复杂:--tags
让我的Git获取所有,--no tags
让我的Git不获取任何分支,但在这两者之间,在Git fetch
中有一些可怕的扭曲代码)
在任何情况下,他们都会提供一些散列,my Git会说它是否需要或有其他散列,他们的Git使用他们的Git rev list
为提交、树、blob和/或带注释的标记对象构造一组散列ID,以放入所谓的瘦包中。在这个git fetch
阶段,您会看到关于远程计数和压缩对象的消息
git获取来源
现在让我运行一个实际的git fetch
:
$ git fetch origin
remote: Counting objects: 2146, done.
remote: Compressing objects: 100% (774/774), done.
remote: Total 2146 (delta 1850), reused 1649 (delta 1372)
最终,他们的Git完成打包所有要发送的对象,并发送这些对象。我的Git收到它们:
Receiving objects: 100% (2146/2146), 691.50 KiB | 3.88 MiB/s, done.
My Git修复了瘦包(Git index pack--fix thin
),使其成为一个可以在My.Git/objects/pack
目录下运行的正常包:
Resolving deltas: 100% (1850/1850), completed with 339 local objects.
最后,我们最感兴趣的部分是获取:
From [url]
ccdcbd54c..e144d126d master -> origin/master
1526ddbba..093e983b0 next -> origin/next
+ 8b97ca562...005c16f6a pu -> origin/pu (forced update)
7ae8ee0ce..7a516be37 todo -> origin/todo
->
箭头左侧的名称是它们的名称;右边的名字是我Git的名字。因为我只运行了git fetch origin
(没有refspec),所以我的git使用了默认的refspec:
$ git config --get remote.origin.fetch
+refs/heads/*:refs/remotes/origin/*
就好像我写了:
$ git fetch origin '+refs/heads/*:refs/remotes/origin/*'
它使用完全限定的refspec,而不是像branchB:branchC
这样的部分名称。此特定语法还使用glob模式,如*
字符。从技术上讲,这些都不是全局的,因为它们只是字符串而不是文件名,右边有一个*
,但原理类似:我要求我的Git匹配以refs/heads/
开头的每个名称,并将它们复制到我自己的存储库中,名称以refs/remotes/origin/
开头
refs/heads/
名称空间是我的Git的所有分支名称所在的位置。refs/remotes/
名称空间是my Git的所有远程跟踪名称所在的位置,refs/remotes/origin/
是my Git和我放置远程跟踪名称的位置,这些名称与我们在Git的origin
中找到的分支名称相对应。前面的前导加号+
设置强制标志,就好像我运行了git fetch--force
参考名称更新
下一步需要查看提交图,即Git存储库中所有提交的有向无环图或DAG。在本例中,由于新的包文件已经集成,这包括了我刚刚通过git fetch添加的所有新对象,因此我从它们的git中获得了新的提交(以及与之相关的任何树和blob)
每个对象都有一个唯一的哈希ID,但这些ID太难直接使用。我喜欢在StackOverflow上从左到右用文本绘制图形,并使用roundo
s或si
$ git fetch origin '+refs/heads/*:refs/remotes/origin/*'
...--o--o--A <-- master
\
o--B <-- develop
...--o--o--A <-- master, origin/master
\
o--B <-- develop, origin/develop
C
/
...--o--o--A <-- master
\
o--B <-- develop
\
D
C <-- origin/master
/
...--o--o--A <-- master
\
o--B <-- develop
\
D <-- origin/develop
aaaaaaa..ccccccc master -> origin/master
+ bbbbbbb...ddddddd develop -> origin/develop (forced update)
git merge <commit-specifier>
Step 1 : I am on branchA.
Step 2 : I do `git pull origin branchB:branchC` .
Step 3: I notice :
a) commits from branchB on remote comes and update `refs/heads/branchC`
b) Then based on `remote.origin.fetch` was used to try to update `remotes/origin/branchB` on our local.
[ Notice that no attempts will be made to update `remotes/origin/branchC`]
c) The `branchC` was merged into `branchA`.