更流畅地使用分布式版本控制系统 git

git 简介

git 是目前最流行的分布式版本控制系统,大家都知道,它的诞生是由于 Linux 系统之父林纳斯痛恨集中式的版本控制系统 CVS 和 SVN,花了两周的时间写出来的。分布式系统最突出的特点是它的去中心化。git 不像 CVS 或 SVN 那样需要有一台存放版本库的中央服务器,而是在每个用户电脑上都有一个完整的版本库,可以在自己的电脑上进行代码的修改与提交,不需要联网,因此速度很快,就算一台电脑出了问题,也不影响其他电脑的工作,备份和恢复都十分方便。这也是 git 一经问世,就火遍全球的原因。

git 的教程遍地都是,而且写的都还不错,比如廖雪峰的 Git 教程, 在这里我只是介绍下工作当中常用到的 git 命令,包括分支管理,冲突解决,版本回退。这也是刚开始接触 git 时比较令人困惑的地方。

git 之分支管理

在一个项目的开发过程中,一般都会有两个分支——master 分支和 develop 分支。生产环境跑的代码位于 master 分支,测试环境的位于 develop 分支。当需要开发一个新功能时,需要执行下面命令,从 master 创建一个新分支,比方说是 issue-1:

1
$ git checkout -b issue-1

此时你已经切换到了这个新分支,现在你就可以进行新功能的开发了。比如你现在开发的文件是 GOT.txt,那么过程中需要频繁使用下面两个命令:

1
2
$ git add GOT.txt              # 将工作区的修改提交至暂存区
$ git commit -m 'add GOT.txt' # 将暂存区的修改提交至分支

为了“偷懒”,可以合成一个命令:

1
$ git commit -am 'add GOT.txt'

5 分钟后,你的新功能已经开发完成了,但是还没有进行测试,接下来需要合并至 develop 分支并推至远程仓库进行测试:

1
2
3
$ git checkout develop
$ git merge issue-1
$ git push origin develop

如果测试有问题,就重新切回 issue-1 分支进行修改,并重复以上步骤。5 分钟后,bug 全都被搞定,接下来就可以将新开发的代码合并至 master 分支并推送至远程仓库:

1
2
3
$ git checkout master
$ git merge issue-1
$ git push origin master

如果你没有 master 分支的 merge 权限,就需要先将自己的分支推送至远程仓库:

1
$ git push origin issue-1  # 若没有则会创建

然后在 gitlab 上提交 merge request 申请,让有权限的人帮忙合并至 master 分支。

最后,需要删除你的本地分支和远程分支(如果有的话):

1
2
$ git branch -d issue-1	            # 删除本地分支,如果未 merge,则需要使用 '-D' 来删除
$ git push origin --delete issue-1 # 删除远程分支

git 之冲突解决

由于一个项目很可能是多个人在开发,故在使用 git merge 的过程中,难免会出现冲突,手动解决冲突的确是一件让人感到头冷的事,因为很可能误删或者少删了某些代码,而出现意想不到的问题。为了不那么头冷,我们可以通过命令来实现。

假设现在你正在将刚刚开发的新分支 issue-1 合并至 develop 分支,工作环境处于 develop 分支,如下图所示:

1
2
3
4
5
           HEAD
|
develop C
/
issue-1 A -- B

如果未发生冲突,执行 git merge issue-1 之后的变化如下:

1
2
3
4
5
                HEAD
|
develop C -- D
/ /
issue-1 A -- B

如果 B 和 C 同时修改了 A 的同一行代码,就会引发一个冲突。假设 GOT.txt 文件在版本 A、B 和 C 中的内容分别是 Jon SnowTyrion LannisterArya Stark,执行 git merge issue-1 会失败, GOT.txt 会变成下面的样子:

1
2
3
4
5
<<<<<<< HEAD
Arya Stark
=======
Tyrion Lannister
>>>>>>> issue-1

可以看出,<<<<<<<=======>>>>>>> 标记出了冲突的位置,<<<<<<<======= 中间的部分称为 ours=======>>>>>>> 中间的部分称为 theirs,如果仅保留单方的修改,则可以通过下面的命令来实现而不需要在文本中进行编辑:

1
2
$ git checkout --ours GOT.txt    # => Arya Stark
$ git checkout --theirs GOT.txt # => Tyrion Lannister

然后执行 git commit -am 'fix conflicts' 提交为版本 D。如果项目中很多文件发生了冲突,则可以使用 git diff <filename> 来查看单个文件的冲突内容,并使用上述命令依次解决。

git 之版本回退

试想你开发上线的新功能,因测试不够周全而出现了问题,而且问题目前还无法定位,老板让你立刻版本回退,你心想:“坏了,自己从没遇到过这种版本回退的情况,要怎么办呢?”,为了让你不在原地一脸懵逼,可以继续往下看。

首先,回退操作过程中为了保险起见,创建一个当前 master 分支的备份分支 master_backup

1
$ git branch master_backup

如果想要简单粗暴一些,只需执行以下命令即可完成回退操作:

1
2
$ git reset --hard HEAD^     # 回退至上一版本
$ git push -f origin master # 将本地分支强推至远程

在 git 中, HEAD 指当前版本,HEAD^ 指上一个版本,上上个版本则是 HEAD^^,如果往上 100 个版本呢? 可以写成 HEAD~100git push -f 是令人嗤之以鼻的操作,因为它将远程仓库的提交记录完全覆盖为你本地的记录,这就有可能导致别人辛辛苦苦开发的代码都被你给搞没了!曾因为 git push -f 这个命令发生过命案你敢信!所以使用这个命令的时候一定要谨慎谨慎再谨慎

还有一种情况,就是你并没有权限使用 git push -f 这个命令。那么这种情况该如何回退呢?

可以先将远程的 master 分支删除,然后将本地回退后的 master 分支推送至远程:

1
2
3
$ git reset --hard HEAD^
$ git push origin --delete master
$ git push origin master # 会重新创建新的远程 master 分支

当然,这种情况也一定不要忘记备份哦!