【问题标题】:Why was "git push origin master" used when origin wasn't a remote name?当 origin 不是远程名称时,为什么使用“git push origin master”?
【发布时间】:2014-10-17 21:07:39
【问题描述】:

我在一个教程中看到有人使用git push origin master,但远程分支被命名为o/master。没有名为origin 的遥控器,只有o。这是他犯的错误还是origin有特殊含义?

This is the tutorial.(如果出现弹出窗口,请单击红色按钮)。输入 level,转到 Remote 选项卡,然后在第二行上,单击显示 push arguments 的第 4 个按钮。继续前进,直到您看到第一个视觉演示。

【问题讨论】:

  • 可能有助于包含指向所述教程的链接,这样我们就可以查看作者是否只是犯了一个错误,或者在谈论如何命名遥控器,或者在谈论fetch = 行git config,或者其他什么...
  • @torek Here you go(如果出现弹出窗口,请单击红色按钮)。输入level,转到Remote 选项卡,然后在第二行单击显示push arguments 的第四个按钮。继续前进,直到您看到第一个视觉演示。
  • 我在教程本身遇到了各种各样的问题,但是根据下面已经添加的答案,作者实际上是在设置他的fetch 行以使用“o/branch”代替“起源/分支”的。

标签: git


【解决方案1】:

这并不完全是对所提问题的回答。如果我使用评论中的链接访问该页面,并通过 NoScript 允许足够的东西让脚本运行等,它似乎从一个中等复杂的 git rebase 操作的演示开始,我很快迷失在高级项目中,而不是从某个开始。但是,如果重新构建问题,我们会得到......

远程和“远程分支”

“远程”只是一个名称,例如 originupstreambob,它作为“远程”部分进入 .gitconfig.git/config 文件:

[remote "bob"]
    url = git://name.in.some.domain/path/to/bob.git
    fetch = +refs/heads/*:refs/remotes/bob/*

远程名称是双引号中的内容。 url 行给出了 fetchpush 操作的 URL。 fetch = 行也很重要;我们几乎马上就会回来。

当您第一次运行git clone 时,clone 命令会设置一个“远程”部分。它使用的名称默认为origin,尽管您可以选择其他名称为-o--origin

远程分支(又名远程跟踪分支)

git 的所有引用(分支、标签和“远程分支”)实际上都是本地的,因为它们本地存储在您自己的.git 目录中。分支和标签是您可能已经熟悉的东西,“远程分支”看起来也很简单。但这里有一两个转折点。

当您第一次克隆某个​​存储库时,源存储库有一堆分支。好吧,它可能至少有一个分支。例如,某些存储库只有一个 master 分支。 (您甚至可以拥有一个完全没有分支的存储库;git clone 对此给出了警告,但允许这样做。)

为了讨论,我们假设原始源中有两个分支,称为masterdevelop。在任何情况下,git 都会复制所有远程的分支。但它并不简单地使它们成为普通的本地分支,即使它必须在本地存储它们。它使它们成为“远程分支”。

假设您没有使用-o 更改任何名称,因此您的克隆有一个名为origin 的远程。在这种情况下,git clone 将做的是将“远程分支”命名为 origin/masterorigin/develop

这些名字是怎么来的?这就是fetch = 行的用武之地。

参考和参考规范

fetch = 行的内容是一个“refspec”。 refspec 是第二简单的,只是一对用冒号分隔的 ref 名称,例如 master:masterdevelop:develop。但是等等,到底什么是引用名?

引用名称只是一个人类可读的名称,例如masterorigin/master;但每一种都有几种形式。有一个“全名”形式,通常以refs/ 开头,还有一个“短”形式,如master。简称master 只是写全名refs/heads/master 的一种方便方式。全名不仅包含名称本身,还包含该名称的“名称空间”,它真正告诉您它是什么种类refs/heads/ 命名空间是所有常规分支所在的位置。 refs/tags/ 命名空间包含你所有的标签,refs/remotes/ 包含你所有的远程跟踪分支。

这几乎就是它的全部内容:“本地”分支是refs/heads/ 中的一个引用名称,而“远程”分支是refs/remotes/ 中的一个。 远程分支的目的, git 本地存储在您自己的存储库中, 是跟踪“分支在远程的位置, 最后一次我 (git) 有机会查看远程和见。”

fetch = 行包含一个 refspec

同样,引用规范的简单形式就是ref:ref——一个左侧的引用名和一个右侧的引用名。 fetch = 行使用这个,但添加了几个星号和一个加号:+refs/heads/*:refs/remotes/origin/*

加号有一个相对简单的含义:它是“强制”标志。我们会回到这个。

星号也做了一件很明显的事情。我们上面说过,当我们克隆存储库时,原来有masterdevelop。这些是本地分支机构,实际上是refs/heads/masterrefs/heads/develop。星号匹配它可以匹配的所有内容,因此左侧refs/heads/* 最终匹配这两个分支。在右侧,星号的意思是“放入左侧匹配的任何内容”。

换句话说,这匹配refs/heads/master(左侧)并产生refs/remotes/origin/master(右侧)。它还匹配refs/heads/develop 并生成refs/remotes/origin/develop

前面的加号表示“强制”,或“强制更新”。这告诉 git 更新引用,即使更新不是“快进”(我只是方便地忽略这个答案中的快进 :-))。那么,这一切所做的就是让git fetch总是更新远程跟踪分支,无论何时运行git fetch。这维护了我们想要的属性: git 连接到远程存储库,与该服务器上的 gitty 对应方交谈,并找出所有分支的位置,所以现在它应该更新我们的“远程分支”副本。

(等一下,我们是如何从git clone 转到git fetch 的?嗯,clone 实际上只是第一次获取:它初始化一个存储库,添加它的第一个“远程”,然后执行fetch,全部作为一件事。所以它遵守与git fetch 相同的规则,这里。)

当您签出一个与远程跟踪分支同名的(新的、本地的)分支时,git 会使您的新本地分支在与远程跟踪分支相同的提交处开始,并设置本地分支以引用远程分支,以便git status 可以打印1 ahead, 3 behind 之类的内容。令人困惑的是,一些 git 文档喜欢说这个本地分支“跟踪”远程分支,而其他 git 文档将“远程分支”称为“远程跟踪分支”,并指出这个分支“跟踪”远程。 (真的,只要我们让本地 git 联系远程,它就会更新。)

起初,重命名的原因有点模糊。但是,一旦您使用了 git 一段时间,它就会变得很明显。假设您克隆了该存储库,然后自己在分支masterdevelop 或两者上进行一些提交。然后你做一些联系远程的事情,远程分支得到更新。如果 git 要更改您自己的(本地)master 和/或develop,这将失去您已经完成的工作。所以它只更新远程跟踪分支。

远程跟踪分支不必与远程名称匹配

在上面的示例中,我有[remote "bob"],然后是fetch = +refs/heads/*:refs/remotes/bob/*。但是fetch = 行并没有必须在其参考规范的右侧使用与远程相同的字符串。如果fetch 行读取+refs/heads/*:refs/remotes/robert/*,我所有远程bob 的远程分支最终都被命名为robert/<em>branch</em>

[这(显然)是这里原始难题的答案:无论出于何种原因(显然是窗口的宽度),教程作者已设置originfetch = 行以包含refs/remotes/o/*。但让我们用别的东西来结束这个更大的答案。]

git push 的这些参数到底是什么?

一个经典的推送命令示例是git push origin developgit push origin master。但是,如果您检查the git push documentation,您会看到这两个参数显示为“repository”和“refspec”。 origin 是一个存储库,当它是远程的名称时,master 在它没有冒号因此没有左右手边的情况下如何成为一个参考规范?

第一部分的答案是git push 随着时间的推移而演变。 remote-as-repository 参数 origin 可能在某些古老版本的 git 中是不允许的。相反,您必须编写实际的 URL,该 URL 现在存储在远程定义的 url = 行中。您今天仍然可以这样做,因此文档不能将该参数限制为只是一个“远程”名称。所以在git push origin 中,origin 部分是远程的名称,这是编写完整 URL 的一种简短方式。 (就此而言,您实际上可以在配置文件中列出一个单独的“pushurl”,以便获取到一个 URL 并推送到另一个。)

对于第二部分,好吧,当我说 refspec 是一对用冒号分隔的 ref 名称时,我还说这是 second 最简单的形式。最简单的形式只是一个引用名!以这种方式编写时,git push 将其视为中间带有冒号的重复引用名称。所以在这里,master 仅表示master:master。 (由于种种原因,git fetch handles no-colon refspecs differently。)

如果你终于“明白”了为什么 git 会重命名 git fetch 上的引用,那么这就引发了另一个问题。

我们为什么要推送master:masterdevelop:develop

使用fetch,我们更新了远程跟踪分支origin/master(或者可能是o/master)等等。为什么不更新服务器上的“推送跟踪分支”?例如,我可以将我的工作推送到refs/me/master

实际上,我们可以做到这一点;有些系统可以处理这类事情(某些类型的拉取请求和某些类型的自动化测试系统使用推送到refs/for/... 等)。但是 git “开箱即用”并没有这样做。我们只是直接推送到遥控器上的原始分支。

这意味着git push 的最后一个参数——git push origin master 中的master——将我们的 master 直接推向他们的 master。该推动必须是“快进”,否则它会被“拒绝”(或者我们必须使用强制标志,--force 或者-您应该从 fetch 行中识别出这一点-加号,@987654429 @)。

[这也导致了一些小缺陷——实际上是最近修复的——在 git 中,用于“安全收回”工作的情况,包括以人们可以处理的方式“倒回”已发布的分支。强制标志是全有或全无,但在某些情况下,很高兴能够告诉远程服务器:“我认为您的引用 &lt;ref&gt; 位于 &lt;raw commit ID&gt;;如果是这样,请将其更改为 &lt;new ID&gt;。 "本质上,这是对 ref-names 的比较和交换:它启用了一种原子更新形式,否则这是不可能的。但这是另一个话题,这个答案已经花了太长时间来写。 :-)]

【讨论】:

  • 哇!我将不得不停止我正在做的事情来阅读这篇文章!从我目前所读到的内容来看,你已经回答了我的问题,我现在明白了。谢谢。
【解决方案2】:

远程名称不必出现在 refname 中的任何位置。

.git/config 中完全有效的部分可能如下所示

[遥远的“起源”] 网址 = https://github.com/pcottle/learnGitBranching.git fetch = +refs/heads/*:refs/remotes/o/*

git log --decorate 中将远程分支显示为o/master 等,但仍使用origin 作为远程名称。

换句话说,您看到的o 不是远程名称。

【讨论】:

  • 他确实说过他正在使用o,因为origin 无法放入显示分支名称的框中。所以origino/master 中指的是o
  • @user2030677 不完全是。在git push/git pull 操作期间,origin 指的是配置文件中定义特定遥控器行为的部分。这反过来可能会引用本地引用refs/remotes/o/*,或者,根据具体的论点,可能不会。在git push origin master 中,refs/remotes/o/master 当前指向的是未使用的。 refs/heads/master用于更新origin,其中配置文件中origin的部分告诉Git它的URL,然后远程refs/heads/master匹配本地refs/remotes/o/master更新后者。
  • "git 将其视为重复的 ref 名称,中间有一个冒号" - 如果不是这种情况,请检查您的 git config push.default。过去这让我很伤心。
猜你喜欢
  • 1970-01-01
  • 2012-09-09
  • 2011-08-07
  • 2011-07-13
  • 2018-10-31
  • 2012-09-21
  • 1970-01-01
  • 2021-07-14
  • 2021-04-13
相关资源
最近更新 更多