内容简介:前情提要:这一节来介绍本地仓库与远程仓库的分支映射关系:
前言
前情提要: Git应用详解第六讲:Git协作与Git pull常见问题
这一节来介绍本地仓库与远程仓库的分支映射关系: git refspec
。彻底弄清楚本地仓库到底是如何与远程仓库进行联系的。
一、 Git refspec
refspec
是 Reference Specification
的缩写,字面意思就是 具体的引用 。它其实是 一种格式 , git
通过这种格式来表示 本地分支 与 远程分支 的映射关系;
在本地仓库创建 master
分支外的其他两个分支 develop
和 test
:
在 develop
分支上执行 git push
命令,出现如下错误:
这是由于本地分支 develop
没有与任何的远程分支建立联系导致的。通过 git branch -vv
查看本地与远程分支的关联情况,可见并没有建立任何联系:
二、本地远程分支
在讲解如何建立与本地分支关联的远程分支之前,首先我们来介绍期待已久的本地远程分支:
-
git
中其实有三种分支:本地分支、本地远程分支、远程分支; - 可以这样理解:本地远程分支是远程分支的一个镜像,并且在本地仓库与远程仓库之间起到一个桥梁的作用;
- 在没有办法直接查看远程仓库的时候,可以通过本地远程分支观察远程分支的变化情况。比如本地远程分支
origin/develop
就对应着远程分支develop
。
1.三分支关系
当本地 master
分支建立了与之关联的远程分支 master
后,查看当前分支状态:
图中的 origin/master
为本地远程分支,代表的是远程仓库的 master
分支,而这个分支是在本地的;也就是说加上远程仓库的 master
分支,一共有三个 master
分支:
并且,当本地仓库中的每一个分支都有与之关联的远程分支之后,本地仓库都会创建对应的 本地远程分支 ,它们所处的位置和关系如下图所示:
可以这样理解:本地远程分支 origin/master
为远程分支 master
的本地化形式;
假设远程仓库和本地仓库文件内容是一样的,都只有两次提交,此时三个分支的状态如下图所示:
然后,在本地的 master
分支中新增了提交 3rd
,本地仓库的分支情况变为:
上图中的 git dog
为指令: git log --all --decorate --oneline --graph
的别名,有关内容将在下一节讲解。
分支的示意图如下:
可见本地 master
分支比本地远程分支 origin/master
多了一次提交。这是因为本地远程分支是为了追踪远程分支而存在的,只有在执行 pull
或 push
操作时它的指向才会更新。比如在执行推送( push
)指令时:
-
首先,本地
master
分支对应的本地远程分支(origin/master
)会指向本地master
分支最新的提交(向前走了几步); -
然后,本地
master
分支再将文件推送到远程master
分支中。完成推送后,三分支的状态为:
回到终端,我们将刚才新增的提交 3rd
推送到远程分支,成功后查看本地分支以及本地远程分支的提交历史:
可见,本地远程分支的指向得到了更新,指向了最新的提交 3rd
,由此验证了上述说法。
查看分支关联
可以通过以下指令查看本地分支与本地远程分支的关联情况:
git branch -vv
可以看到:本地的 master
分支有本地远程分支 origin/master
关联,说明本地 master
分支已经和远程 master
分支建立了关联;
其余两个本地分支 pop
和 develop
并没有与之关联的本地远程分支,所以它们并没有与远程分支建立联系。
简单点说:只要 本地分支 有与之对应的 本地远程分支 ,就有与之对应的 远程分支 。
总结: origin/master
作用: 追踪远程分支 。当执行 git push/pull
操作时,该分支的指向都会相应地发生变化,用于与远程仓库保持同步;比如:本地仓库在执行 git push
操作的时候,不仅会把本地的修改推送到远程;还会同时修改 origin/master
分支的指向;
2.实战演示
可通过该指令查看本地的所有分支及其 最新的提交信息 :
git branch -av
首先,在 master
分支上进行三次提交,并将它推送到与之关联的远程 master
分支,此时各分支的提交历史为:
三个分支的状态为:
在此基础上,在 master
分支上进行一次提交 4th
,然后查看状态 git status
:
图中提示信息表明,当前分支( master
)已经领先于 origin/master
分支一次提交。为了看得更清楚,我们查看本地各分支的提交历史:
从图中可看出, origin/master
分支确实落后了一次提交,表示远程 master
分支落后了一次提交。此时可以使用 git push
将新增的提交推送到远程 master
分支,在这个过程中会将本地远程分支 origin/master
指向最新的提交 4th
。成功推送之后,再次查看本地各分支的提交历史:
可见,通过 git push
操作本地远程分支确实发生了更新,指向了最新提交 4th
。这就验证了执行 git push
时进行了两步操作:
- 将本地
master
分支的新提交推送到与之关联的远程master
分支; - 将本地远程分支
origin/master
指向本地master
分支的最新提交;
git pull
操作同理,也会更新本地远程分支的指向;
也就是说: 每次执行 push
或 pull
操作后,本地分支、本地远程分支、远程分支三个分支的指向都会达到同步。
当切换到 origin/master
分支上时,如下图所示:
git
并不会直接将分支切换到 origin/master
上,而是切换到最新的一次提交上,即一个游离的提交。这从侧面说明了: git
是 禁止 我们直接修改 origin/master
分支的,只允许我们切换到最新的提交上;
也就是说本地远程分支(如 origin/master
)是只读的,只能由 git
来改变,这就解释了为何使用 git branch
无法查看本地远程分支。
三、设置远程分支
弄清楚了什么是本地远程分支,就能更好地理解接下来所要介绍的,如何建立本地分支与远程分支的联系了。
1.设置同名远程分支
上图提示信息中的: upstream branch
表示上游分支,即远程仓库的分支。当前的本地分支 develop
并没有一个远程仓库的 develop
分支与之对应;要想推送 develop
分支到远程仓库的 同名分支 ,首先要创建对应的远程分支,有以下两种类型四种方法:
-
类型一:建立本地与远程分支追踪关系的。
git push --set-upstream origin <branch> git push -u origin <branch>
使用该类型方法,只需设置一次,之后就可以使用简写形式
git push
进行推送。 -
类型二:不建立本地与远程分支追踪关系的。
git push origin HEAD git push origin <branch_name>
使用该类型方法,每次推送都需要采用上述的完整写法。
下面就来详细介绍这四种方法:
git push --set-upstream origin <branch>
方法一:采用下述指令为本地仓库 mygit
的 develop
分支创建远程分支:
git push --set-upstream origin develop
该命令的作用为:在远程仓库创键一个与本地分支 develop
关联 的同名分支 develop
,并将本地分支 develop
的文件推送到该远程分支上。
也就是将本地分支 develop
的上游分支设置为远程仓库的 develop
分支,并进行文件同步。
执行完上述命令后会有这样的提示:
表示本地的 develop
分支已与远程的 develop
分支 建立联系 ;此时查看本地分支,会发现多了一个本地远程分支 origin/develop
,并且已与本地 develop
分支建立了联系:
随后再次执行 git push
就不会出现问题了:
此时在 github
上查看对应的远程仓库,就能查看到新增的远程分支 develop
了:
上图中的 master
分支是远程仓库创建时默认创建的,并没有与本地 master
分支建立联系。
随后点开 branch
可以看到:
当前一共有两个分支, master
分支是 default
(默认)分支,是不能够被删除的;活跃的分支为 deavelop
;
git push -u origin <branch>
方法二:先切换到 test
分支,再执行以下命令,为本地仓库 mygit
的 test
分支创建对应的远程分支:
git push -u origin test
-u
与 --set-upstream
作用是类似的,都是在远程仓库新建一个新的分支,并与本地分支建立联系。
执行完上述指令后,再次查看本地分支的详细情况,以及分支对应关系,可以发现 test
分支已与远程 test
分支建立联系:
git push origin HEAD
方法三:
如下图所示,通过该指令成功设置了本地 develop
分支对应的远程 develop
分支。但没有显示追踪信息,之后不能使用 git push
推送。
git push origin <branch>
方法四:
如下图所示,该方法实质上与方法三相同,因为 HEAD
指向的就是当前分支。同样没有显示追踪信息,之后也不能使用 git push
推送。
总结:当本地分支与远程分支同名时,一旦手动建立了它们之间的联系。之后推送本地分支的文件到对应的远程分支时可以采用简写形式: git push
。
这是因为在已经建立三个分支的对应关系并后,再执行 git push
, git
会自动地将同名的本地分支与远程分支进行匹配;
而其他情况则要采用完整写法进行推送。关于这些结论,将在第三大点 -u
参数的作用中详细介绍。
2.设置不同名远程分支
主要有以下四种方法,注意:使用每种方法前都需要先切换到对应分支上。
git push --set-upstream origin <branch1>:<branch2>
方法一:比如当前位于 develop
分支,如果采用的是以下简写命令:
git push --set-upstream origin develop
则会创建一个同名的远程分支 develop
。而如果采用该命令的完整写法,就可以自定义远程分支的名字了,比如设为 develop2
:
git push --set-upstream origin develop:develop2
执行上述指令后,成功创建了对应的,不同名的本地远程分支 origin/develop2
。表示本地 develop
分支已与远程 develop2
分支建立联系(因为远程分支与本地远程分支是一一对应的关系):
在 github
上查看本地仓库关联的远程仓库 MY
,可以看到顺利创建了 develop2
分支:
可以发现这么一个规律:在创建远程分支的同时会创建同名的本地远程分支。
git push -u origin <branch1>:<branch2>
方法二:
如下图所示,使用 -u
参数也能将本地 develop
分支的远程分支自定义为 develop2
。
git push origin HEAD:<branch>
方法三:
通过该方法也能成功设置与本地分支关联的,不同名的远程分支 develop2
:
git push origin <branch1>:<branch2>
方法四:该方法与方法二实质上是一样的,因为方法二中的 HEAD
指针指向的就是当前所在的分支,也就是 develop
分支。过程与方法二类似:
上面这四种设置不同名远程分支的方法,都有一个共同特点:不能使用git push进行推送。
若使用 git push
都会出现找不到对应远程分支的错误:
原因在下面第三点的 -u
参数作用中会详细讲解。
既然是 -u
参数追踪问题,那我加上 -u
参数不就行了么?其实这样也行不通:
解决方案:每次推送的时候,指明本地分支与远程分支的对应关系,即采用上述命令的完整写法,比如:
git push --set-upstream origin develop:develop2 git push -u origin develop:develop2 git push origin develop:develop2 git push origin HEAD:develop2
采用了完整写法后,成功地进行了推送,如下图所示:
注意:虽然可以自定义远程分支与本地远程分支的名字,但是十分不推荐,因为容易出错。所以,建议 本地远程分支和远程分支 都使用默认的,与本地分支相同的名字。
3.总结
以本地分支 develop
为例,不难发现:
-
使用下列简写命令时,远程分支和本地远程分支都会采用默认的,与本地分支相同的名字:
git push --set-upstream origin develop git push -u origin develop
-
而使用下列命令的完整写法时,就可以自定义远程分支与本地远程分支的名字:
git push --set-upstream origin develop:develop2 git push -u origin develop:develop2 git push origin develop:develop2 git push origin HEAD:develop2
四、 git push origin master
与 git push -u origin master
的区别
第一次将本地仓库的 master
分支推送到远程仓库的 master
分支上时,使用前者和后者都可以顺利推送,区别在于是否使用了 -u
参数:
-
推送时不使用
-u
参数: -
推送时使用
-u
参数:
注意到推送时使用 -u
参数会打印下列提示信息:
Branch 'master' set up to track remote branch 'master' from 'origin'.
表示本地的 master
分支被设置去追踪远程的 master
分支,在第 2~n
次推送中,只需要使用 git push
这样的简写命令(当然,完整写法效果等同)。 git
就会自动将本地的 master
分支与远程的 master
分支进行匹配,完成推送:
而不使用 -u
参数时,没有上述的分支追踪信息。此时使用简写 git push
进行推送会出现错误:
错误信息显示:当前分支没有与之对应的远程分支。这个时候想要成功推送,必须采用指明对应关系的完整写法,比如:
git push origin master
这就是推送时使不使用 -u
参数的区别。并且,根据上面的介绍,使用如下指令进行推送也能达到 -u
参数的效果:
git push --set-upstream origin develop
之后也可以使用简写的 git pull
指令进行推送:
细心的你一定发现了,以上都只是本地分支与远程分支同名的情况。不同名的情况下,上面的两个方法还好使吗?
首先验证方法一: -u
参数:
设置不同名的远程分支时要注意写成完整形式: pop:pop2
可以看到,即使创建不同名的远程分支, -u
参数也一样能够设置追踪关系;但是,奇怪的是 git push
却不好使了:
还是和没使用 -u
参数时一样,找不到对应的远程分支,需要采用指明对应关系的完整写法,比如:
git push origin pop:pop2
其次验证方法二: --set-upstream
:
同样设置分支对应关系时要使用完整写法。可以看到,该方法也设置了追踪关系。奇怪的是 git push
同样不管用:
同样找不到对应的远程分支,需要采用指明对应关系的完整写法,比如:
git push origin bob:bob2
所以可以得出结论:
- 本地/远程分支同名时:
-
-u
参数的作用是设置本地分支与远程分支的追踪关系,设置了追踪关系后,之后的推送可使用简写git push
,git
内部会自动进行匹配; -
--set-upstream
参数与-u
参数效果等同;
-
- 本地/远程分支不同名时:
-
--set-upstream
参数与-u
参数依然可以设置分支的追踪关系,但是,之后的推送不能使用简写git push
,只能使用指定分支对应关系的完整写法;
-
总结:十分建议将所有的本地分支与对应的远程分支设为同名,并且第一次推送使用 --set-upstream
或 -u
参数建立分支追踪关系,之后就可以使用简写 git push
进行推送了!
五、 git push -f
该命令的完整写法为:
git push -f origin master
意思为 强制推送 :直接跳过与远程仓库的 master
分支合并的环节,强制覆盖远程仓库上 master
分支的内容,即以本地的 master
分支内容为准。应慎用该命令,否则将覆盖远程仓库中 master
分支上其他人推送的文件(一星期的成果没了)。
1.应用场合
- 当远程仓库的历史提交记录太乱了,想要重新整理时。 注意: 一定要与其他人协商好再用本地分支强制覆盖远程分支。
- 只有一个人开发时,代码以本地为准。为了避免推送时繁琐的合并,可以使用
-f
强制推送,直接覆盖远程分支上的内容;
分两种写法:
-
第一种:已经通过
-u
参数等方式,设置了本地分支与远程分支的追踪关系时,采用:git push -f
-
第二种:还未设置追踪关系,采用:
git push -u origin master -f
2.预防措施
Github
提供了相应的分支保护机制,可以在 Settings
选项中进行设置:
可以看到 Github
默认是保护分支的:
3.补救措施
让有进度的人,再次对被强制覆盖的远程分支执行一次 git push -f
指令,把正确的内容强制推送上去,覆盖前一次 git push -f
所造成的灾难。
六、设置远程分支对应的本地分支
假如远程仓库 M3Y
中有 master
和 develop
两个分支,此时新建一个空的本地仓库 mygit
,通过以下指令将它的远程仓库地址 origin
设置为 M3Y
的地址:
git remote add origin git@github.com:AhuntSun/M3Y.git
此时两仓库的状态为:
由于 mygit
是空仓库与远程仓库 M3Y
没有任何公共提交历史,所以在执行 git pull
时会出现下图所示的不同源冲突(上一节中详细介绍过该冲突):
虽然 git pull
操作失败了,但是也成功地将远程仓库 M3Y
的分支拉取了下来。但是,通过 git branch -vv
查看分支追踪关系,发现并没有本地分支与这两个远程分支建立了联系:
如何建立这两个本地远程分支对应的本地分支?可以通过以下两种方法:
1. git checkout -b <branch> origin/<branch>
比如可以通过以下命令,设置本地远程分支 origin/master
与本地 master
分支的追踪关系:
git checkout -b master origin/master
以上为本地 master
分支已存在的情况,如果本地分支 develop
未创建,可以采用下述命令创建并切换到 develop
分支,并且设置 origin/develop
与 develop
的追踪关系:
git checkout -b develop origin/develop
设置了本地分支与远程分支的追踪关系,接下来就可以在本地仓库执行 git push
进行推送了:
2. git checkout --track origin/<branch>
重置条件,新创建一个空的本地仓库 mygit2
,同样将其远程地址 origin
设置为远程仓库 M3Y
的地址。随后在本地仓库 mygit2
中执行 git pull
操作,将远程仓库 M3Y
中的两个分支拉取到本地:
与上次一样,拉取到本地的两个本地远程分支没有与任何本地分支建立追踪关系。这次可以采用另外一种方法:
git checkout --track origin/test
创建并切换到 develop
分支,并且设置该分支与 origin/develop
分支的追踪关系:
可以说该方法是方法一的特殊情况,因为该方法没有指明创建的本地分支的名字,所以默认采用与远程分支一样的名字 develop
来命名;
如果想在本地建立一个 develop2
(不同名)的分支与本地远程分支 origin/test
建立追踪关系,则应采用第一种方法。
七、远程分支信息
可以进入 .git
目录,查看储存远程分支信息的文件:
1.查看 config
文件
使用 vim
编辑器打开该文件,可以查看到关于远程分支的信息:
可以看到 remote
这一栏中有两个信息,第一个是远程仓库的 url
,第二个是 fetch
信息,这两个信息尤为重要:
-
refs/heads/*
表示远程仓库的refs/heads
目录下的所有引用都会写入到本地的refs/remotes/origin
目录中; - 其中的
+
号是可选的,加了表示无论是否能够自动合并,即是否为Fast Forward
方式,都将远程仓库所有文件拉取到本地。 - 而不加
+
则表示如果不是Fast Forward
方式就不拉取。一般情况下都是加上+
号的,先把文件拉取到本地,不是Fast Forward
方式就手动合并;
2.查看 refs
文件
refs
文件夹存储着 refspec
的文件,里面维护着三个目录:
-
第一个目录
heads
:存储的是本地仓库的分支信息:可以查看其中一个分支:
是一个
SHA1
值,表示分支就是一个指针,指向当前提交。 -
第二个目录
remotes
:里面存放着远程分支信息,远程仓库中也存在这样的目录与文件;从上图可以看到,远程分支只有
master
,没有develop
(因为之前被删除了)。并且它们本质上也是一个代表提交的SHA1
值:建立
refspec
映射(即本地分支、本地远程分支、远程分支三者间对应关系)后,git
会获取远端上refs/heads
下的所有引用,并将它们写入本地的refs/remotes/origin
目录下。所以,可以通过查看本地远程分支(如origin/master
)的方式查看本地仓库最后一次访问远程仓库时,远程仓库master
分支上的历史提交记录://完整写法 git log refs/remotes/origin/master //进一步简写 git log remotes/origin/master //继续简写 git log origin/master
上述两种省略的写法最终都会转换为完整的写法:
-
第三个目录
tags
:存放标签信息,也是一个SHA1
值:详细内容将在下一节介绍。
八、删除远程分支
如下图所示,远程仓库有三个分支 master
、 develop
和 test
:
通过前面的学习,我们知道通过下述指令可以删除本地 develop
分支:
git branch -d develop
那么如何删除远程分支呢?
首先我们来看看 git push
的完整写法:
git push origin srcBranch:destBranch
-
srcBranch
表示本地的分支,destBranch
表示对应的远程分支; -
表示将本地的分支推送到远程分支,这两个分支可以不同;
-
之所以可以直接使用
git push
是因为我们设置的本地分支和远程分支的名字是 相同的 ,并且手动建立了联系,所以git
能够自动识别;
明白了这点后,就不难理解下列删除远程分支的两种做法了:
1. git push origin :destBranch
将空的分支推送到远程分支,这样就能将该远程分支删除;比如删除本地分支 develop
的远程分支:
git push origin :develop
可以看到成功删除了远程分支 develop
以及它所对应的本地远程分支 origin/develop
。
注意:并不需要切换到需要删除远程分支的本地分支 develop
上,再执行上述指令。也就是说,可以在任意本地分支上删除任意本地分支对应的远程分支。
2. git push origin --delete destBranch
还可以采用更加直观的 --delete
参数,比如删除远程分支 develop
:
git push origin --delete develop
这两种方式是等价的,可根据需求选择。
3. git remote prune origin
该方法用于删除无效的 远程分支 对应的 本地远程分支 ,具体场合如下:
如图所示 mygit
与 mygit2
共享一个有三个分支的远程仓库:
首先在 mygit2
中删除远程仓库的 develop
分支,可以看到 mygit2
中远程分支 develop
对应的本地远程分支 origin/develop
被删除了:
然后在 mygit
中查看远程分支详细信息:
可以看到提示信息中显示远程分支 develop
对应的本地远程分支 origin/develop
处于 stale
(腐烂,游离)状态,即该分支对于 mygit
来说已经失效,可以使用:
git remote prune origin
( prune
:裁剪)删除 mygit
上这个无效的本地远程分支:
再次查看分支信息,可发现 mygit
中的本地远程分支 origin/develop
已经被删除了:
注意:一般本地远程分支设置了保护措施,不能随意删除;
九、重命名分支
1.本地分支
可以通过以下命令,将本地分支 dev
重命名为 develop
:
git branch -m dev develop
2.远程分支
无法直接重命名远程分支,只能通过先删除原来的远程分支,再创建重命名后 develop
分支对应的远程分支,过程为:
//删除远程分支dev git push origin :dev //创建重命名后develop分支对应的远程分支 git push -u origin develop
由此间接地完成了远程分支的重命名。
以上就是本节的全部内容,相信看到这里的你已经十分熟悉 git refspec
了。下一节将介绍 git
标签与别名。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Usability for the Web
Tom Brinck、Darren Gergle、Scott D. Wood / Morgan Kaufmann / 2001-10-15 / USD 65.95
Every stage in the design of a new web site is an opportunity to meet or miss deadlines and budgetary goals. Every stage is an opportunity to boost or undercut the site's usability. Thi......一起来看看 《Usability for the Web》 这本书的介绍吧!
随机密码生成器
多种字符组合密码
RGB HSV 转换
RGB HSV 互转工具