Git学习日记(二)远程仓库与分支管理
远程仓库
GitHub提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。
第1步:创建SSH
Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git
Bash),创建SSH Key:
1 | $ ssh-keygen -t rsa -C "youremail@example.com" |
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,这两个就是SSH
Key的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。
第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH
Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub
文件的内容
添加远程库
首先,登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库。
在Repository
name填入仓库名(learngit
),其他保持默认设置,点击“Create
repository”按钮,就成功地创建了一个新的Git仓库:
目前,在GitHub上的这个learngit
仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。
现在,我们根据GitHub的提示,在本地的learngit
仓库下运行命令:
1 | $ git remote add origin git@github.com:xxxxxx/learngit.git |
请千万注意,把上面的xxxxxx替换成你自己的GitHub账户名
添加后,远程库的名字就是origin
,这是Git默认的叫法,也可以改成别的,但是origin
这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:
1 | $ git push -u origin master |
把本地库的内容推送到远程,用git push
命令,实际上是把当前分支master
推送到远程。
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
删除远程库
删除远程库
如果添加的时候地址写错了,或者就是想删除远程库,可以用git remote rm <name>
命令。使用前,建议先用git remote -v
查看远程库信息:
1 | $ git remote -v |
然后,根据名字删除,比如删除origin
:
1 | $ git remote rm origin |
此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除。
从远程库克隆
用命令git clone
克隆一个本地库:
1 | $ git clone git .com:xxxxxx/gitskills.git |
注意把Git库的地址换成你自己的,然后进入gitskills
目录看看,已经有README.md
文件了:
1 | $ cd gitskills |
分支管理
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长。
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
创建与合并分支
首先,我们创建dev
分支,然后切换到dev
分支:
1 | $ git checkout -b dev |
git checkout
命令加上-b
参数表示创建并切换,相当于以下两条命令:
1 | $ git branch dev |
然后,用git branch
命令查看当前分支:
1 | $ git branch |
git branch
命令会列出所有分支,当前分支前面会标一个*
号。
然后,我们就可以在dev
分支上正常提交,比如对readme.txt
做个修改,加上一行:
1 | Creating a new branch is quick. |
然后提交:
1 | $ git add readme.txt |
现在,dev
分支的工作完成,我们就可以切换回master
分支:
1 | $ git checkout master |
切换回master
分支后,再查看一个readme.txt
文件,刚才添加的内容不见了!因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变:
现在,我们把dev
分支的工作成果合并到master
分支上:
1 | $ git merge dev |
git merge
命令用于合并指定分支到当前分支。合并后,再查看readme.txt
的内容,就可以看到,和dev
分支的最新提交是完全一样的。
注意到上面的Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。
当然,也不是每次合并都能Fast-forward
,我们后面会讲其他方式的合并。
合并完成后,就可以放心地删除dev
分支了:
1 | $ git branch -d dev |
删除后,查看branch
,就只剩下master
分支了:
1 | $ git branch |
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全。
switch
我们注意到切换分支使用git checkout <branch>
,而前面讲过的撤销修改则是git checkout -- <file>
,同一个命令,有两种作用,确实有点令人迷惑。
实际上,切换分支这个动作,用switch
更科学。因此,最新版本的Git提供了新的git switch
命令来切换分支:
创建并切换到新的dev
分支,可以使用:
1 | $ git switch -c dev |
直接切换到已有的master
分支,可以使用:
1 | $ git switch master |
使用新的git switch
命令,比git checkout
要更容易理解。
解决冲突
准备新的feature1
分支,继续我们的新分支开发:
1 | $ git switch -c feature1 |
修改readme.txt
最后一行,改为:
1 | Creating a new branch is quick AND simple. |
在feature1
分支上提交:
1 | $ git add readme.txt |
切换到master
分支:
1 | $ git switch master |
Git还会自动提示我们当前master
分支比远程的master
分支要超前1个提交。
在master
分支上把readme.txt
文件的最后一行改为:
1 | Creating a new branch is quick & simple. |
提交:
1 | $ git add readme.txt |
现在,master
分支和feature1
分支各自都分别有新的提交,变成了这样:
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:
1 | $ git merge feature1 |
果然冲突了!Git告诉我们,readme.txt
文件存在冲突,必须手动解决冲突后再提交。git status
也可以告诉我们冲突的文件:
1 | $ git status |
我们可以直接查看readme.txt的内容:
1 | Git is a distributed version control system. |
Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,我们修改如下后保存:
1 | Creating a new branch is quick and simple. |
再提交:
1 | $ git add readme.txt |
现在,master
分支和feature1
分支变成了下图所示:
用带参数的git log
也可以看到分支的合并情况:
1 | $ git log --graph --pretty=oneline --abbrev-commit |
最后,删除feature1
分支:
1 | $ git branch -d feature1 |
分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
下面我们实战一下--no-ff
方式的git merge
:
首先,仍然创建并切换dev
分支:
1 | $ git switch -c dev |
修改readme.txt文件,并提交一个新的commit:
1 | $ git add readme.txt |
现在,我们切换回master
:
1 | $ git switch master |
准备合并dev
分支,请注意--no-ff
参数,表示禁用Fast forward
:
1 | $ git merge --no-ff -m "merge with no-ff" dev |
因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去。
合并后,我们用git log
看看分支历史:
1 | $ git log --graph --pretty=oneline --abbrev-commit |
可以看到,不使用Fast forward
模式,merge后就像这样:
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样: