本文面向初次接触版本控制系统的Git用户,旨在介绍一些关于版本控制和Git的简单概念。

文中并不涉及过多的Git实际操作,文末推荐更多的Git学习资源。

导言

Git是目前最先进的版本控制系统,拥有最多的用户数量并管理着数量庞大的实际软件项目;风靡全球的Github更是让Git版本控制系统名声大震。本文以“版本控制系统”为切入点,以国内的GitCafe平台为例,介绍相关概念和简单的Git用法。

什么是版本控制系统?

如果对“版本控制”没有概念,那真是没法用Git。

所以我们来看一个例子:假设大四毕业生小张在写毕业论文,写好初稿后经常删改,甚至还会在第二天把前一天删掉的东西找回来。如果他动点脑子,就不会只在一个文档中改来改去,而会在文件夹中有:

1
2
3
4
5
6
7
8
9
10
毕业论文_初稿.doc
毕业论文_修改1.doc
毕业论文_修改2.doc
毕业论文_修改3.doc
毕业论文_完整版1.doc
毕业论文_完整版2.doc
毕业论文_完整版3.doc
毕业论文_最终版1.doc
毕业论文_最终版2.doc
……

看起来是不是很郁闷啊?小张当然也郁闷了,因为自己总是改不好,所以他把自己的论文发给女朋友(是学霸),求帮忙;与此同时他自己也在继续修改。第二天就有了:

1
2
毕业论文_最终版3.doc
毕业论文_女友版1.doc

他女友毕竟是学霸,当然给他的论文做了比较大的修改,此时小张虽然看到了希望,但还要纠结着做一件事情:把上面两个版本的论文合并成:

1
毕业论文_死也不改版.doc

等合并好,已是凌晨三点半。小张无比怨念:这样子真是没法和女友开心的玩耍了呢!

怎么办?

小张想,如果能有个什么东西来帮忙控制一下这该死的版本,那真是谢天谢地了!就像这样:

版本 修改人 说明 日期
初稿 小张 这是初稿 Day1
修改1 小张 修改目录 Day2
修改2 小张 合并段落 Day3
…… …… …… ……
最终版2 小张 xxx Day7
死也不改版 女友 bala Day8

这样就不用手动控制那么多版本啦!

所以,所谓“版本控制系统”,就是来解决这类问题的。

Git又是什么?

没错,Git就是一个版本控制软件。在进行软件开发时,一个团队的人靠使用Git,就能轻松管理好项目版本,做好项目的追踪和辅助进度控制。确切的讲,Git是一款分布式版本控制系统。这个“分布式”,要和“集中式”放在一起理解。

所谓“集中式版本控制”,就好比这一个团队中,版本库都集中在一台服务器上,每个开发者都要从服务器上获取最新的版本库后才能进行开发,开发完了再把新的版本提交回去。

而“分布式版本控制”,则是这个团队中每个人的电脑上都会有一份完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。

在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

版本库是咋回事?

版本,不是文件的版本么?这个版本库是什么意思?

如果能意识到控制的是文件的版本,那就离理解版本库只差一步了(最关键的一步)。

程序要记录项目的版本号,最简单的方法就是把版本相关的信息放在某文件里,持续写入文件便视为版本更新。所以,当我们在一个目录中运行命令:

1
2
$ git init
# Git初始化的命令,用于新建版本库

这个目录中就会默认产生一个新目录:

1
.git/

没错,这就是我们刚刚新建的版本库,它将默认记录当前目录(直接包含.git的这个目录,下文中叫做“项目目录”)中任何文件的改动。如果把这个版本库删除了,这里面记录的文件版本就都没有了,项目目录中的文件当前是什么样子,那就一直是什么样子,没法恢复到以前的版本了。如果想让Git忽视项目目录中的什么文件(比如程序缓存等),可以在.gitignore中写清楚那些文件。

那么,Git怎么用?

在实际应用中,Git有非常多的用法,而本文是面向Git完全初学者,所以我们要从最基本的开始做。
比如,在刚才建好的版本库中,A新建了README文件,并在里面写了东西。写好后他想给项目做个版本,就需要这样:

1
2
$ git add README
$ git commit -m "add README"

第一个命令是告诉Git要追踪什么文件,第二个命令是进行提交,并对此次提交做个简答说明。当然,今后他再对README做什么修改,都可以这样做。Git会自动为此次提交生成一个16进制的版本号。

如果此时他查看本地的版本库,就会发现最新的一次提交是在刚才,提交说明为:add README

然后,他要把项目的版本库更新到GitCafe上,当然这时候项目本身已经在GitCafe上建立好了。他只需要:

1
$ git push origin master

这行命令应该这样理解:A已经在本地把项目最新的版本做好了,他要发到GitCafe上,以便团队里其他人都能收到这个新的版本,于是他运行git push;push的目的地是origin,这其实是个名字,意义为该项目在GitCafe上的地址;推送的是本地的master分支。

这个时候,GitCafe上项目的版本号与A本地的最新版本号一致。

分支是版本控制里面的一个概念:在项目做大了之后,如果要在原基础上进行扩展开发,最好新建一个分支,以免影响原项目的正常维护,新的分支开发结束后再与原来的项目分支合并;而在一个项目刚开始的时候,大家一般会在同一个分支下进行开发.这是一种相对安全便捷的开发方式。

此时,小组里成员B对项目其他文件做了一些更改,同样也在本地做了一次提交,然后也想推到GitCafe上面。他运行了git push origin master命令,结果发现提交被拒绝。这要做如何解释?

仔细想想,最开始的时候,A和B是在同一个版本号上做不同的更改,这就会分别衍生出两个不同的版本号。A先把自己的版本推到GitCafe上,此时GitCafe上的版本库与B本地版本库相比,差异很大,主要在于B这里没有A的版本记录,如果B这时把自己的版本强制同步到GitCafe上,就会把A的版本覆盖掉,这就出问题了。

所以B进行了如下操作:

1
$ git pull

这样子,B先把GitCafe上的版本库和自己的版本库做一个合并,这个合并的意义在于:B通过GitCafe,把A刚才添加的版本加了进来,此时B本地的版本库是整个项目最新的,包括项目之前的版本、刚刚A添加的版本和B自己添加的版本。

这之后,B再次运行git push origin master,成功地把自己的版本推到了GitCafe上。如果A想要推送新的版本,也要像B之前这样折腾一番才行。

还能再给力点么?

当然可以!

这里有两个在线代码托管平台:

还有一些优秀的Git在线资料:

最后还有一本不错的中文书籍:

不过,本文就要到此为止了,因为再往下写就会涉及更多更深的Git操作实践,而这些内容在上面给出的资料中已经整理好。

最后

本文的目的在于解决身边乃至更多开发者同时入门版本控制和Git时候遇到的困惑,希望能对各位初学者起到积极的作用。

如果您有更好的意见或建议,欢迎与我讨论:)