这并不完全是对所提问题的回答。如果我使用评论中的链接访问该页面,并通过 NoScript 允许足够的东西让脚本运行等,它似乎从一个中等复杂的 git rebase 操作的演示开始,我很快迷失在高级项目中,而不是从某个开始。但是,如果重新构建问题,我们会得到......
远程和“远程分支”
“远程”只是一个名称,例如 origin 或 upstream 或 bob,它作为“远程”部分进入 .gitconfig 或 .git/config 文件:
[remote "bob"]
url = git://name.in.some.domain/path/to/bob.git
fetch = +refs/heads/*:refs/remotes/bob/*
远程名称是双引号中的内容。 url 行给出了 fetch 和 push 操作的 URL。 fetch = 行也很重要;我们几乎马上就会回来。
当您第一次运行git clone 时,clone 命令会设置一个“远程”部分。它使用的名称默认为origin,尽管您可以选择其他名称为-o 或--origin。
远程分支(又名远程跟踪分支)
git 的所有引用(分支、标签和“远程分支”)实际上都是本地的,因为它们本地存储在您自己的.git 目录中。分支和标签是您可能已经熟悉的东西,“远程分支”看起来也很简单。但这里有一两个转折点。
当您第一次克隆某个存储库时,源存储库有一堆分支。好吧,它可能至少有一个分支。例如,某些存储库只有一个 master 分支。 (您甚至可以拥有一个完全没有分支的存储库;git clone 对此给出了警告,但允许这样做。)
为了讨论,我们假设原始源中有两个分支,称为master 和develop。在任何情况下,git 都会复制所有远程的分支。但它并不简单地使它们成为普通的本地分支,即使它必须在本地存储它们。它使它们成为“远程分支”。
假设您没有使用-o 更改任何名称,因此您的克隆有一个名为origin 的远程。在这种情况下,git clone 将做的是将“远程分支”命名为 origin/master 和 origin/develop。
这些名字是怎么来的?这就是fetch = 行的用武之地。
参考和参考规范
fetch = 行的内容是一个“refspec”。 refspec 是第二简单的,只是一对用冒号分隔的 ref 名称,例如 master:master 或 develop:develop。但是等等,到底什么是引用名?
引用名称只是一个人类可读的名称,例如master 或origin/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/*。
加号有一个相对简单的含义:它是“强制”标志。我们会回到这个。
星号也做了一件很明显的事情。我们上面说过,当我们克隆存储库时,原来有master 和develop。这些是本地分支机构,实际上是refs/heads/master 和refs/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 一段时间,它就会变得很明显。假设您克隆了该存储库,然后自己在分支master 或develop 或两者上进行一些提交。然后你做一些联系远程的事情,远程分支得到更新。如果 git 要更改您自己的(本地)master 和/或develop,这将失去您已经完成的工作。所以它只更新远程跟踪分支。
远程跟踪分支不必与远程名称匹配
在上面的示例中,我有[remote "bob"],然后是fetch = +refs/heads/*:refs/remotes/bob/*。但是fetch = 行并没有必须在其参考规范的右侧使用与远程相同的字符串。如果fetch 行读取+refs/heads/*:refs/remotes/robert/*,我所有远程bob 的远程分支最终都被命名为robert/<em>branch</em>。
[这(显然)是这里原始难题的答案:无论出于何种原因(显然是窗口的宽度),教程作者已设置origin 的fetch = 行以包含refs/remotes/o/*。但让我们用别的东西来结束这个更大的答案。]
git push 的这些参数到底是什么?
一个经典的推送命令示例是git push origin develop 或git 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:master 和develop: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 中,用于“安全收回”工作的情况,包括以人们可以处理的方式“倒回”已发布的分支。强制标志是全有或全无,但在某些情况下,很高兴能够告诉远程服务器:“我认为您的引用 <ref> 位于 <raw commit ID>;如果是这样,请将其更改为 <new ID>。 "本质上,这是对 ref-names 的比较和交换:它启用了一种原子更新形式,否则这是不可能的。但这是另一个话题,这个答案已经花了太长时间来写。 :-)]