【问题标题】:Is there a difference between "remotes/origin/dev" and "origin/dev" in your local repo?本地仓库中的“remotes/origin/dev”和“origin/dev”之间有区别吗?
【发布时间】:2019-01-15 04:19:14
【问题描述】:

本地仓库中的“remotes/origin/dev”和“origin/dev”有区别吗?

我似乎目睹了一些差异,但还没有向自己证明。

【问题讨论】:

  • origin 可能是远程引用;我不确定这两个版本是否不同。您发现了哪些差异?

标签: git git-branch git-remote


【解决方案1】:

通常,remotes/origin/dev 和 origin/dev 是表达同一事物的两种方式。但这并不一定!问题类似于如果您同时创建一个名为 X 的 branch 和一个名为 X 的 tag 会发生的情况。在这种情况下,简单地写X是模棱两可的,在不同的情况下,你可能不得不写不同的东西。

整个故事相当长,但有必要了解正在发生或可能发生的事情。

Git 引用有全名

所有这些名称——分支名称,如master,标签名称,如v1.2,以及远程跟踪名称,如origin/master——存在于计算机科学/信息学中,一个单独的名称空格 或namespace。如果有歧义的问题,我们所做的与我们在所有男人都被命名为布鲁斯而所有女人都被命名为希拉的派对上所做的一样:我们使用不同的和/或更长的名字。在编程语言中,我们倾向于称这些限定名称,它们看起来像std::map。在 Git 中,我们只是将它们称为 reference 名称,它们以 refs/ 开头,然后继续识别我们指的是哪个 name space。

Git 现有的标准化参考名称空间按字母顺序排列,在我撰写本文时为:1

  • refs/heads/: 分支名称
  • refs/namespaces/:为递归保留的空间(git upload-pack 和 git receive-pack 的特殊 hack,真的——这些不适合普通使用)
  • refs/notes/:被git notes使用
  • refs/remotes/:远程跟踪名称
  • refs/replace/:被git replace使用
  • refs/stash(没有斜杠,其中不能有名字):由git stash使用
  • refs/tags/:标签名称

这意味着如果您同时创建了refs/heads/X(一个名为X 的分支)和refs/tags/X(一个名为X 的标记),您可以明确地拼写出来 em> refs/heads/X 表示 branch X,refs/tags/X 表示 tag X。标准规则有令人讨厌的例外,但首先,让我们看看这些规则。


1名称空间随着时间的推移而增长。


通常的规则

一般来说,当 Git 要显示你一个参考时,它倾向于根据一些简单的规则来缩短它。而且,如果你使用一个不合格引用——我的意思是一个不以refs/开头的名字——Git有一个六步的过程来弄清楚你的意思。 the gitrevisions documentation 中描述了这个六步过程:

当有歧义时,refname> 通过采用以下规则中的第一个匹配来消除歧义:

  1. 如果 $GIT_DIR/ 存在,这就是你的意思(这通常只对HEAD、FETCH_HEAD、ORIG_HEAD、MERGE_HEAD 和 CHERRY_PICK_HEAD 有用);

  2. 否则,refs/如果存在;

  3. 否则,refs/tags/ 如果存在;

  4. 如果存在,refs/heads/;

  5. 否则,如果存在,refs/remotes/;

  6. 否则,refs/remotes//HEAD(如果存在)。

所以如果你写master,并且refs/heads/master 存在,你通常会得到名为master 的分支(从第4 步开始)。因此,如果 Git 要打印 refs/heads/master,它可能只打印 master。同样,如果你写origin/dev,并且refs/remotes/origin/dev 存在,你通常会得到它(从第5步开始)——所以如果Git 要打印refs/remotes/origin/dev,它可能只打印origin/dev。

有很多例外

如果你运行git branch -r,Git 会剥离refs/remotes/:

$ git branch -r
  origin/HEAD -> origin/master
  origin/maint
  origin/master
  origin/next
  origin/pu
  origin/todo

这告诉我们refs/remotes/origin/HEAD 存在于refs/remotes/ 中,依此类推。这些将在上面的第 5 步中匹配。

但如果你运行 git branch -a,Git 只会从远程跟踪名称中删除 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/ 和heads/。但是,以前删除了两个组件的远程跟踪名称现在只删除了一个:例如remotes/origin/master。这些仍然有效,事实上,它们会在第 2 步中更早地匹配。但为什么它们不一致?唯一的答案似乎是:这是传统的。

现在假设您不小心创建了一个名为master 的标签,即完全限定名称refs/tags/master。根据这六个步骤的列表,如果你写了名字master,Git 应该首先找到tag,因为那是第3 步。让我们看看它是否找到。首先,让我们看看 master 的哈希 ID 名称,然后选择一个不同的(早期提交)哈希 ID:

$ git rev-parse master
b7bd9486b055c3f967a870311e704e3bb0654e4f
$ git rev-parse master~3
18f2717578853edfdaed5fb7361b5f992a68a79e

现在让我们使用哈希 ID 18f2717578853edfdaed5fb7361b5f992a68a79e 创建 标签 master,这样第 3 步将找到这个 18f27... 事物,而不是第 4 步找到 b7bd9... 事物:

$ git tag master 18f2717578853edfdaed5fb7361b5f992a68a79e
$ git rev-parse master
warning: refname 'master' is ambiguous.
18f2717578853edfdaed5fb7361b5f992a68a79e

啊哈:我们得到了一个警告,Git 实际上确实找到了标签而不是分支。所以如果我们运行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

git checkout 命令首先尝试将名称作为 branch 名称,结果发现 commit b7bd9486b055c3f967a870311e704e3bb0654e4f!它仍然给了我们警告,但它使用了分支名称。如果我们想要标签名称,我们必须完整地拼出它,或者使用tags/master 来完成第 2 步。我自己更喜欢完整的拼写:

$ 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'.

Git 充满了像这样奇怪的极端情况,而了解每个命令的实际行为方式的唯一方法是进行实验(或避免模棱两可的情况)。

这对原始问题意味着什么

可以创建一个名为origin/dev 的分支(因此其全名是refs/heads/origin/dev)或一个全名为refs/remotes/remotes/origin/dev 的远程跟踪名称 , 例如。如果我们从这个姓氏中去除两个名称组件,我们会看到 remotes/origin/dev,它看起来就像我们从 git branch -a 中得到的那种名称,当它只去除 一个 组件时。如果您使用各种着色选项,则远程跟踪名称默认为红色,分支名称默认为绿色或黑色,因此其中一些会脱颖而出。但绝对有可能让自己陷入一些糟糕的境地。

要查看所有引用及其全名,请使用git for-each-ref。请注意,在具有许多标签或分支的存储库中,这会产生大量输出,因此我已经从 Git 存储库中剥离了 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

此处的名称(在第三列中)是完全限定的,因此您可以查看是否发生了任何奇怪的事情。您还可以仅检查名称空间的特定部分,并使用 --format 指令来限制输出:

$ git for-each-ref --format='%(refname)' refs/remotes/origin
refs/remotes/origin/HEAD
refs/remotes/origin/maint
refs/remotes/origin/master
refs/remotes/origin/next
refs/remotes/origin/pu
refs/remotes/origin/todo

如果你认为自己的处境很糟糕——尤其是当 Git 警告你名字不明确时——你可以使用 git for-each-ref 来分析你的实际情况,并用它来计划你的恢复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-25
    • 2021-03-07
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多