前言
Git 这个词相信大家并不陌生,做开发的童鞋们每天都离不开它,当然,如果你的项目中没有用到分布式,那么,你可能从未用过 Git,当然也可能没听过。不过,这不是重点,重点是这一篇文章,我们将一起谈谈 Git,从宏观上来认识 Git。
一点序
在写这篇文章之前,就一直在想该如何定位呢?是写一篇基础一些的使用教程,还是从宏观上介绍一下 Git 的机制呢?由于最近时间很紧,没有那么多的空余时间,因此,思来想去决定写一篇宏观上的介绍,至于基础教程嘛,网上已经有很多了,我都看了看,写的都比我好,因此我也就没有必要再误人子弟了。
鉴于此,我的定位是,这篇文章的主要内容是在宏观上谈谈 Git 给我们带来的改变,以及 Git 的内部实现,或者说是机制吧。最后,把自己在项目中使用 Git 的感受跟大家分享一下。
版本控制
什么是版本控制?我为什么要关心它呢?我想我问的这个问题有点小傻了。有过开发的童鞋们这点就不用说了,如果你真的不知道,那么去 Baidu 一下吧。那么,你知道版本控制系统有几种吗?你考虑过为什么公司里用的是 Git 而不用 SVN 呢?或者用的是 SVN 而不用 Git 呢?先看看他们之间的区别吧。
- 本地版本控制系统
还记得在很早以前,我们要备份文件、备份数据的时候,我们是怎么做的吗?反正我的做法就是,复制整个文件(文件夹),拷贝到另一块硬盘,或者网盘。然后自己给他们命名。这是一种纯手工的 job 。大家都知道,只要是人工的重复性劳动,出错的几率会大大增加的。然而,为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
其中最流行的一种叫做 rcs(Revision Control System),现今许多计算机系统上都还看得到它的踪影。甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。它的工作原理基本上就是保存并管理文件补丁(patch)。文件补丁是一种特定格式的文本文件,记录着对应文件修订前后的内容变化。所以,根据每次修订后的补丁,rcs 可以通过不断打补丁,计算出各个版本的文件内容。
- 集中化版本控制系统
rcs 的出现大大的方便了人们对历史文件(数据)的控制和维护。然而,不久之后,又一个问题出现了。操作系统的差异,导致开发人员无法进行协同工作,尤其是在版本控制方面。聪明的 Human 找到了解决的办法,就这样,集中式版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。
像这类系统,诸如 CVS,Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法。而且在企业开发中,集中式的版本控制系统也是最常见的、最普遍的。
这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。
古人有一句老话,叫做“福兮祸之所伏,祸兮福之所倚”。他是说,任何事物都有两面性,不存在绝对的好与坏,只不过是一个相对的比较罢了。我们要追求的就是一种平衡,或者说对我们来说,利大于弊的方面而已。似乎有点跑题啊。。。回到正题, 可以看到,集中式版本控制系统,最显而易见的缺点就是中央服务器的单点故障。如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。
要是中央服务器的磁盘发生故障,碰巧没做备份,或者备份不够及时,就会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,而被客户端偶然提取出来的保存在本地的某些快照数据就成了恢复数据的希望。但这样的话依然是个问题,你不能保证所有的数据都已经有人事先完整提取出来过。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
- 分布式版本控制系统
为什么说人类总是最聪明的呢?人类总是能解决遇到的难题,这次也不例外。基于上述的问题,分布式版本控制系统(Distributed Version Control System,简称 DVCS )面世了。在这类系统中,像 Git,Mercurial,Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份。
更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。
思想
那么,简单地说,Git 究竟是怎样的一个系统呢?让我们来看看他映射出的思想。
- 直接记录快照,而非差异比较
Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。
Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。
- 近乎所有操作都是本地执行
在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。但如果用 CVCS 的话,差不多所有操作都需要连接网络。因为 Git 在本地磁盘上就保存着所有当前项目的历史更新,所以处理起来速度飞快。
举个例子,如果要浏览项目的历史更新摘要,Git 不用跑到外面的服务器上去取数据回来,而直接从本地数据库读取后展示给你看。所以任何时候你都可以马上翻阅,无需等待。如果想要看当前版本的文件和一个月前的版本之间有何差异,Git 会取出一个月前的快照和当前文件作一次差异运算,而不用请求远程服务器来做这件事,或是把老版本的文件拉到本地来作比较。
用 CVCS 的话,没有网络或者断开 VPN 你就无法做任何事情。但用 Git 的话,就算你在飞机或者火车上,都可以非常愉快地频繁提交更新,等到了有网络的时候再上传到远程仓库。同样,在回家的路上,不用连接 VPN 你也可以继续工作。
- 时刻保持数据完整性
在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为 Git 的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。
Git 使用 SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个 SHA-1 哈希值,作为指纹字符串。该字串由 40 个十六进制字符(0-9 及 a-f)组成。
Git 的工作完全依赖于这类指纹字串,所以你会经常看到这样的哈希值。实际上,所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。
- 多数操作仅添加数据
常用的 Git 操作大多仅仅是把数据添加到数据库。因为任何一种不可逆的操作,比如删除数据,都会使回退或重现历史版本变得困难重重。在别的 VCS 中,若还未提交更新,就有可能丢失或者混淆一些修改的内容,但在 Git 里,一旦提交快照之后就完全不用担心丢失数据,特别是养成定期推送到其他仓库的习惯的话。
- 文件的三种状态
对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。
由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录,暂存区域,以及本地仓库。
基本的 Git 工作流程如下:
- 在工作目录中修改某些文件。
- 对修改后的文件进行快照,然后保存到暂存区域。
- 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。
所以,我们可以从文件所处的位置来判断状态:如果是 Git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。
感受
由于 Git 是在 Linux 下开发的,主要是作为开源系统 Linux 下的分布式版本控制系统。不过,由于一些大神的移植,使得 Windows 下的用户也可以使用。不过,对于兼容性来说,Windows 环境下的模拟 Git 还是稍微差一点的,但这并不意味着 Windows 下 Git 的功能缺失。只不过在 Windows 下,更容易出一些让人无法预料的问题。当然,这些问题还是可以解决的,就是有点麻烦而已。
比如,在 Windows 下的文件的权限因为无法和linux上完全一致,所以用Git检出的文件权限可能显示为被更改。 另外因为 Windows 下的换行和 Linux 上也不一样,协作开发时也容易出问题。所以在 Windows 上使用 Git 的同学需要加上以下2行配置参数:
git config --global core.filemode false
git config --global core.autocrlf true
第一句是忽略文件权限的改动。
第二句是将文件checkout时自动把LF转成CRLF,check in 时自动把CRLF转成LF
当然,如果在 Windows 平台上开发,建议使用图形界面的 TortoiseGit 客户端,而在 Java IDE 下也有相应的 Git 插件,比如在 Eclipse 下,可以通过安装插件的方式,安装 Git 。而在 IntelliJ IDEA 中,则默认安装了此插件,只要在设置中勾选激活,然后进行配置即可。
至于如何使用 Git,这里就不再多说了,网上有大把大把的教程。这里推荐一篇文章,讲的是如何在 Eclipse 下使用 Git,文章地址,讲的还是挺详细的,有需要的自己去看一下。
结束语
这篇文章主要讲了 Git 宏观上的一些东西,还有 Git 的基本原理。致力于让大家对 Git 有个基本的了解。当然,这是建立在使用过版本控制系统的基础之上的。而且,也不必纠结于到底使用 SVN 还是使用 Git 的问题上。个人觉得,如果是为了学习,那么最好是都了解一下。如果公司里有要求,那么就按照公司里的要求即可。没必要两款都使用,根据公司里具体的情况而定。如果,你以后打算向架构师的方向发展的话,那么就要多思考一些了,各种技术、各种工具的优势、劣势,你必须了然于胸,而且,这也是最基本的。
参考: