Git之旅(14):远程仓库(一)

  • A+
所属分类:Git  运维技术

  

博主会将与Git有关的知识点总结到"通俗易懂Git入门系列"文章中,如果你对Git不是特别了解,请按照顺序阅读"Git系列",以便站在前文的基础上理解新的知识点。

 

我们之前聊的所有话题都是单人怎样使用git进行版本管理,这篇文章我们来聊聊怎样多人协作使用git进行版本管理,其实只要理解了前文,理解多人协作也是非常容易的,我们先来聊聊概念,理解了概念以后,再聊具体操作。

 

当多人需要通过git进行协作时,通常的做法是创建一个远程仓库,每个人把远程仓库克隆到本地,克隆后,每个人就在本地获取到了一个和远程仓库一样的git仓库,这样,每个人就可以在自己的本地库中对代码进行开发和管理,当你需要把自己本地产生的最新代码同步到远程仓库中时,就可以进行一个推送操作,将本地的最新代码推送到远程仓库中,同理,如果你想要从远程仓库中获取到别人推送的最新的代码,也可以进行一个拉取操作,将远程仓库的最新代码拉取到本地仓库中,换句话说就是,将远程仓库克隆到本地以后,大家通过本地仓库进行实际修改和版本管理,通过远程仓库进行代码的更新和交换,这样描述并不具象化,别着急,向下看。

 

看完上述描述,你可能会有如下疑问

1、如果之前有一个只有自己使用的本地仓库,现在突然想要多人协作,能否将已经存在于本地个人仓库中的内容同步到远程仓库中供大家使用呢?

2、如果多个人同时修改了自己本地仓库中的同一个文件,比如A文件,那么当他们都向远程仓库中推送A文件时,会出现冲突吗?同理,如果从远程仓库中拉取的最新代码与本地的代码存在冲突,该怎样解决呢?

3、一些其他的问题

 

咱们一个一个聊

 

首先咱们聊聊第一个问题,能否将现有仓库中的内容同步到新建的远程仓库中呢?答案是肯定的。

一开始,你可能是一个光杆司令,所有开发工作都是你一个人完成的,你并不需要与别人协作,所以你只是在你的电脑上创建了一个git仓库,然后通过这个本地的git仓库管理代码,经过一段时间的发展,你找到了一些志同道合的小伙伴,你们准备多人协作,共同开发,于是您们想要通过远程仓库协作工作,所以,你创建了一个远程仓库,但是新创建的远程仓库是一个空的仓库,里面什么也没有,大家想要基于你之前的工作成果继续开发,所以你们的首要目的是将你本地仓库中已有的内容同步到新创建的远程仓库中,以便远程仓库中存放的内容是最新的,然后其他人再通过克隆远程仓库的方式,在本地创建出一个本地仓库,这样所有人就都能够获取到一份相同的代码了,之后,所有人就能够通过自己的本地仓库进行工作了,如果需要更新或者交换代码,再依靠远程仓库进行。

如果是一个新的项目,完全可以直接先创建一个新的空的远程仓库,然后大家将远程仓库克隆下来,在本地仓库中丰富内容,然后将新内容推送到远程仓库中。

所以,是否存在已有的工作成果和git仓库,都不会影响你创建远程仓库,也不会影响你何时创建远程仓库,这样空口白话的描述可能还是有点难以理解,不如先来创建一个远程仓库,从头到尾的将整个过程测试一遍,结合实际去理解吧。

 

本文中会在github上创建远程仓库,github是一个代码托管平台,我们可以在github上免费的创建仓库,以便多人协作使用,除了GitHub,国际上比较著名的代码托管平台还有Bitbucket、GitLab,国内比较著名的代码托管平台有码云、coding等,你可以选择自己喜欢的代码托管平台创建远程仓库,本文中使用GitHub,早期的时候,在GitHub中创建的远程仓库分为公有仓库和私有仓库两种,公有仓库是所有人都能访问的远程仓库,通常开源项目代码的存放会选择使用公有仓库,私有仓库是只有指定的仓库成员才能够访问的远程仓库,早期的时候在github上创建私有仓库是收费的,后来可以免费的创建私有仓库了,但是之前我记得github上免费创建的私有仓库最多只能3个人进行协作,也就是说免费创建的私有仓库有人数限制,这么长时间过去了,不知道是否还是存在对应的限制,不过此处我们只是用于测试,即使有限制也不影响我们此处的测试,所以,我们会在github上创建一个远程仓库来进行演示。

 

首先,打开github官网,网址如下:

https://github.com

 

使用github账号登录,没有账户可以免费注册一个,如果你是新注册的账号,登录后可以看到如下图所示的 "创建仓库" 的按钮,点击此按钮

Git之旅(14):远程仓库(一)

 

点击创建仓库按钮后,会让你填写远程仓库的基本信息,界面如下图

Git之旅(14):远程仓库(一)

上图1的位置会让你填写仓库的名称,此处设置远程仓库的名称为test

上图2的位置可以填写远程仓库的相关注释

上图3的位置可以选择创建的远程仓库是公有仓库还是私有仓库,此处选择私有仓库

上图4的位置如果勾选,会在创建远程仓库时,自动在仓库中创建一个README文件,此处没有勾选

如上图所示,点击绿色的创建仓库按钮

 

点击创建仓库按钮后,可以看到如下图中的仓库地址提示,

通常情况下,代码托管平台会为我们提供两种格式的仓库地址,HTTPS格式的仓库地址,以及SSH格式的仓库地址

Git之旅(14):远程仓库(一)

如果你点击上图中的HTTPS,就会看到对应的https格式的仓库地址,默认情况下会显示SSH格式的仓库地址,

由于我创建的是私有仓库,所以当你通过https地址克隆远程仓库时,会提示你输入github的用户名和密码,以便验证你是否有权限克隆当前的仓库,如果你创建了一个公有仓库,可以直接使用https地址克隆对应的仓库,不会要求你输入任何用户名和密码,如果是私有仓库,在执行克隆等操作时,都会提示你输入用户名密码进行身份验证,所以为了方便,我通常不会通过https地址操作私有的远程仓库,我通常习惯使用ssh地址克隆远程仓库,因为使用ssh地址只需要一次设置好相关的秘钥,之后就能很方便的操作所有有权限的远程仓库了,不需要重复的验证用户名和密码,如果你不明白我在说什么,没有关系,我们先照做,多做两边就熟悉了,除了上图中的远程仓库地址,你可能还会在页面中看到一些github推荐你执行的命令,这些命令对应了不同的使用场景,我们暂时先不用关心这些命令,等到我们了解了整个过程以后,再来看这些命令就一目了然了,所以,此处我们先不用在意它们,完成上述操作以后,其实远程仓库的创建操作就已经完成了,到目前为止,我已经在我的github账号下创建了一个名为test的远程仓库了,回到github的首页,你就会看到所有创建过的仓库列表,从仓库列表中已经可以看到我刚才创建的远程仓库test,如下图所示。

Git之旅(14):远程仓库(一)

我们已经创建了一个新的空的远程仓库,但是我现在还没有把它克隆到本地,在将远程仓库克隆到本地之前,我们需要提前做一些配置,比如,配置ssh密钥,之前说过,我习惯使用远程仓库的ssh地址操作远程仓库,当我们通过这种地址操作远程仓库时,代码托管平台会通过ssh密钥验证你的身份,验证你是否有权限克隆当前的远程仓库,由于当前仓库是私有仓库,所以在使用ssh地址操作远程仓库时,需要同时满足如下两个条件,才能验证成功

条件一:你是仓库的成员之一,由于我创建了test仓库,所以我默认就是test仓库的管理员,也是成员,如果你想要参与到别人创建的项目中,则需要仓库管理员将你的github账号添加到仓库成员中,同理,你创建的仓库也可以将别人的github账号添加为成员。

条件二:你的github账户中有对应的公钥,这里所说的公钥是ssh密钥对中的公钥,如果你不明白什么是ssh的密钥对,可以参考如下文章,如下链接中说明了怎样使用ssh密钥对以及创建密钥对的方法

https://www.zsythink.net/archives/2375
 

由于我是在windows中安装的git,所以我们可以在git bash的命令行窗口中使用" ssh-keygen.exe"命令生成密钥对,具体的方法可以参考上述链接,此处就不再赘述了,如果你之前就有已经生成好的密钥对,也可以直接使用,假如你是新生成的密钥对,默认情况下,会在当前用户的家目录中的".ssh"目录中生成密钥对,也就是如下路径

C:\Users\用户名\.ssh

当生成密钥对以后,我们需要做的就是将公钥配置到github账户中,以便github可以通过公钥验证我们的身份,私钥是我们自己保留的,不要泄露给任何人。

 

将公钥添加到github账号的具体步骤如下:

登录github账号后,点击右上角的账户图标,然后点击"Settings"菜单,如下图

Git之旅(14):远程仓库(一)

 

进入页面后,点击左侧菜单中的"SSH and GPG keys"菜单,如下图

Git之旅(14):远程仓库(一)

 

点击上述菜单后,可以看到绿色的 "New SSH Key" 按钮,点击绿色按钮进入添加ssh密钥的页面

配置公钥的页面如下,我们可以在title中给公钥起个名字,名字随便,在key的文本框中填入ssh公钥的内容

如下图

Git之旅(14):远程仓库(一)

配置完成后,点击上图中的"Add SSH key"绿色按钮,点击按钮后,github会验证你的身份,要求你输入github账户的密码,输入正确的github账户密码后,即可看到你添加的ssh密钥

Git之旅(14):远程仓库(一)

到目前为止,公钥已经配置完成了,你也可以为github添加多个公钥,以便对应多个私钥使用,当然,配置公钥的操作只需要设置一次,在密钥没有变更的情况下,可以一直使用。

 

到目前为止,我已经满足了使用ssh形式的仓库地址克隆远程仓库的两个条件。

1、自己的github账号是仓库的成员(之后会描述怎样为仓库添加成员,此处不用纠结)

2、自己的github账号中配置了ssh的公钥,ssh的私钥在自己的本地电脑上

 

现在,我要做的就是将远程仓库克隆到我的本地电脑上,其他同事也可以在满足上述条件时,将这个远程仓库克隆到自己的本地电脑中,以便所有人都能通过这个远程仓库协作管理代码。那么具体的克隆命令是什么呢?克隆命令如下:

在本地电脑上想要克隆远程仓库的目录中打开git bash,执行上述命令即可通过ssh格式的地址克隆远程仓库,如果你使用的是https的地址,只需要将上述ssh格式的地址换成https格式的地址即可,执行上述命令后返回信息如下:

从返回信息可以看出,目前test仓库是一个空的仓库,我们可以进入这个仓库,查看一下情况,命令如下

从上述信息可以看出,除了.git目录,目前test仓库中还什么都没有,也没有任何提交,这是正常的,因为目前远程仓库中就是什么也没有的,此刻,在本地仓库中执行"git remote -v"命令,即可看到当前本地仓库对应的远程仓库,如下

