上一篇中学习了基本的本地的版本控制,现在进一步的学习Git的使用。
远程仓库
现在我们需要找一个网站用来为我们提供Git仓库托管服务,这个网站叫做Github,这个网站为我们提供了Git仓库的托管服务,不过我们首先需要注册一个账号,这里我已经搞完了。
本地的Git仓库和Github仓库之间的传输时通过SSH设置的,所以我们需要设置:
创建SSH Key:这个我在本机上已经搞过了,这次在WSL上也搞一次,输入指令
1 | $ ssh-keygen -t rsa -C "youremail@example.com" |
然后你就会看到这个界面:
1 | $ ssh-keygen -t rsa -C "3280661240@qq.com" |
在Enter passphrase
处可以输入你的密码(用于生成私钥)
配置公钥文件:获取公钥的内容
1 | $ cat id_rsa.pub |
然后复制内容到这里:


现在我们就添加成功啦。
SSH Key的作用是,Github需要识别处你推送的提交是你的推送的,而不是别人冒充的。同时由于RSA加密的特性,公钥是可以公开的,但是私钥是由自己保管的,从而确保了加密的安全性。在此基础上,我们可以开始远程仓库的使用了。
添加远程库
现在我们在本地已经有了一个版本库,我们再到Github创建一个仓库,让这两个仓库进行远程同步。这样Github上的仓库既可以作为备份,也可以让其他人通过仓库来协作。
首先在Github上面创建一个新的仓库:

一个Github仓库就这样建好了,现在我们将其和我们的本地仓库关联起来。根据Github下面的提示,我们在本地的仓库里面运行这些命令:
1 | $ git remote add origin git@github.com:Ylin07/Learn_Git.git |
添加后,远程库的名字就是origin
,这是Git的默认叫法。下一步,我们将本地库的所有内容推送到远程库上:
1 | $ git push -u origin master |
其中由于,这是第一次关联,我们需要验证公钥指纹,然后将身份添加到~/.ssh/known_hosts
中,同时需要输入密钥验证,这里我用分割线把这一部分区分出来。下面则是我们将本地仓库推送上去的信息。
由于远程库是空的,我们第一次推送master
分支,使用了-u
参数,Git不但会把本地的master
分支推送上去,还会把本地的master
和远程的master
分支关联起来,这样在之后的推送和拉去就可以简化命令。
然后我们就可以在Github页面中看到远程库的内容和本地是一样的

从现在开始,只要本地作了提交,就可以通过命令:
1 | $ git push origin master |
把本地的master
分支的最新修改推送到GIthub,现在我们就有了完整的分布式版本库。
删除远程库
如果添加的时候地址写错了,或者是想删除远程库,可以使用git remote rm <name>
命令。使用前,我们先使用git remote -v
来查看远程库的信息:
1 | $ git remote -v |
然后根据名字删除,比如删除origin
:
1 | $ git remote rm origin |
此处的删除实际上是解除了本地和远程的关联状态,并不是删除了远程库。远程库本身并没有改动,如果想要删除远程库,应该到Github后台删除。
克隆远程仓库
上次我们讲了现有本地库,再有远程库的时候,如何关联远程库。现在我们从头开始,假如我们从远程库开始克隆该怎么做呢?
我们把这个网址的项目给clone
下来8086
1 | $ git clone git@github.com:Rexicon226/8086.git |
OK,然后看看文件夹,已经被远程库的内容已经被拉下来了:
1 | $ cd 8086 |
当然我们还可以使用其他的方法,比如https协议,只不过这个对网络环境有一定的要求。
1 | git clone https://github.com/Rexicon226/8086.git |
现在我们就掌握了对于远程仓库的基本操作啦。
分支管理
当你和别人共同开发一个项目时,你们可以各自创建一个分支,不同的分支拥有自己的进度,互不打扰。再各自的项目完成之后,再将分支合并,从而实现同时开发的效果,这就是Git的分支管理功能。
创建与合并分支
在版本回退中我们知道,Git把提交串成一条时间线,这个时间线就是一个分支。当目前为止,我们只有一个分支,这个分支就叫主分支master
,而其中的HEAD
指针并不直接指向提交的,它指向的是master
,而master
指向的是提交,所以我们说HEAD
指向的是当前的分支。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
1 | HEAD --> master |
每次提交master
分支就向前移动一步,这样随这不断的提交,master
分支也会越来越长。
当我们创建新的分支new
时,Git新建了一个指针叫new
,指向master
相同的提交,再把HEAD
指向new
,就表示当前分支在new
上:
1 | master |
由此可以看出,Git创建一个分支很快,实际上就是增加了一个new
指针,然后改变一下HEAD
的指向
不过接下来,对于工作区的修改与提交就是针对new
了,比如提交一次之后会变成这样:
1 | master |
假如我们在分支new
上的工作完成了,就可以把new
合并到master
上。Git怎么合并呢,实际上就是将master
移动到和new
指向相同的版本上,然后将HEAD
指向master
1 | HEAD --> master |
合并完成之后,甚至可以删除new
分支,我们直接将其new
指针删除既可,这样就只剩下一个mater
分支:
1 | HEAD --> master |
这样相当于用分支的功能完成了提交,这样更加安全。
现在我们来进行尝试:
首先创建一个new
分支,然后切换过去
1 | $ git checkout -b new |
git checkout
命令加上-b
参数表示创建并切换,相当于以下两个命令的组合
1 | $ git branch new |
git branch
命令会列出所有的分支,当前分支前会有一个*
号。然后我们在new
分支上修改后正常提交:
1 | $ git add readme.txt |
现在dev
的分支工作完成,我们切换回master
分支,然后再查看readme.txt
:
1 | $ git checkout master |
然后我们打开readme.txt
发现原来的修改怎么不见了。因为刚刚提交的修改在new
分支上,此时master
分支的提交点并没有变:
1 | HEAD --> master |
现在我们将new
分支的工作成果合并到master
分支上:
1 | $ git merge new |
git merge
命令用于合并指定分支到当前分支。合并后,再查看readme.txt
的内容,可以看到现在和最新提交是一样的了。
注意到上面的Fast-forward
信息,它的意思是这次合并是“快进模式”,也就是把master
指向dev
的当前提交,所以很快
合并之后,我们将new
分支删除,并查看branch
,只剩下了master
:
1 | $ git branch -d new |
由于创建,合并和删除分支非常快,所以Git更加鼓励使用分支完成任务,然后再删除分支,这样比直接在master
上工作的效果是一样的,但是过程更加的安全。
switch
切换分支,除了使用git checkout <branch>
,还可以使用switch
来实现:
- 创建并切换到新得
new
分支,可以用:git switch -c new
- 直接切换到已有得
master
分支,可以用:git switch master
解决冲突
有时候合并也会遇到各种冲突,现在我们手动制造一个冲突,我们创建一个新的分支n1
:
1 | $ git switch -c n1 |
最后一行加个01234
,然后提交修改
1 | $ git add readme.txt |
然后切换回master
分支,然后对readme.txt
做不一样的修改,并提交:
1
2
3
4$ git add readme.txt
$ git commit -m "56789"
[master 38fe2c0] 56789
1 file changed, 1 insertion(+)
现在master
和n1
分支各自都分别有新的提交:
1 | +-->[ ] <-- master <-- HEAD |
这种情况下,Git没办法快速合并,只能视图把各自的修改合并起来:
1 | $ git merge n1 |
结果提示了冲突,我们需要手动解决冲突后再提交,我们可以用git status
告诉我们冲突的文件:
1 | $ git status |
我们打开readme.txt
看看:
1 | Hello Git!!! |
Git
用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,我们修改后再保存
我们修改之后再保存:
1 | Hello Git!!! |
再提交:
1 | $ git add readme.txt |
现在我们的版本库变成了这样:
1 | +-->[ ]-->[ ] <-- master <-- HEAD |
我们可以用带参数的got log
可视化的看到我们分支的合并情况:
1 |
|
解释以下标签的意思:
--graph
:以字符可视化的方式打印分支流程--pretty=oneline
:以一行压缩打印,不显示id之外的信息--abbrev-commit
:简略id信息,只显示一部分
合并之后,我们删除n1
分支:
1 | $ git branch -d n1 |
分支管理策略
通常合并分支时,Git会优先使用Fast forward
的模式,但这种模式下,删除分支,会丢失分支信息
如果我们要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样就从分支历史上可以看出分支信息,下面我们尝试一个--no-ff
方法的git merge
:
首先创建一个分支,并修改内容,然后提交修改:
1 | $ git switch -c dev |
然后我们切换回master
并使用--no-ff
参数,以禁用Fast forward
:
1
2
3
4
5
6$ git switch master
Switched to branch 'master'
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'ort' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
因为本次合并会产生新的commit,所以加上-m
参数,把commit描述加进去。然后用git log
查看历史:
1 | $ git log --graph --pretty=oneline --abbrev-com |
可以看到不用Fast forward
模式,merge后就像这样:
1 | +-->[ ] <-- master <-- HEAD |
分支策略
在实际开发种,我们应该明确几个基本原则,进行分支管理:
- 首先
master
分支应该稳定的,仅用来发布新版本,不能在上面干活 - 平时应该在
dev
分支上干活,只有特定版本可以发布时,我们再将dev
和master
分支进行合并 - 每个人又自己的分支,当完成特定功能时,再向
dev
分支合并
因此,团队协作更像是这样的:
