本地存储区中的“ remotes / origin / dev”和“ origin / dev”之间有区别吗?
我似乎已经目睹了一些差异,但是还没有向我自己证明。
答案 0 :(得分:2)
通常,remotes/origin/dev
和origin/dev
是表达同一事物的两种方式。但这不一定是事实!该问题类似于如果您同时创建名为X
的分支和名为X
的 tag 会发生什么。在这种情况下,简单地写X
是模棱两可的,并且在不同的情况下,您可能不得不写一些不同的东西。
整个故事很长,但是必须了解正在发生或可能发生的事情。
所有这些名称(如master
之类的分支名称,v1.2
之类的标签名称和origin/master
之类的远程跟踪名称)都存在于计算机科学/信息学中,单独的名称空间或namespace。如果存在歧义问题,我们在派对上所做的就是我们在同一场派对上所做的相同的事情,所有男人都被命名为布鲁斯,所有女人都被命名为希拉:我们使用不同的名称和/或更长的名称。在编程语言中,我们倾向于称这些限定名称,它们看起来像std::map
。在Git中,我们仅将它们称为 reference 名称,它们以refs/
开头,然后继续确定我们指的是哪个名称空间。
Git现有的标准化参考名称空间是按字母顺序排列的,在我写这篇文章时,它是: 1
refs/heads/
:分支名称refs/namespaces/
:一个保留的递归空间(git upload-pack
和git receive-pack
的特殊技巧,实际上-这些不是普通用途)refs/notes/
:由git notes
refs/remotes/
:远程跟踪名称refs/replace/
:由git replace
refs/stash
(不带斜杠,其中不能包含名称):由git stash
refs/tags/
:标记名称这意味着,如果您同时创建了refs/heads/X
(这是一个名为X
的分支)和refs/tags/X
(这是一个名为X
的标记),则可以明确地拼出 refs/heads/X
表示分支X ,而refs/tags/X
则表示标签X 。标准规则有烦人的例外,但首先让我们看一下这些规则。
1 名称空间随时间增长。
通常,当Git要向您显示参考时,它会根据一些简单的规则将其缩短。而且,如果您使用一个不合格的引用(我的意思是名称不是以refs/
开头),Git会通过六个步骤来弄清楚您的身份的意思。 the gitrevisions documentation中描述了此六步过程:
如果模棱两可,则采用以下规则中的第一个匹配项来消除<< em> refname >的歧义:
如果存在 $ GIT_DIR /
,这就是您的意思(通常仅对 HEAD
,FETCH_HEAD
,{{1} },ORIG_HEAD
和MERGE_HEAD
);否则,使用 refs /
(如果存在) 否则,如果存在 refs / tags /
; 否则,如果存在 refs / heads /
; 否则, refs / remotes /
(如果存在) 否则, refs / remotes /
/ HEAD (如果存在)。
因此,如果您写CHERRY_PICK_HEAD
并且存在master
,通常会得到名为refs/heads/master
的分支(从第4步开始)。因此,如果Git要打印master
,则可能只打印refs/heads/master
。类似地,如果您写master
并且origin/dev
存在,那么通常会得到(从第5步开始),因此,如果Git要打印refs/remotes/origin/dev
,它可能只打印{{1 }}。
如果您运行refs/remotes/origin/dev
,Git将剥离origin/dev
:
git branch -r
这告诉我们refs/remotes/
中存在$ git branch -r
origin/HEAD -> origin/master
origin/maint
origin/master
origin/next
origin/pu
origin/todo
,依此类推。这些将在上面的步骤5中匹配。
但是如果您运行refs/remotes/origin/HEAD
,Git只会从远程跟踪名称中剥离refs/remotes/
:
git branch -a
当前分支refs/
(实际上是$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/maint
remotes/origin/master
remotes/origin/next
remotes/origin/pu
remotes/origin/todo
)已剥离了两个部分:master
和refs/heads/master
。但是,以前删除了两个部分的远程跟踪名称,现在只删除了一个:例如refs/
。这些仍将起作用,实际上,它们将在步骤2中更早地匹配。但是为什么它们不一致?唯一的答案似乎是:这是传统的。
现在假设您不小心创建了一个名为heads/
的 tag ,即完全限定的名称remotes/origin/master
。根据六个步骤的清单,如果您输入名称master
,Git应该首先找到 tag ,因为这是步骤3。让我们来看看是否可以。首先,让我们看一下哈希ID refs/tags/master
的名称,然后选择一个不同的(早期提交)哈希ID:
master
现在,让我们创建具有哈希ID master
的 tag $ git rev-parse master
b7bd9486b055c3f967a870311e704e3bb0654e4f
$ git rev-parse master~3
18f2717578853edfdaed5fb7361b5f992a68a79e
,以便第3步将找到此master
而不是第4步找到{{ 1}}的事情:
18f2717578853edfdaed5fb7361b5f992a68a79e
啊哈:我们得到一个警告,而Git实际上确实找到了标签而不是分支。因此,如果我们运行18f27...
,我们将检出 tag ,对吗? 错!
b7bd9...
$ git tag master 18f2717578853edfdaed5fb7361b5f992a68a79e
$ git rev-parse master
warning: refname 'master' is ambiguous.
18f2717578853edfdaed5fb7361b5f992a68a79e
命令首先尝试将该名称作为分支名称,然后发现提交git checkout master
!它仍然给我们警告,但使用了分支名称。如果我们想要标签名称,则必须完全将其拼写出来,或者使用$ git checkout master
warning: refname 'master' is ambiguous.
Already on 'master'
Your branch is up-to-date with 'origin/master'.
$ git rev-parse HEAD
b7bd9486b055c3f967a870311e704e3bb0654e4f
使其通过步骤2。我更喜欢自己使用完整的拼写:
git checkout
删除多余的b7bd9486b055c3f967a870311e704e3bb0654e4f
并恢复理智是一个好主意:
tags/master
Git充满了这种奇怪的极端情况,而找出每个命令的实际行为的唯一方法是进行实验(或避免模棱两可的情况)。
可以创建一个名为$ git checkout refs/tags/master
Note: checking out 'refs/tags/master'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 18f2717578... Merge branch 'ms/core-icase-doc'
的分支-因此其全名是master
-或一个其全名的远程跟踪名称例如为$ git tag -d master
Deleted tag 'master' (was 18f2717578)
$ git checkout master
Previous HEAD position was 18f2717578... Merge branch 'ms/core-icase-doc'
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
。如果我们从姓氏中剥离两个名称成分,我们将看到origin/dev
,它看起来像是从refs/heads/origin/dev
中获得的名称种类,当它仅剥离一个成分时。如果您使用各种颜色选项,则远程跟踪名称默认情况下为红色,而分支名称默认情况下为绿色或黑色,因此其中一些会突出。但是绝对有可能使自己陷入困境。
要查看所有具有全名的引用,请使用refs/remotes/remotes/origin/dev
。请注意,在具有许多标签或分支的存储库中,这会产生大量输出,因此我从Git存储库中剥离了Git的输出:
remotes/origin/dev
此处的名称(在第三列中)是完全限定的,因此您可以查看是否有任何奇怪的情况。您还可以仅检查名称空间的特定部分,并使用git branch -a
指令来限制输出:
git for-each-ref
如果您认为自己处境不好,特别是如果Git向您警告名称不明确的情况,则可以使用b7bd9486b055c3f967a870311e704e3bb0654e4f commit refs/heads/master
b7bd9486b055c3f967a870311e704e3bb0654e4f commit refs/remotes/origin/HEAD
53f9a3e157dbbc901a02ac2c73346d375e24978c commit refs/remotes/origin/maint
b7bd9486b055c3f967a870311e704e3bb0654e4f commit refs/remotes/origin/master
5c9ce644c390ec4ef3ba4adc94e7f4af17ade36b commit refs/remotes/origin/next
1aaaa8cf15ba4eb62d485c5c8b64d6a75b9e7c3f commit refs/remotes/origin/pu
f59de5ad04b18866024fb298ddb276cb51d91673 commit refs/remotes/origin/todo
d5aef6e4d58cfe1549adef5b436f3ace984e8c86 tag refs/tags/gitgui-0.10.0
33682a5e98adfd8ba4ce0e21363c443bd273eb77 tag refs/tags/gitgui-0.10.1
ca9b793bda20c7d011c96895e9407fac2df9648b tag refs/tags/gitgui-0.10.2
[mass snippage]
f883596e997fe5bcbc5e89bee01b869721326109 tag refs/tags/v2.9.3
8d091e9ed473c372a5b89d1258d1c3ad01daa04c tag refs/tags/v2.9.4
dcba104ffdcf2f27bc5058d8321e7a6c2fe8f27e tag refs/tags/v2.9.5
分析实际情况,并以此来计划恢复情况。