当原点不是远程名称时,为什么使用“git push origin master”?

时间:2014-08-24 16:10:59

标签: git

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

This is the tutorial.(如果弹出窗口,请单击红色按钮)。键入级别,转到“远程”选项卡,然后在第二行,单击第4个按钮,显示推送参数。继续进行直到你进行第一次视觉演示。

2 个答案:

答案 0 :(得分:4)

这不是问题的答案。如果我使用评论中的链接转到页面,并通过NoScript允许足够的东西让脚本运行等,它似乎从一个中等复杂的git rebase操作的演示开始,我&# 39; m在高级项目中迅速丢失,而不是从一开始就开始。但是,如果问题被重新定义,我们就会得到......

遥控器和"远程分支"

A"遥控"只是一个名称,如originupstreambob,它作为"远程"进入.gitconfig.git/config文件。部分:

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

远程名称是双引号中的东西。 url行提供了fetchpush操作的网址。 fetch =行也非常重要;我们几乎马上回来。

当您第一次运行git clone时,clone命令会设置一个"远程"部分。默认情况下,它使用的名称origin,但您可以选择-o--origin的其他名称。

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

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

当您第一次克隆某个​​存储库时,源存储库有一堆分支。好吧,它至少有一个分支。例如,某些存储库只有master分支。 (你甚至可以拥有一个根本没有分支的存储库; git clone会给出一个警告,但允许它。)

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

我们也说您没有使用-o更改任何名称,因此您的克隆有一个名为origin的遥控器。在这种情况下,git clone将要做的是命名"远程分支" origin/masterorigin/develop

它是如何获得这些名字的?这就是fetch =行所在的位置。

refs和refspecs

fetch =行的内容是" refspec"。 refspec是第二最简单的,只是一对用冒号分隔的引号,例如master:masterdevelop:develop。但是,请问,首先究竟什么是ref-name?

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

这几乎就是它的全部内容: A" local" branch是refs/heads/中的引用名称,以及" remote" branch是refs/remotes/中的一个。远程分支的目的,git在本地存储在您自己的存储库中,是为了跟踪"分支在哪里在遥控器上,我最后一次(git)有机会看到遥控器并看到。"

fetch =行包含refspec

同样,refspec的一个简单形式只是ref:ref - 一个左侧的refname和一个右侧的refname。 fetch =行使用此行,但添加了几个星号和一个加号:+refs/heads/*:refs/remotes/origin/*

加号有一个相对简单的含义:它是" force"旗。我们会回到这一点。

星号也很明显。我们上面说过,当我们克隆存储库时,原始文件有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时,make git fetch 始终更新远程跟踪分支。这维护了我们想要的属性:git连接到远程存储库,与该服务器上的gitty对应方交谈,并找到所有分支的位置,所以现在它应该更新我们的远程分支"副本。

(等一下,我们是如何从git clone转到git fetch的?好吧,clone实际上只是第一次获取:它初始化了一个存储库,添加了它的第一个& #34;远程",并进行提取,所有这些都是一回事。所以它遵循与git fetch相同的规则,在这里。)

当您签出与远程跟踪分支同名的(新的,本地)分支时,git会使您的新本地分支在与远程跟踪分支相同的提交时启动,并设置本地分支以引用远程分支,以便git status可以打印1 ahead, 3 behind之类的内容。令人困惑的是,一些git文档喜欢说这个本地分支"跟踪"远程分支,而其他git文档调用"远程分支" a"远程跟踪分支"并注意到这个分支"跟踪"遥控器。 (实际上,每当我们将本地git发送到联系遥控器时,它就会更新。)

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

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

在上面的示例中,我有[remote "bob"],然后是fetch = +refs/heads/*:refs/remotes/bob/*。但是fetch =行不会拥有在其refspec的右侧使用与远程相同的字符串。如果fetch行显示为+refs/heads/*:refs/remotes/robert/*,则远程bob的所有远程分支最终都会被命名为robert/branch

[那个(显然)是原始难题的答案:无论出于何种原因(显然,窗口的宽度),教程作者已设置origin' s fetch =包含refs/remotes/o/*的行。但是,让我们用其他东西来完成这个更大的答案。]

git push的这些参数是什么?

经典示例推送命令为git push origin developgit push origin master。但是,如果你检查the git push documentation,你会发现这两个参数显示为" repository"和" refspec"。 origin如何成为存储库,当它是遥控器的名称时,master如何在没有冒号且因此没有左右两侧时成为refspec?

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

对于第二部分,好吧,当我说refspec是一对用冒号分隔的ref-names时,我还说这是第二个 - 最简单的形式。最简单的形式只是一个引用名称!以这种方式编写时,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的最后一个参数 - master中的git push origin master - 将我们的 master直接推送到他们的< / em> master。这种推动需要是一个快速前进的&#34;否则它会被拒绝&#34; (或者我们必须使用强制标记,--force或 - 您应该从fetch行 - 加号git push origin +master:master)识别出来。

[这也导致一些轻微的缺陷 - 最近修复,实际上是git,对于&#34;安全撤回&#34;工作,包括&#34;倒带&#34;以人们可以处理的方式发布分支机构。强制标志是全有或全无,但在某些情况下,能够告诉远程服务器很好:&#34;我认为您的参考<ref>位于<raw commit ID> ;如果是,请将其更改为<new ID>。&#34;从本质上讲,这是ref-names的比较和交换:它实现了原子更新的形式,否则是不可能的。但这是另一个话题,这个答案已经花了太长时间才能写出来。 :-)]

答案 1 :(得分:2)

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

.git/config中完全有效的部分可能看起来像

[remote "origin"]
    url = https://github.com/pcottle/learnGitBranching.git
    fetch = +refs/heads/*:refs/remotes/o/*

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

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