嵌入式C语言自我修养:从芯片、编译器到操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 代码管理工具:Git

曾几何时,你有没有遇到过这种情况:你正在开发一个51单片机项目,昨天的代码运行得还好好的,今天早上起来,吃了两个包子后,突然灵感乍现,文思泉涌,添加了一些代码,修改了一些参数,重新编译运行,发现硬件不工作了!尤其是和硬件时序、通信协议相关的,很容易遇到这种情况。于是你又想把今天修改的东西改回去,但是改来改去,因为修改的地方太多,你甚至都忘记了到底修改了哪些地方!

吃一堑长一智,在一个坑里跌倒过,下次你肯定不会这么干了。于是你开始步步为营、稳扎稳打:在软件开发过程中,每实现一个功能,每前进一步,都赶紧存档备份,保存为一个版本,然后以这个版本为基点进行下一个版本的开发。客户不停地提需求,改需求,你就不停地备份版本,这就像你在那伤感的6月里写毕业论文一样,你不停地改论文,导师不停地打回来,到最后就变成了图1-2的样子。

图1-2 文档的不同备份版本

不同版本的论文之间到底修改了哪些东西?时间久了,记忆如潮水般退去,可能也就慢慢忘记了。有没有更好的方法去记录这些详细的变化呢?答案是有的,我们可以使用版本控制系统来记录每一次的修改和变化。

1.3.1 什么是版本控制系统

版本控制系统就和各大银行柜台的会计一样,每个客户存入、取出的每一笔钱都记录在账,都有详细记录可查:时间、地点、人物、存取的现金数额,都一一记录在案。版本控制系统也有类似的功能,它会跟踪并记录一个项目中每一个文件的变化:谁创建了它,谁修改了它,又是谁删除了它,是什么时候,修改了什么内容,都一一记录在案。自从有了版本控制系统,工程师之间互相推卸责任的机会大大减少了:你修改了什么,都有详细的记录在案,都保存在版本库中,铁证如山,随便翻一翻就可以查得到。

版本控制系统一般分为集中式版本控制系统和分布式版本控制系统,如图1-3所示。顾名思义,集中式版本控制系统就是软件的各个版本快照只保存在服务器上,服务器中包含各个版本的软件代码。用户如果想要观看某个版本的代码,首先要从版本库中将该版本的代码拉取到本地的计算机上,然后才能查看和修改,最后将自己的修改保存到服务器上。集中式版本控制系统的一个缺点就是数据存储在服务器上,使用时要联网,如果哪一天断网了,就不能工作了,员工就可以提前下班了。如果哪一天某个加班的员工受了委屈,为泄私愤,直接登录服务器删库跑路,如果数据没有备份,那么问题就严重了,基本上就很难恢复了。除此之外,集中式版本控制系统一般都是收费的,所以现在远远没有免费的分布式版本控制系统受欢迎。

图1-3 集中式和分布式版本控制系统

顾名思义,分布式版本控制系统就是不再将整个版本库保存在一个服务器上,而是保存在每个员工的计算机中。这样做的好处是:即使服务器崩溃了,或者离职的员工删除了服务器的代码,只要数据在任何一个员工的计算机中有备份,都可以直接恢复,因为每个计算机保存的版本库数据都是一样的。自从有了分布式版本控制系统,老板再也不怕员工删库跑路了!集中式和分布式版本控制系统典型的代表就是小乌龟和Git,如图1-4所示。

图1-4 集中式和分布式版本控制系统典型的代表

早期使用TortoiseSVN的比较多,自从Linux内核的作者Linus开发出Git这款免费的版本控制工具后,Git变得越来越流行,原先使用TortoiseSVN的软件项目也开始逐渐转向Git。Git逐渐成为软件工程师的一个标配技能,越来越多的公司开始使用Git来管理软件项目,你不会使用Git拉取和提交代码,就无法融入团队参与开发。

1.3.2 Git的安装和配置

安装Git非常简单,以Ubuntu为例,在联网环境下,直接使用下面的命令即可完成安装。

Git安装好之后,还不能立即使用,在使用之前还需要做一些配置,如你提交代码时的一些信息:提交人是谁?提交人的邮箱是多少?如何联系?这些信息是必须要有的,当别人看到你的修改,想和你联系时,可以通过这些配置信息找到你。

Git可以通过不同的参数,灵活设置这些配置的作用范围。

● --global:配置~/.gitconfig文件,对当前用户下的所有仓库有效。

● --system:配置/etc/gitconfig文件,对当前系统下的所有用户有效。

● 无参数:配置.git/config文件,只对当前仓库有效。

1.3.3 Git常用命令

配置完毕后,我们就可以使用Git命令来管理我们的软件代码了,常用的Git命令如下。

● git init:创建一个本地版本仓库。

● git add main.c:将main.c文件的修改变化保存到仓库的暂存区。

● git commit:将保存到暂存区的修改提交到本地仓库。

● git log:查看提交历史。

● git show commit_id:根据提交的ID查看某一个提交的详细信息。

学习Git,首先要明白几个重要的基本概念:工作区(Working Directory)、暂存区(Staging Area)和版本库(Repository)。版本库里保存的是我们提交的多个版本的代码快照,如果你想查看某个版本的代码,可以通过git checkout命令将版本库里这个版本的代码拉取出来,释放到工作区。在工作区,你可以浏览某一个版本的代码、修改代码。如果你想把你的修改保存到版本库中,可以先将你的修改保存到暂存区,接着修改,再保存到暂存区,直到真正完成修改,再统一将暂存区里所有的修改提交到版本库中,如图1-5所示。

图1-5 Git的工作区、暂存区和版本库

这个时候你可能就犯嘀咕了:为什么还需要一个暂存区呢?将工作区的修改直接提交(Commit),保存到版本库中岂不是更方便?其实,笔者也曾思考过这个问题,个人理解是:对于一个版本库来说,你的任何一个提交,包括修改、添加文件、删除文件等操作都会有一个记录,而在实际工作中,对于一个工程师来说,在开发一个功能时,可能会分成很多步,如果每一小步都去提交一次,意义不是很大,而且不是一个完整的功能,别人可能就搞不懂你的提交到底实现了什么功能。如有一个提交:将大象放到冰箱里,别人一看可能就知道是怎么回事。但在实际的开发过程中,我们可能分步开发。

● 打开冰箱门。

● 把大象放到冰箱里。

● 关上冰箱门。

如果将每次很小的修改都做一次提交,就不是很合适,从原则上讲,我们的每一次提交,都是一个里程碑:要么新增了一个功能,要么修改了一个Bug,要么优化了一个功能。在实际开发中的每一小步,都可以先保存到暂存区,等整体功能完成后,再统一提交比较合理。

讲了这么多,为了让大家更快地上手Git,还是给大家演示一遍。首先我们新建一个目录tmp,在tmp目录下新建一个C源文件main.c。

然后在tmp目录下新建一个Git仓库,并将main.c提交到仓库中。

在上面的操作步骤中,每执行一步,我们都可以使用git status命令来查看文件的状态,你会发现每一步操作后,main.c的文件状态都会发生变化:从untracked到changes to be commited,工作区的状态也会跟着变化。提交成功后,我们可以使用git log来查看提交信息,包括提交的ID、提交作者、提交时间、提交信息说明等。

如果提交后你又修改了main.c文件,想把这个修改再次提交到仓库,可以使用下面的命令。

通过上面的命令,我们可以将main.c的第二次修改提交到本地仓库,然后使用git log或者git show命令来查看我们新的提交信息和修改变化。其中git show后面的一串数字字符串是每一次提交的commit ID。

如果你想让你的提交不影响整个项目,不影响其他人使用,则可以创建一个自己的分支my_branch,切换到my_brancn分支上,然后在这个分支上修改代码就可以了。提交时再将你的修改用上面的方法提交到my_branch分支上。通过这种操作,你的所有修改都提交到你自己创建的分支my_branch上,而不会影响master主分支上的代码,不会影响其他人。

掌握上面的常用命令,我们就可以使用Git进行代码的修改、提交了。除了上面的常用命令,Git还有很多其他更好用的命令,如分支管理、分支的合并和衍合、标签管理等,实际使用场景也远比上面的复杂,如远程仓库的代码拉取和提交、合并提交时的代码冲突等。大家有兴趣,可以观看《Linux三剑客》视频教程或者参考相关文档自行学习。