从上述返回信息可以看出,当前仓库对应的远程仓库的地址就是"git@github.com:zsythink/test.git",你可能会有疑问,为什么有两条记录呢?仔细观察上面两条记录,地址是一样的,但是地址后面分别有 (fetch)和(push)标记,没错,这两条记录分别对应的拉取操作和推送操作,也就是说,拉取操作对应的远程仓库地址"git@github.com:zsythink/test.git",这个远程仓库的名字是"origin  ",同理,推送操作对应的远程仓库地址"git@github.com:zsythink/test.git",这个远程仓库的名字是"origin  ",你也可以这样理解,origin仓库就是当前本地仓库对应的远程仓库,一个远程仓库会对应两条记录,两条记录分别对应了拉取操作和推送操作的地址,通常情况下,一个远程仓库的拉取地址和推送地址是同一个仓库地址,虽然可以通过命令,将拉取地址和推送地址设置为不同的仓库地址,但是git不推荐我们这样做,一些特殊的使用场景此处暂且不聊,我们只需要知道,默认情况下,当你将远程仓库克隆到本地以后,本地仓库对应的远程仓库默认的名字就叫"origin","origin"这个名字也是可以手动设置的,在没有需求的情况下,我个人不会去修改远程仓库的默认名称。

看到这里,你肯定已经明白了,当我们将远程仓库克隆到本地以后,本地仓库默认的上游仓库就是远程仓库,其实,我们也可以手动的设定某个本地仓库的上游远程仓库,这些都是后话,我们之后再聊。

 

我们现在就可以在本地仓库中完成各种工作了,比如,创建文件、创建提交等等git操作,与之前唯一不同的是,本地仓库和远程仓库是有对应关系的,本地仓库中的分支也是与远程仓库中的分支对应的,虽然我们之前创建的远程仓库是空的,但是也会有一个默认的master分支,当你克隆到本地时,本地仓库的master分支与远程仓库的master分支就是对应的,如果你在本地仓库中创建了新的new分支,那么当你将本地仓库的中的新内容推送到远程仓库时,本地仓库的new分支就会被推送到远程仓库中,随之远程仓库也会出现new分支,如果此时别的同事从远程仓库拉取更新,就会看到你推送到远程仓库的new分支,new分支也可以被同事通过远程仓库拉取同事的本地电脑中,同事也可以在他的电脑中对new分支创建新提交,然后再将他本地的new分支的新提交推送到远程仓库,就这样来回来去,来来回回的更新,从而实现了通过远程仓库进行协作的目的,同理,同事在他本地电脑中创建的新分支也可以推送到远程仓库中,我们也可以通过远程仓库获取别人创建的分支。

 

现在,我们就来创建一些测试文件、创建一些提交,然后推送到远程仓库中吧,在本地仓库中执行如下命令

如上所示,我们添加了一个testfile1文件,并且在本地库中创建了第一个提交,此时,如果使用" git branch -vv"命令就可以查看到本地分支与远程仓库分支的对应关系,如下:

从返回信息中可以看到,本地仓库的master分支与远程仓库origin的master分支是对应的,换句话说就是,origin/master分支时本地master分支的上游分支,也就是说,本地的master分支与远程仓库origin的master分支已经建立起了关系,当你在本地推送master分支时,master分支的新内容应该被推送到origin仓库的master分支中,当你在本地拉取master分支时,origin仓库的master分支中的新内容会被拉取到本地的master分支中。

 

那么现在,我们就来实际操作一下,看看怎样将本地的master分支中新产生的内容推送到远程仓库中,由于一开始远程仓库是空的,现在本地仓库中产生了新内容,所以我们需要将新内容推送到远程仓库中,以便其他同事可以通过远程仓库获取到最新的master分支中的内容。

我们可以在本地仓库中执行"git push origin master:master"命令,"git push origin master:master"命令的作用是将本地的内容推送到远程origin仓库中,推送的本地分支名是master,远程分支名也是master,没错,"master:master"中冒号左边的master代表你要推送的本地master分支,冒号右边的master代表推送到远程仓库中的master分支 ,命令的执行效果如下:

可以看到,本地的master分支已经成功的推送到了远程仓库的master分支中,

回到你的github页面中,能够看到远程仓库中master分支中已经可以看到对应的文件了。

Git之旅(14):远程仓库(一)

此时,其他同事也可以从远程仓库的master分支中获取到最新的testfile1中的文件内容了,具体的操作咱们后面再进行演示。

刚才我们执行的push命令是"git push origin master:master",本地分支的分支名与远程分支的分支名是相同的,你可能会问,在推送分支时,远程分支与本地分支的分支名能不能不同呢?是可以的,我们现在就来试试,当前的状态是,本地仓库和远程仓库中都只有一个master分支,我们现在在本地仓库中执行如下命令试试:

如你所见,我在本地仓库中执行了"git push origin master:m1"命令,表示将本地的master分支推送到远程origin仓库的m1分支中,但是在执行上述命令之前,远程仓库中并没有名为m1的分支,那么执行上述命令后,我们到github页面中看看,会不会有m1分支呢?刷新github页面,点击分支按钮(如下图),可以看到,远程仓库中多出了一个m1分支

Git之旅(14):远程仓库(一)

由此可见,当远程仓库中没有m1分支时,如果在本地执行"git push origin master:m1"命令,则会在远程仓库中新建m1分支,并且将本地master分支中的内容推送到新建的远程的m1分支中,换句话说就是,当远程仓库中没有m1分支时,我们可以借助上述命令,基于本地的master分支,在远程仓库中创建一个名为m1的分支。

 

之前说过,使用"git branch -vv"命令可以查看到本地分支与远程分支的对应关系,那么此刻我们来看看,master分支的上游分支是master分支还是m1分支,如下:

可以看到,本地仓库的master分支的上游分支仍然是master分支,而不是m1分支,所以不用担心,你的推送操作不会影响默认的上游分支的设置。

其实,本地分支对应的上游分支都是可以手动进行设置的,也就是说,你也可以将本地master分支的上游分支设置为其他远程分支,但是通常情况下,为了方便记忆,都会将远程同名分支设置为上游分支,具体设置上游分支的方法我们之后再聊,现在我们先来聊聊默认使用同名的上游分支有什么好处,除了刚才提到的方便记忆,还有一个好处就是在执行推送操作时,能够使用更加简短的命令进行推送,比如,本地master的上游分支名也是master,是同名的,那么当你想要将本地的master分支中的新提交推送到远程分支的master分支时,可以直接在本地的master分支中执行"git push"命令(注意:不同版本的git执行此命令的效果不同,之后咱们再详细解释),执行"git push"命令不用指定远程仓库的名称,也不用指定本地分支的名称和远程分支的名称,也就是说," git push origin master:master"可以直接简化成" git push",是不是很方便,我们来试试,先创建一个新的提交,然后直接用"git push"命令推送,如下:

如上述命令所示,我们新创建了一个测试文件testfile2,并且针对这个新文件创建了一个新提交,我们在master分支中直接执行"git push"命令,即可将本地master分支的新提交推送到远程仓库的master分支中,你可以去github的页面中刷新核实一下,就可以看到对应的testfile2文件了。

注意:刚才提到过,在不同版本的git中执行"git push"命令的效果是不同的,这里来描述一下具体有什么不同,在1.x版本的git中,"git push"命令会推送所有与上游分支同名的本地分支到远程,而在2.x版本的git中,"git push"命令只会在当前分支与上游分支同名的情况下推送当前分支到远程,换句话说就是,无论是1.x还是2.x的版本,使用"git push"命令推送分支到远程都有一个共同的前提,这个前提就是本地分支需要有对应的上游分支,并且本地分支与上游分支必须同名,这是使用"git push"命令的默认的前提条件,但是在1.x的git中,"git push"命令会推送所有满足前提条件的分支,而在2.x的git中,"git push"命令只会在符合前提条件时推送当前分支。其实无论是在1.x版本还是在2.x版本中,我们都可以对"git push"命令的默认行为进行设置,通过"push.default"属性就能够控制"git push"的默认条件与行为,1.x与2.x的默认行为不同就是因为不同版本中"push.default"属性的值不同,此处我们不用深究,了解即可。

 

现在,我们在本地仓库中,创建一个新的分支,在新分支上创建一些提交,然后推送到远程仓库中,以便再次熟悉一下push操作,先来创建新分支并且创建新提交,命令如下:

如上命令所示,我们基于master分支创建了一个new分支,并且创建了一个新的测试文件和一个新的提交。

此时,我想要将本地的new分支同步到远程仓库中,该怎么办呢?能不能直接使用"git push"命令推送呢?我们来试试,执行命令后附发现返回信息如下:

从返回信息可以看到,推送失败了,失败的原因也已经告诉了我们,失败的原因是new分支没有对应的上游分支,刚才在介绍"git push"命令的时候,就强调过默认的前提条件,条件就是本地分支必须有对应的上游分支,而且上下游分支必须同名,根据提示信息可以看出,"git push"命令执行失败的原因是因为new分支没有上游分支造成的,那么我们执行"git branch -vv"命令看看,看看具体的情况是什么样的

从上述信息可以看出,master的上游分支是origin/master,而new分支却没有对应的上游分支。

为什么master分支默认就有同名的上游分支,而我们创建的new分支就没有对应的上游分支呢?

原因就是,master分支从一开始就存在于远程仓库中,而new分支是我们在本地新建的,并不存在于远程仓库中。换话说就是,当我们将远程仓库克隆到本地时,master分支就已经存在于远程仓库中了,虽然远程仓库被创建时是一个空的仓库,但是远程仓库默认也是存在master分支的,当我们将远程仓库克隆到本地时,远程仓库中的master分支也会被克隆到本地,所以本地仓库中的master分支会自动将远程仓库的master分支设置成上游分支, 但是new分支则不同,new分支是我们在本地新创建的分支,new分支并不存在于远程仓库中,所以本地的new分支并不会自动设置对应的上游分支,于是,当我们在new分支中执行"git push"命令推送当前分支时,会提示没有对应的上游分支。

 

由于目前远程仓库中还没有new分支,所以我们可以先将本地new分支推送到远程仓库中,然后再手动的将本地的new分支的上游分支设置为远程仓库中的new分支,上述操作需要分两步完成,先推送new分支到远程,然后设置本地的上游分支为远程分支,其实我们也可以将上述两步合并成一步去完成,我们可以直接执行"git push --set-upstream origin new:new"命令,此命令会在推送本地new分支到远程仓库的同时,直接将本地new分支的上游分支设置为远程仓库中的new分支,实际效果如下

从上述命令可以看出,当我们执行"git push --set-upstream origin new:new"命令后,本地new分支的上游分支自动设置成了"origin/new"分支,其实我们只是在原来推送分支的命令的基础上,添加了一个"--set-upstream"选项,就可以达到推送本地分支到远程的同时设置本地上游分支的效果了,这时我们可以查看github的页面,会发现new分支已经推送到了远程仓库中。

 

如果你和我一样,使用的是2.x版本的git,那么你可以使用短选项"-u"代替上述命令中的长选项"--set-upstream",也就是说,"git push --set-upstream origin new:new" 和 "git push -u origin new:new" 的效果是一样的,需要注意的是,在早期的1.x版本的git中,只有长选项,没有短选项,由于我们想要推送的本地分支与对应的远程分支同名,所以上述命令还可以简写成"git push -u origin new",效果也是一样的,当执行完上述命令后,由于new分支已经存在了对应的上游分支,而且上游分支和本地new分支同名,之后再在new分支中执行推送操作时,就可以直接执行"git push"命令了(注意:2.x版本默认只会推送当前所在的分支),不用加任何选项和参数,直接执行"git push"命令即可。

 

刚才说过,我们也可以分两步来完成上述操作:

一、先推送本地分支到远程仓库

二、再手动的设置本地分支的上游分支

我们先聊聊第一步

推送本地分支到远程分支的命令我们已经使用过,如下:

由于推送的本地分支和对应的远程分支同名,所以上述命令也可以简写为

当执行完上述命令后,本地new分支已经推送到了远程仓库中。

这时我们再执行第二步,手动的将本地new分支的上游分支设置为远程origin仓库中的new分支,命令如下: