黑帽联盟

标题: Git之旅(14):远程仓库(一) [打印本页]

作者: 定位    时间: 2020-4-3 21:40
标题: Git之旅(14):远程仓库(一)
我们之前聊的所有话题都是单人怎样使用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账号登录,没有账户可以免费注册一个,如果你是新注册的账号,登录后可以看到如下图所示的 "创建仓库" 的按钮,点击此按钮
1.png

点击创建仓库按钮后,会让你填写远程仓库的基本信息,界面如下图
2.png
上图1的位置会让你填写仓库的名称,此处设置远程仓库的名称为test
上图2的位置可以填写远程仓库的相关注释
上图3的位置可以选择创建的远程仓库是公有仓库还是私有仓库,此处选择私有仓库
上图4的位置如果勾选,会在创建远程仓库时,自动在仓库中创建一个README文件,此处没有勾选
如上图所示,点击绿色的创建仓库按钮

点击创建仓库按钮后,可以看到如下图中的仓库地址提示,
通常情况下,代码托管平台会为我们提供两种格式的仓库地址,HTTPS格式的仓库地址,以及SSH格式的仓库地址
3.png
如果你点击上图中的HTTPS,就会看到对应的https格式的仓库地址,默认情况下会显示SSH格式的仓库地址,
由于我创建的是私有仓库,所以当你通过https地址克隆远程仓库时,会提示你输入github的用户名和密码,以便验证你是否有权限克隆当前的仓库,如果你创建了一个公有仓库,可以直接使用https地址克隆对应的仓库,不会要求你输入任何用户名和密码,如果是私有仓库,在执行克隆等操作时,都会提示你输入用户名密码进行身份验证,所以为了方便,我通常不会通过https地址操作私有的远程仓库,我通常习惯使用ssh地址克隆远程仓库,因为使用ssh地址只需要一次设置好相关的秘钥,之后就能很方便的操作所有有权限的远程仓库了,不需要重复的验证用户名和密码,如果你不明白我在说什么,没有关系,我们先照做,多做两边就熟悉了,除了上图中的远程仓库地址,你可能还会在页面中看到一些github推荐你执行的命令,这些命令对应了不同的使用场景,我们暂时先不用关心这些命令,等到我们了解了整个过程以后,再来看这些命令就一目了然了,所以,此处我们先不用在意它们,完成上述操作以后,其实远程仓库的创建操作就已经完成了,到目前为止,我已经在我的github账号下创建了一个名为test的远程仓库了,回到github的首页,你就会看到所有创建过的仓库列表,从仓库列表中已经可以看到我刚才创建的远程仓库test,如下图所示。
4.png
我们已经创建了一个新的空的远程仓库,但是我现在还没有把它克隆到本地,在将远程仓库克隆到本地之前,我们需要提前做一些配置,比如,配置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"菜单,如下图
5.png

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

点击上述菜单后,可以看到绿色的 "New SSH Key" 按钮,点击绿色按钮进入添加ssh密钥的页面
配置公钥的页面如下,我们可以在title中给公钥起个名字,名字随便,在key的文本框中填入ssh公钥的内容
如下图
7.png
配置完成后,点击上图中的"Add SSH key"绿色按钮,点击按钮后,github会验证你的身份,要求你输入github账户的密码,输入正确的github账户密码后,即可看到你添加的ssh密钥
8.png
到目前为止,公钥已经配置完成了,你也可以为github添加多个公钥,以便对应多个私钥使用,当然,配置公钥的操作只需要设置一次,在密钥没有变更的情况下,可以一直使用。

到目前为止,我已经满足了使用ssh形式的仓库地址克隆远程仓库的两个条件。
1、自己的github账号是仓库的成员(之后会描述怎样为仓库添加成员,此处不用纠结)
2、自己的github账号中配置了ssh的公钥,ssh的私钥在自己的本地电脑上

现在,我要做的就是将远程仓库克隆到我的本地电脑上,其他同事也可以在满足上述条件时,将这个远程仓库克隆到自己的本地电脑中,以便所有人都能通过这个远程仓库协作管理代码。那么具体的克隆命令是什么呢?克隆命令如下:
git clone git@github.com:zsythink/test.git
在本地电脑上想要克隆远程仓库的目录中打开git bash,执行上述命令即可通过ssh格式的地址克隆远程仓库,如果你使用的是https的地址,只需要将上述ssh格式的地址换成https格式的地址即可,执行上述命令后返回信息如下:
$ git clone git@github.com:zsythink/test.git
Cloning into 'test'...
warning: You appear to have cloned an empty repository.
从返回信息可以看出,目前test仓库是一个空的仓库,我们可以进入这个仓库,查看一下情况,命令如下
/d/workspace/git
$ cd test

/d/workspace/git/test (master)
$ ls -la
total 12
drwxr-xr-x 1 zz 197121 0 1月  30 11:39 ./
drwxr-xr-x 1 zz 197121 0 1月  30 11:39 ../
drwxr-xr-x 1 zz 197121 0 1月  30 11:39 .git/

/d/workspace/git/test (master)
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)
从上述信息可以看出,除了.git目录,目前test仓库中还什么都没有,也没有任何提交,这是正常的,因为目前远程仓库中就是什么也没有的,此刻,在本地仓库中执行"git remote -v"命令,即可看到当前本地仓库对应的远程仓库,如下
/d/workspace/git/test (master)
$ git remote -v
origin  git@github.com:zsythink/test.git (fetch)
origin  git@github.com:zsythink/test.git (push)
从上述返回信息可以看出,当前仓库对应的远程仓库的地址就是"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分支的新提交推送到远程仓库,就这样来回来去,来来回回的更新,从而实现了通过远程仓库进行协作的目的,同理,同事在他本地电脑中创建的新分支也可以推送到远程仓库中,我们也可以通过远程仓库获取别人创建的分支。

现在,我们就来创建一些测试文件、创建一些提交,然后推送到远程仓库中吧,在本地仓库中执行如下命令
/d/workspace/git/test (master)
$ echo 1 > testfile1

/d/workspace/git/test (master)
$ git add -A

/d/workspace/git/test (master)
$ git commit -m "first commit"
[master (root-commit) b6d3b72] first commit
1 file changed, 1 insertion(+)
create mode 100644 testfile1
如上所示,我们添加了一个testfile1文件,并且在本地库中创建了第一个提交,此时,如果使用" git branch -vv"命令就可以查看到本地分支与远程仓库分支的对应关系,如下:
/d/workspace/git/test (master)
$ git branch -vv
* master b6d3b72 [origin/master: gone] first commit
从返回信息中可以看到,本地仓库的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分支 ,命令的执行效果如下:
/d/workspace/git/test (master)
$ git push origin master:master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 208 bytes | 69.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:zsythink/test.git
* [new branch]      master -> master
可以看到,本地的master分支已经成功的推送到了远程仓库的master分支中,
回到你的github页面中,能够看到远程仓库中master分支中已经可以看到对应的文件了。
9.png
此时,其他同事也可以从远程仓库的master分支中获取到最新的testfile1中的文件内容了,具体的操作咱们后面再进行演示。
刚才我们执行的push命令是"git push origin master:master",本地分支的分支名与远程分支的分支名是相同的,你可能会问,在推送分支时,远程分支与本地分支的分支名能不能不同呢?是可以的,我们现在就来试试,当前的状态是,本地仓库和远程仓库中都只有一个master分支,我们现在在本地仓库中执行如下命令试试:
/d/workspace/git/test (master)
$ git push origin master:m1
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'm1' on GitHub by visiting:
remote:      https://github.com/zsythink/test/pull/new/m1
remote:
To github.com:zsythink/test.git
* [new branch]      master -> m1
如你所见,我在本地仓库中执行了"git push origin master:m1"命令,表示将本地的master分支推送到远程origin仓库的m1分支中,但是在执行上述命令之前,远程仓库中并没有名为m1的分支,那么执行上述命令后,我们到github页面中看看,会不会有m1分支呢?刷新github页面,点击分支按钮(如下图),可以看到,远程仓库中多出了一个m1分支
10.png
由此可见,当远程仓库中没有m1分支时,如果在本地执行"git push origin master:m1"命令,则会在远程仓库中新建m1分支,并且将本地master分支中的内容推送到新建的远程的m1分支中,换句话说就是,当远程仓库中没有m1分支时,我们可以借助上述命令,基于本地的master分支,在远程仓库中创建一个名为m1的分支。

之前说过,使用"git branch -vv"命令可以查看到本地分支与远程分支的对应关系,那么此刻我们来看看,master分支的上游分支是master分支还是m1分支,如下:
/d/workspace/git/test (master)
$ git branch -vv
* master b6d3b72 [origin/master] first commit
可以看到,本地仓库的master分支的上游分支仍然是master分支,而不是m1分支,所以不用担心,你的推送操作不会影响默认的上游分支的设置。
其实,本地分支对应的上游分支都是可以手动进行设置的,也就是说,你也可以将本地master分支的上游分支设置为其他远程分支,但是通常情况下,为了方便记忆,都会将远程同名分支设置为上游分支,具体设置上游分支的方法我们之后再聊,现在我们先来聊聊默认使用同名的上游分支有什么好处,除了刚才提到的方便记忆,还有一个好处就是在执行推送操作时,能够使用更加简短的命令进行推送,比如,本地master的上游分支名也是master,是同名的,那么当你想要将本地的master分支中的新提交推送到远程分支的master分支时,可以直接在本地的master分支中执行"git push"命令(注意:不同版本的git执行此命令的效果不同,之后咱们再详细解释),执行"git push"命令不用指定远程仓库的名称,也不用指定本地分支的名称和远程分支的名称,也就是说," git push origin master:master"可以直接简化成" git push",是不是很方便,我们来试试,先创建一个新的提交,然后直接用"git push"命令推送,如下:
/d/workspace/git/test (master)
$ echo testfile2 > testfile2

/d/workspace/git/test (master)
$ git add testfile2

/d/workspace/git/test (master)
$ git commit -m "add testfile2"
[master 23294a7] add testfile2
1 file changed, 1 insertion(+)
create mode 100644 testfile2

/d/workspace/git/test (master)
$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 273 bytes | 136.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:zsythink/test.git
b6d3b72..23294a7  master -> master
如上述命令所示,我们新创建了一个测试文件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操作,先来创建新分支并且创建新提交,命令如下:
/d/workspace/git/test (master)
$ git checkout -b new
Switched to a new branch 'new'

/d/workspace/git/test (new)
$ echo testfile3 > testfile3

/d/workspace/git/test (new)
$ git add testfile3

/d/workspace/git/test (new)
$ git commit -m "add testfile3 in new branch"
[new 9edf8e5] add testfile3 in new branch
1 file changed, 1 insertion(+)
create mode 100644 testfile3
如上命令所示,我们基于master分支创建了一个new分支,并且创建了一个新的测试文件和一个新的提交。
此时,我想要将本地的new分支同步到远程仓库中,该怎么办呢?能不能直接使用"git push"命令推送呢?我们来试试,执行命令后附发现返回信息如下:
/d/workspace/git/test (new)
$ git push
fatal: The current branch new has no upstream branch.
To push the current branch and set the remote as upstream, use

git push --set-upstream origin new
从返回信息可以看到,推送失败了,失败的原因也已经告诉了我们,失败的原因是new分支没有对应的上游分支,刚才在介绍"git push"命令的时候,就强调过默认的前提条件,条件就是本地分支必须有对应的上游分支,而且上下游分支必须同名,根据提示信息可以看出,"git push"命令执行失败的原因是因为new分支没有上游分支造成的,那么我们执行"git branch -vv"命令看看,看看具体的情况是什么样的
/d/workspace/git/test (new)
$ git branch -vv
master 23294a7 [origin/master] add testfile2
* new    9edf8e5 add testfile3 in new branch
从上述信息可以看出,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分支,实际效果如下
/d/workspace/git/test (new)
$ git push --set-upstream origin new:new
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 309 bytes | 154.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'new' on GitHub by visiting:
remote:      https://github.com/zsythink/test/pull/new/new
remote:
To github.com:zsythink/test.git
* [new branch]      new -> new
Branch 'new' set up to track remote branch 'new' from 'origin'.

/d/workspace/git/test (new)
$ git branch -vv
master 23294a7 [origin/master] add testfile2
* new    9edf8e5 [origin/new] add testfile3 in new branch
从上述命令可以看出,当我们执行"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"命令即可。

刚才说过,我们也可以分两步来完成上述操作:
一、先推送本地分支到远程仓库
二、再手动的设置本地分支的上游分支
我们先聊聊第一步
推送本地分支到远程分支的命令我们已经使用过,如下:
git push origin new:new
由于推送的本地分支和对应的远程分支同名,所以上述命令也可以简写为
git push origin new
当执行完上述命令后,本地new分支已经推送到了远程仓库中。
这时我们再执行第二步,手动的将本地new分支的上游分支设置为远程origin仓库中的new分支,命令如下:
git branch new --set-upstream-to=origin/new
上述命令表示将本地的new分支的上游分支设置为"origin/new"分支,我们也可以使用短选项"-u"代替长选项"--set-upstream-to=",命令如下:
git branch new -u origin/new
上述命令中的本地分支和远程分支的顺序也可以调换,如下命令与上述命令效果相同
git branch -u origin/new new
记忆小技巧:"-u"选项后面指定远程分支名即可,本地分支和远程分支顺序无所谓。
我们也可以省略上述命令中的本地分支名称,当没有指定本地分支名称时,表示默认设置当前分支的上游分支,比如,当前我处于a分支,那么当我执行如下命令时,就表示将本地a分支的上游分支设置为"origin/new"
git branch -u origin/new
上述命令都可以重复执行,所以,我们不仅可以通过上述命令为没有上游分支的本地分支指定上游分支,也可以使用这些命令将本地分支的上游分支修改为其他远程分支,不过,还是之前的建议,建议本地分支和对应的上游远程分支使用相同的名称,以便对应记忆,也方便推送。

之前说过,当你需要确定分支对应的上游分支时,你可以执行"git branch -vv"命令查看,此时,我的执行结果如下
/d/workspace/git/test (new)
$ git branch -vv
master 23294a7 [origin/master] add testfile2
* new    9edf8e5 [origin/new] add testfile3 in new branch
其实," git branch -vv"命令显示出的分支都是"纯本地分支",你可能会问,"纯本地分支"是啥意思???难道还有"不纯的本地分支"吗,先别着急,我们在" git branch -vv"命令的基础上加一个"-a"参数看看,我们使用" git branch -avv"命令看看会显示出什么信息,如下:
/d/workspace/git/test (new)
$ git branch -avv
master                23294a7 [origin/master] add testfile2
* new                   9edf8e5 [origin/new] add testfile3 in new branch
remotes/origin/m1     b6d3b72 first commit
remotes/origin/master 23294a7 add testfile2
remotes/origin/new    9edf8e5 add testfile3 in new branch
可以看到,使用" git branch -avv"除了显示出了本地的master分支和new分支,还显示出了一些以"remotes/origin/"开头的分支,这些分支又是什么分支呢?
我们可以把这些分支理解成是远程分支在本地仓库中的投影,或者说这些分支就是用于在本地跟踪真正的远程分支的,真正的远程分支存在于github的服务器上,而本地仓库中的这些"remotes/origin/***"分支的主要作用是用于跟踪真正的远程分支,方便我们在本地进行一些操作,这些分支在本地代表了真正的远程分支,我们可以把这种分支称为远程追踪分支,聪明如你一定已经看出来了,当我们为本地分支指定上游分支时,其实指定的就是这些远程追踪分支的名字,这种远程追踪分支在进行推送操作时通常不用在意,当我们执行拉取操作时,会仔细的聊聊这种远程追踪分支,现在只是初步的了解即可。

说了这么多,其实也只是描述了怎样创建远程仓库、怎样克隆远程仓库、怎样设置上游分支以及怎样将本地分支的更新推送到远程等常用操作,到目前都没有描述怎样从远程仓库拉取更新,这些就留到下一篇文章详细聊吧。

有的朋友可能比较在意之前聊到的某个场景,这个场景就是,如果之前有一个只有自己使用的本地仓库,现在突然想要多人协作,能否将已经存在于本地个人仓库中的内容同步到 新创建的远程仓库中供大家使用呢?答案是肯定的,其实方法也很简单,就是将本地私人使用的仓库与新创建的远程仓库建立关系,还记得我们之前使用的"git remote -v"命令吗?我们可以在克隆远程仓库以后,通过此命令查看当前仓库与远程仓库的对应关系,通过此命令可以查看到本地仓库的上游仓库的具体名称和地址,其实,我们也可以手动的指定本地仓库对应的远程仓库,如果一开始的本地仓库只是你个人使用的,现在你想要通过新创建的远程仓库与别人进行协作,只需要手动的将本地仓库的上游仓库指定为新创建的远程仓库即可,指定远程仓库以后,再将需要协作的分支从本地仓库推送到远程仓库中,别人就能够通过远程仓库获取到对应的分支了,那么为本地仓库添加远程仓库的命令是什么呢?命令如下:
git remote add origin git@github.com:zsythink/test1.git
你肯定看明白了,你只需要在本地仓库中执行如上命令,即可将本地仓库的远程仓库指定为origin,远程仓库的地址就是"git@github.com:zsythink/test1.git",远程仓库名和仓库地址你可以根据实际情况进行设定,当然,这个地址也可以是https格式的地址。
执行上述命令后,本地仓库的远程仓库就指定为origin了,此时再次执行命令"git remote -v"查看远程仓库,就能够看到对应的远程仓库地址了。
你也可以为同一个本地仓库添加多个远程仓库,换句话说就是,你可以为本地仓库添加多个远程仓库的对应关系,这样你在本地仓库执行拉取操作和推送操作时,就需要指定对应的远程仓库名称,比如,我们之前执行的推送命令"git push -u origin new",你可以将命令中的origin换成对应的远程仓库名称就行了。
当你为本地仓库指定远程仓库后,剩下的事情就很好办了,我们只需要将本地仓库的分支推送到远程,就可以通过远程仓库进行协作了,你理解了吗?如果你理解了,可以在github上再创建一个新的远程仓库,当仓库创建后,你会看到远程仓库的地址以及github建议你执行的命令,现在回过头来再看这些命令,是不是感觉很轻松呢?

有时候,公司可能不允许将代码存放在外部服务器中(比如github、gitlab、Bitbucket等这些非公司私有的服务器都属于外部服务器),你可能会需要在公司内部的私有服务器上搭建git托管平台,我自己通常的做法是选择GitLab的安装包(注意是GitLab不是GitHub),GitLab有提供托管服务平台的安装包,我们可以通过安装包快速的在自己的服务器上搭建git托管服务,我们只需要在Centos中配置好对应的yum源,就可以方便快速的在服务器上安装属于自己的代码托管平台了,GitLab的安装包版本也分为社区版和企业版,社区版是免费的,基本功能都有,企业版的功能更多,但是是收费的,记得前一段时间安装GitLab的托管平台社区版本时,有看到GitLab官方建议我们直接安装企业版,我记得当时GitLab官方有段声明,大致意思就是说我们可以直接安装企业版的GitLab托管服务,在没有导入企业版授权的情况下,企业版也只能提供社区版的功能,只有在导入付费授权后,企业版的所有功能才会解锁,也就是说, 我们可以正常的把企业版当做社区版使用,以便随时付费解锁企业版的所有功能,在没有导入付费授权之前,企业版也只会提供社区版的功能,与社区版无二,当然,这只是前一段时间的规定,如果你需要使用GitLab的安装包搭建git托管服务器,还是要搞明白对应的版权要求,以免出现版权问题。

好了,这篇文章有点长了,就先写到这里,其他的问题和场景我们之后再聊。






欢迎光临 黑帽联盟 (https://bbs.cnblackhat.com/) Powered by Discuz! X2.5