Git原理杂谈之pull/push命令

这边文章是基于之前三篇文章的后续篇,之前三篇能够帮助小白同学概要性地理解Git的整体原理及快速掌握Git基本技能以进入团队开发,所以建议对Git只了解些浅层内容的看客老爷们先看完前三篇再从本篇开始往后看。因为从本篇开始,小编将会挑一些细节内容给大家进行解析,这些细节内容是建立在读者已经掌握了整体原理的基础上的。

好了废话不多说了,直接进入今天的主题——git push/ git pull命令。

也许很多人会轻视这两个最常见且高频使用的命令,这有什么可讲的?没错,如果项目没有出问题的情况下,我们大可不必关心这两个命令的原理及涉及到的所有细节,直接使它们喽~但是一旦项目出了什么小问题,使项目管理稍微脱离了“正常”流程,如果不了解这两个命令的话,大部分都容易傻眼。

续之前几篇的风格,小编先从偏表层讲清楚一些原理,等消化后再进一步讲解内部的细节原理。(PS:了解了各层次的原理后才能遇到问题都能理智地分析并解决掉问题)

Upstream:这个概念虽然不官方,但也非纯粹小编自造,希望大家首先了解一下。心存感激,如果当我们上来就能够顺利使用git push(git pull同理,后文尽量只拿一个来讲)命令就能实现推送数据到服务器上的时候,我们是幸运的。因为有幸在一些底层的未知设置下我们才能够简单地使用git push命令。其实,如果稍微写全面一点的话,很有可能我们的命令是像这样的:git push origin master。origin是远程端代名词(前几篇有讲解),master是这个远程端的一个分支名【有问题,应是本地分支名,详见git push,并且连同下边红色标记部分也有问题】。所以这句命令的意思可以理解成“推送我的数据到origin端的master分支上去”。为什么很多时候,我们根本没有写这么全,也能够实现呢?而且刚才的解释也没有说明:把什么数据哪里的数据推送上去? 所以通过这些问题的引入,希望读者们能够开始进行思考问题并去了解其内在的原理。这里可以引入一个upstream的概念。可以将upstream理解成一个通道,它用于连接你本地的一个分支和远程的某个分支。

比如远程库A上有3个分支branch1、branch2、branch3。远程库B上有3个分支branchx、branchy、branchz。本地仓库有2个分支local1和local2。那么当初始状态时,local1和local2和任何一个分支都没有关联,也就是没有upstream。当通过git branch --set-upstream-to=A/branch1 local1命令执行后,会给local1和branch1两个分支建立关联,也就是说local1的upstream指向的是branch1。这样的好处就是在local1分支上执行git push(git pull同理)操作时不用附加其它参数,Git就会自动将local1分支上的内容push到branch1上去。同样,local2分支也可以和远程库A和远程库B上的任何一个分支建立关联,只要给local2分支设置了upstream,就可以在local2分支上用git push(git pull同理)方便地与目标分支推拉数据。综上所述,upstream与有几个远程库没有关系,它是分支与分支之间的流通道。
再来说说git push -ugit branch --set-upstream-to=指令之间的区别。
举个例子:我要把本地分支master与远程仓库origin里的分支gaga建立关联
(如果使用下列途径1的话,首先,你要切换到master分支上(git checkout master))
这部分同样有问题!
两个途径:1. git push -u origin gaga 2. git branch --set-upstream-to=origin/gaga master 这两种方式都可以达到目的。但是1方法更通用,因为你的远程库有可能并没有gaga分支,这种情况下你用2方法就不可行,连目标分支都不存在,怎么进行关联呢?所以可以总结一下:git push -u origin gaga 相当于 git push origin gaga + git branch --set-upstream-to=origin/gaga master。

讲到这里,希望大家在使用简单的git push命令时,心里要有个upstream的概念,而且一旦遇到了本地分支没有设置upstream时可以一眼看出并顺利解决。

番外篇:来说说一个比较关键的题外话git config --global push.default simple命令。因为这个命令是在电脑上安装完后小编建议大家就须要设置的一个指令

这句命令的意思是“把push命令的全局默认模式设置成:simple”。当然,你也可以设置成其它模式(如:matching),当然小编不建议,后文详解。

由此可以联想到也可以git config --global pull.default simple来给pull动作也设置一下,后文就不再点出。

simple模式:说白了就是从哪里来到哪里去。一个简单的例子就会明白:如果我的本地分支branchA是从origin端的branchA分支上pull下来的,那么在simple模式下,以后我再站在branchA这个本地分支上执行git push命令时,Git就会自动把我本地branchA上更新的内容推到origin端的branchA分支上去。现在就不难推断,为什么有时候,我们什么都没设置,上来就用git push就能够达到目的而没有被老大骂?因为你的Git版本的默认push.default是simple模式,而且在你来入职的时候,公司远程服务器上已经为你准备好了属于你的分支branchA,而当你把公司远程库给clone下来的时候,因为simple的模式设置,当你切(checkout)到你本地的branchA分支时,Git就已经知道你本地的这个branchA分支来自何方,而且它也知道该把你的数据推向何方了(这里读者可以把upstream的概念思考进来,就能够明白其实Git是通过设置upstream来实现simple这个模式的功能的)。这样就会导致很多用户上来就可以无脑地使用git pull和git push了。

matching模式:这个模式小编了解不是特别特别详细,可能就是通过分支名称进行匹配。举个例子,如果你的远程分支上有两个分支:branch1和branch2。你的本地分支上有三个分支branch1和branch2和branch3。那么再matching模式下,不管你站在哪个分支上执行git push时,Git都会寻找本地与远程分支名称相同的分支并且全部进行推送数据。所以小编觉得这个模式很危险,不建议大家使用。

可见,还是simple模式比较实用,站在哪个分支上就操作哪个分支,不会出乱子。

================================================================================================================

上面的浅层操作与原理理解后,相信已经可以解决很大部分读者的疑惑了,下面开始讲解一下内在的一些原理,以便对以后要讲的原理做铺垫。

相信如果问git pull = git fetch + git merge 对不对?很多人都会说没错呀!

其实没错当然是没错。也许你会知道,git fetch是获取服务器上的数据,git merge是将数据合并,所以git pull就是获取数据加合并数据的意思。

那么:fetch的是什么数据?fetch后的数据在哪里?如果不执行git merge它们是不是丢了?git merge 是谁跟谁合并?

为减少篇幅,下面以simple模式下并且只有一个远程库的情景进行分析。

举个例子:你公司的服务器上有一个分支名字是feature(以后标识成feature®),你在clone到本地的库的本地feature(以后标识成feature(L))分支上开发。那么如果在你clone之前远程就有feature(R)的话那么你在本地的feature(L)上开发时使用git push命令就会“自动”将feature(L)的改动推送到feature(R)上(//是这样吗??)。外层原理上文已经说过了,就是stream的理论。但是实际上运作原理却很微妙。

不妨执行以下git branch -a命令。这个命令的含义是展示所有的分支。譬如类似于下图的情况:
在这里插入图片描述

可以看到三类分支名称:

1.上半部分的白色分支:这部分分支就是本地分支,上述的feature(L)就属于这类分支;(主意绿色的dev_0430左边的*号代表我当前HEAD所处的分支,即目前切换到的分支)

2.下半部分的以remotes开头的红色分支:指向远程分支的指针(为了便于理解,就把它们当成远程分支的内容在本地的临时复制品)。这是一个十分重要的概念,后文详述。

3.远程分支:即上图红色分支第一行右边的白色分支名:origin/develop。 这个就代表的远程服务器上的分支,上文提到的feature®就属于这个概念。

这样理解的话,目前认知的就有三种类型的分支了,feature(L) feature®还有以remotes开头的分支(如:remotes/origin/feature,后文简称为feature(T))。其实feature(L)和feature®从技术实现上根本没有任何交集,它们之间的关联全是通过feature(T)这个桥梁实现的。其实在feature(L)上执行了git fetch命令后,Git会把feature®上的改动下载到本地更新到feature(T)分支上,如果此后不执行git merge的话,那么你的feature(L)和feature(T)就会存在差异,只有执行了git merge命令后才会把feature(T)中的内容合并到feature(L)中。

现在清楚了吧?Git与远程仓库之间的交互是通过这个feature(T)进行过渡的。git push也是如此,在simple模式下,我们feature(L)分支的upstream其实指向的就是feature(T),而feature(T)这个指针就会指向真正的远程分支feature®。所以如果我们的feature(L)有了改动,执行git push的话,Git会先把改动更新到feature(T)中,然后再讲feature(T)的改动推向远程的feature®中。由此才可以理解成:feature(T)就是feature®在本地的一个镜像。

=============================================================================================================

好了,关于git push/git pull背后的分支的概念现在已经说的差不多了。这是以后讲解其它原理的基础,所以一定要放在前面说下,以后的文章里会更深入地讲解一些关键的原理,以帮助大家更加灵活地运用Git及解决问题。
————————————————
版权声明:本文为CSDN博主「小轩腾空」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaoxuantengkong/article/details/46128621

发布了34 篇原创文章 · 获赞 1 · 访问量 1817

猜你喜欢

转载自blog.csdn.net/weixin_44761910/article/details/105043196
今日推荐