1 问题背景
git远程仓库有很多命令,但是教程里面讲解的都是特别模糊的。
2 命令实例解析
2.1 git branch -vv -a
命令具体解析
在开始下面的命令解析之前,我们一定要学会git branch -vv -a
这个命令,这个是查询本地仓库+远程仓库+跟踪关系最全的命令了,它可以查询:
本地仓库使用git remote add
命令映射过的所有远程仓库的所有分支。
使用4大跟踪远程分支命令关联了所有远程仓库的所有远程跟踪分支的所有本地分支(4大跟踪远程分支命令在下面章节)。
图片解析:
有8个分支:一个前面有*星号且绿色的dev
分支,一个白色的master
分支,6个有remotes/
字符串开头且红色的远程分支。
2个本地分支中,注意中括号中的蓝色字符串[origin/dev]
和[origin/master]
,它们代表本地仓库的dev和master分支分别远程跟踪了远程仓库origin
的dev
和master
远程分支。
其中红色的remotes/origin/HEAD ->origin/master
远程分支比较特殊,origin/HEAD
就像一个指针,表示默认分支,下图中它指向origin/master
,代表着origin/master
是默认分支。
可以看到曾经用git remote add
命令映射的2个远程仓库remotes/liao
和remotes/origin
。remotes/liao
是廖雪峰老师的Git教程的远程仓库,remotes/origin
是用来测试的远程仓库。
2.2 git remote add 命令具体解析
2.2.1 命令+选项+参数-解析
命令git remote add <shortname> <url>
:将远程仓库唯一的URL 映射成为 在本地仓库中对远程仓库起的别名<shortname>
。(注意:git remote add <shortname> <url>
只负责映射!它不会产生下载或上传的流量!只有git clone,git fetch,git pull
等才产生下载或上传的流量!)
参数<shortname>
:在本地仓库中对远程仓库起的别名。而我们按照Git官方教程,一般会把参数<shortname>
设置为origin
。
为什么要强调在本地仓库中?因为我们要知道git remote add <shortname> <url>
是在我们自己的本地仓库对远程仓库起的别名,这个别名只能在我们自己的本地仓库使用,在真正的远程仓库那边,远程仓库的名字是一个绝对唯一的URL(比如:git@github.com:michaelliao/learngit.git)
,而不是origin。甚至我们的开发团队成员也可以自定义这个开发团队成员他个人的本地仓库中对远程仓库起的别名,比如官方教程2.5 Git 基础 - 远程仓库的使用中的参考命令git remote add pb https://github.com/paulboone/ticgit
,官方给参数<shortname>
设置为pb而不是origin了。
参数:远程仓库在互联网上唯一的URL。比如廖雪峰的Git仓库的SSH地址:git@github.com:michaelliao/learngit.git
。
2.2.2 命令实例解析git remote add origin git@github.com:michaelliao/learngit.git
在Git官方教程2.5 Git 基础 - 远程仓库的使用中,它对git remote add <shortname> <url>
的解释是:
运行 git remote add <shortname> <url>
添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写:
//Git官方教程2.5 Git 基础 - 远程仓库的示例
$ git remote origin
$ git remote add pb https://github.com/paulboone/ticgit
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)
pb https://github.com/paulboone/ticgit (fetch)
pb https://github.com/paulboone/ticgit (push)
在廖雪峰Git教程添加远程库中,把一个已有的本地仓库与远程仓库关联,也用到了以下命令:
$ git remote add origin git@github.com:michaelliao/learngit.git
我们值得注意的是,上面的2份教程对该命令的解释都是:git remote add 命令用于添加一个新的远程 Git 仓库或者把一个已有的本地仓库与远程仓库关联。其实这些都不准确。
git remote add <shortname> <url>
命令真实的用途是:将远程仓库唯一的URL 映射成为 在本地仓库中对远程仓库起的别名<shortname>
。这是因为 远程仓库唯一的URL 实在是太长了,比如git remote add origin git@github.com:michaelliao/learngit.git
命令中的git@github.com:michaelliao/learngit.git
,Git使用者每次使用涉及远程仓库的命令都要加这么长的名字作为参数实在太麻烦了,所以将远程仓库唯一的URL 映射成为 <shortname>
,这样使用涉及远程仓库的命令只需要写 本地仓库中对远程仓库起的别名.
2.3 git push <远程仓库名> <本地分支名>:<远程分支名>
命令具体解析
2.3.1 命令+选项+参数-解析
git push <远程仓库名> <本地分支名>:<远程分支名>
:将本地分支推送到远程仓库的远程分支。(注意:这里的远程仓库名依然是在本地仓库中对远程仓库起的别名)
<远程仓库名>
:在本地仓库中对远程仓库起的别名,如上面命令解析2(1)中设置的origin
。
<本地分支名>
:本地分支的名称,比如我们在项目开发,一般主分支(也是默认分支)叫做master
,一些新功能开发的分支叫做develop
或feature
。这些我们在我们自己电脑本地用git branch
创建的分支就是本地分支。
<远程分支名>
:在远程仓库的普通分支,比如远程仓库上的master
,自己在远程仓库创建的分支,以及自己推送到远程仓库上去的在远程仓库上的分支。
(注意:<远程分支名>与 <远程仓库名>
的情况不同:
(i)<远程分支名>
的取名由git push
中的远程分支名决定,一般Git使用者会省略<远程分支名>这个参数,所以Git会默认把<本地分支名>设置为<远程分支名>;
(ii)<本地分支名>
无论在远程仓库还是本地仓库就只有一个名字,不像<远程分支名>有一个绝对URL地址名字和一个在本地仓库中的别名。)
2.3.2 git push <远程仓库名> <本地分支名>:<远程分支名>
命令的多种常用写法
2.3.2.1 git push
在使用git push
命令之前,因为省略了远程仓库名+本地分支名+远程分支名
3个参数中必要填写的远程仓库名+本地分支名这2个参数,所以当前分支曾经必须被4大跟踪远程分支命令远程跟踪到远程仓库的远程分支。(虽然你可能会说Git可以用当前分支的名字自动填补本地分支名+远程分支名,这样会导致命令的作用太狭隘,所以Git不允许;另外Git再智能也不知道远程仓库名这个参数,Git又不会读心术来知道Git使用者想用哪个远程仓库,所以必须至少补上远程仓库名+本地分支名这2个参数)
-
如果当前分支曾经未被远程跟踪,那么GitBash会报错:
错误提示中的upstream branch
就是上游分支(别名:远程分支
),就是因为当前分支曾经未被远程跟踪,那么Git也不知道我们省略的远程仓库名+本地分支名+远程分支名
3个参数中必要填写远程仓库名+本地分支名这2个参数到底是什么呀!所以报错了。 -
如果当前分支曾经被远程跟踪,那么GitBash就会默认把参数本地分支名设置为当前分支,把参数远程仓库名+远程分支名设置为远程追踪的远程仓库+远程分支。这样这3个被省略的参数就被填补上了,所以不会报错。
这也解释了《Android群英传:神兵利器》中:为什么git push指令加了-u参数后,往后再push代码只需要git push或git push origin master就可以了。
git push
的意义:当Git使用者将当前分支远程跟踪到远程仓库的远程分支后,可以直接用git push
命令向远程仓库推送更新。这样,Git使用者再也不用每次输入此格式命令git push origin master
来向远程仓库推送更新,因为这个命令太长了导致Git使用者体验不佳。
2.3.2.2 git push <远程仓库名>
举例:git push origin
在使用git push <远程仓库名>
命令之前,因为省略了 本地分支名+远程分支名 2个参数中必要填写本地分支名这1个参数,所以当前分支曾经必须被4大跟踪远程分支命令远程跟踪到远程仓库的远程分支。(虽然你可能又会说Git可以用当前分支的名字自动填补本地分支名+远程分支名,但是Git认为这本地分支名+远程分支名两个参数如果直接默认当前分支的名字会让git push <远程仓库名>
命令变得狭隘化,Git认为git push <远程仓库名> 应该是可以在某一分支中随意指定要Push的任意分支,而不应该直接默认当前分支的名字自动填充缺省参数来给Git使用者偷懒,所以Git坚决规定:如果当前分支未被远程跟踪,那么本地分支名必填,远程分支名选填)
如果当前分支曾经未被远程跟踪,那么GitBash会报错:
错误提示中的upstream branch
就是上游分支(别名:远程分支),就是因为当前分支曾经未被远程跟踪,那么Git也不知道我们省略的 本地分支名+远程分支名 2个参数中必要填写的本地分支名这1个参数到底是什么呀!所以报错了。
如果当前分支曾经被远程跟踪,那么GitBash就会默认把参数本地分支名设置为当前分支,把参数远程分支名设置为远程追踪的远程分支。这样这3个被省略的参数就被填补上了,所以不会报错。
个人认为,与git push 类似。
分析:
git分支与远程主机存在对应分支,可能是单个可能是多个。
simple方式:如果当前分支只有一个追踪分支,那么git push origin到主机时,可以省略主机名。
matching方式:如果当前分支与多个主机存在追踪关系,那么git push --set-upstream origin master(省略形式为:git push -u origin master)将本地的master分支推送到origin主机(–set-upstream选项会指定一个默认主机),同时指定该主机为默认主机,后面使用可以不加任何参数使用git push。
matching方式,会推送所有有对应的远程分支的本地分支。
Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。
2.3.2.3 git push <远程仓库名> <本地分支名> 举例:git push origin master
git push <远程仓库名> <本地分支名>
是第一次Push分支时必须使用的命令(因为第一次Push分支必须指定<远程仓库名> <本地分支名>),它省略了远程分支名,但是根据Git官方教程介绍,如果Git使用者没有填写<远程分支名>,Git会用已经填写的<本地分支名>来填补缺省的<远程分支名>。
Git官方教程-3.5 Git 分支 - 远程分支是这么描述的:
如果希望和别人一起在名为 serverfix 的分支上工作,你可以像推送第一个分支那样推送它。 运行
git push (远程仓库名) (远程分支名):$ git push origin serverfix
这里有些工作被简化了。 Git 自动将serverfix
分支名字展开为 refs/heads/serverfix:refs/heads/serverfix
,那意味着,“推送本地的 serverfix
分支来更新远程仓库上的 serverfix 分支。” 我们将会详细学习 Git 内部原理 的 refs/heads/ 部分,但是现在可以先把它放在儿。 你也可以运行 git push origin serverfix:serverfix
,它会做同样的事 - 相当于“推送本地的 serverfix 分支,将其作为远程仓库的 serverfix 分支”。
注意:git push <远程仓库名> <本地分支名>
并未使用短选项-u或长选项–set-upstream,所以它并未使得当前分支远程跟踪了远程分支,所以往后再次Push当前分支时依然要使用git push <远程仓库名> <本地分支名>命令,不可以缩短为git push或者git push <远程仓库名>
。
2.3.2.4 git push -u <远程仓库名> <本地分支名>
举例:git push -u origin master
短选项-u用于指定git push命令中的<远程仓库名>
的<远程分支名>为<本地分支名>
所跟踪的上游分支。
相比于git push <远程仓库名> <本地分支名>
,因为git push -u <远程仓库名> <本地分支名>使用了短选项-u,所以它使得当前分支远程跟踪了远程分支。进而,往后再次Push当前分支时可以直接使用git push或者git push <远程仓库名>命令。
2.3.2.5 git push --set-upstream <远程仓库名> <本地分支名>
举例:git push --set-upstream origin master
相比于git push -u <远程仓库名> <本地分支名>
,git push --set-upstream <远程仓库名> <本地分支名>把短选项-u换成了长选项–set-upstream。因为–set-upstream长选项等价的短选项是-u,所以git push --set-upstream <远程仓库名> <本地分支名>与git push -u <远程仓库名> <本地分支名>是等价
的。
2.3.2.6 git push <远程仓库名> <本地分支名>:<远程分支名>
举例:git push origin serverfix:awesomebranch
一般善于偷懒的Git使用者会直接使用git push -u <远程仓库名> <本地分支名>命令来免去填写<远程分支名> 这个参数的麻烦。但是“免去填写<远程分支名> 这个参数的麻烦”只适用于本地分支名与被推送到的远程分支名相同时的情况。
根据Git官方教程-3.5 Git 分支 - 远程分支的描述:
在本地分支名是serverfix的情况下,如果并不想让远程仓库上的分支也叫做 serverfix,可以运行 git push origin serverfix:awesomebranch 来将本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支。这样就可推送本地分支到一个命名不相同的远程分支。
2.4 git clone [
]命令具体解析
2.4.1 命令+选项+参数-解析
-
命令
git clone <repo> [<dir>]
:复制远程仓库中HEAD指针指向的分支(默认是master分支,其他分支一律不复制)中的每一次提交记录和所有提交版本的文件,粘贴到本地文件夹当前GitBash所在路径/[<dir>]
,如果Git使用者未填写<dir>
,那么默认为远程仓库<repo>
的项目名,并自动默认远程仓库名称为origin和自动设置本地 master 分支远程跟踪远程仓库的 HEAD指针指向的分支(默认是master分支)。 -
参数
<repo>
:远程仓库在互联网上唯一的URL。比如廖雪峰的Git仓库的SSH地址:git@github.com:michaelliao/learngit.git
。 -
参数
[<dir>]
:粘贴下来的所有项目文件应该存放的路径(既可是绝对路径也可是相对路径),该路径的最后一个字符串代表的是项目根目录(也是:本地仓库名)。若Git使用者未填写<dir>
,那么默认为远程仓库<repo>
的项目名。
下图便是参数[<dir>]
写入绝对路径,hello为项目根目录(也是:本地仓库名)。
2.4.2 命令实例解析-git clone git@github.com:jedlee6/RemoteRepositoryTest.git
上图的git clone git@github.com:jedlee6/RemoteRepositoryTest.git really
命令就是克隆远程仓库git@github.com:jedlee6/RemoteRepositoryTest.git
到本地仓库really文件夹中。
同时使用git branch -vv -a
来查询查询本地仓库+远程仓库+跟踪关系,我们发现:
git clone <repo> [<dir>]
只复制远程仓库<repo>
中HEAD指针指向的分支,其他分支一律不复制。
虽然远程仓库有4大分支(其中较为特殊的remotes/origin/HEAD ->origin/master
远程分支不包括在内),但是克隆到本地仓库的只有远程仓库<repo>
中HEAD指针指向的master分支,这也证实了我们对命令git clone <repo> [<dir>]
的一段定义:“复制远程仓库中HEAD指针指向的分支(默认是master分支)中的每一次提交记录和所有提交版本的文件。”
git clone
命令会自动默认远程仓库名称为origin,设置本地 master 分支远程跟踪克隆的远程仓库中HEAD指针指向的master分支。
我们可以在上图片中,绿框圈住的蓝色字符串origin/master
看到本地 master 分支远程跟踪远程分支master。
3 其他注意事项
3.1 分支与远程分支与跟踪分支与远程跟踪分支的区别
有一点特别重要:分支不是某一条提交记录串起来的线,分支是一个指针!
[1]git的跟踪分支和远程跟踪分支学习笔记
[2]Git-远程分支,远程跟踪分支,跟踪分支的区别
3.2 四大跟踪远程分支命令
[3]git跟踪远程分支,查看本地分支追踪和远程分支的关系(简书这篇文章里面git branch -u a b命令的a b写反了)
[4]Git本地分支与远程分支的追踪关系
[5]git 设置分支跟踪关系
[6]git 本地分支追踪远程分支
一个本地分支只能远程跟踪一个远程分支,如果设置新的远程跟踪的远程分支,新的会代替旧的
。git remote add可以映射多个远程仓库。
在本地创建分支用跟踪远端非master分支,用git checkout -b newbranch origin/special
,该命令会以远端special分支的内容创建本地的newbranch分支,从而可以在远端special分支的基础上开发。
如果想要将本地分支与远程分支设置为不同名字,你可以轻松地将上面的newbranch设置为不同的名字:git checkout -b sf origin/serverfix。现在,本地分支 sf 会自动从 origin/serverfix 拉取。
上面的命令也可以简化为git checkout --track origin/special
该命令会在本地创建一个special分支用于跟踪远端的special分支并切换到本地special分支。
设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 --set-upstream-to 选项运行 git branch 来显式地设置,即git branch -u origin/serverfix。
3.3 为什么要跟踪远程分支?跟踪远程分支的作用与意义?
在跟踪分支里输入git push,Git 会自行推断应该向哪个服务器的哪个分支推送数据。反过来,在跟踪分支里运行git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来,同时Git 还能自动地识别去哪个服务器上抓取、合并到哪个分支。
3.4 --set-upstream选项与–set-upstream-to选项的区别
--set-upstream
选项用于指定git push
命令中的<远程仓库名>的<远程分支名>为<本地分支名>所跟踪的上游分支。举例:git push --set-upstream origin master
。并且--set-upstream
长选项等价的短选项是-u
,所以上面的举例可以写成git push -u origin master
,这两个命令是等价的。
--set-upstream-to
选项用于git branch
,举例:git branch --set-upstream-to origin/dev dev
。并且--set-upstream-to
长选项等价的短选项是-u
,所以上面的举例可以写成git branch -u origin/dev dev
,这两个命令是等价的。