关于版本控制
版本控制是一种记录一个或若干个文件内容变化,以便将来查阅特定版本修订情况的系统。
如果你是位图形或网页设计师,可能会需要保存某一幅图片或者页面布局文件的所有修订版本,采用版本控制系统(VCS)是个明智的选择。有了它你就可以将选定的文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致问题出现的原因,又是谁在何时报告了某个功能缺陷等等。使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子,但额外增加的工作量却微乎其微。
本地版本控制系统
许多人习惯用赋值整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。这么做唯一的好处就是简单,但是特别容易犯错。有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
其中最流行的一种叫做RCS,现今许多计算机系统上还能看到它的踪影。RCS的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。
集中化的版本控制系统
接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。这类系统,诸如CVS、Subversion以及Perforce等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法。
这种做法带来了许多好处,特别是相较于老式的本地VCS来说。现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么,而管理员也可以轻松掌控每个开发者的权限,并且管理一个CVS要比在各个客户端上维护本地数据库来得轻松容易。
但是,这么做最显而易见的缺点是中央服务器的单点故障。如果宕机一小时,那么在这一个小时内,谁都无法提交更新,也就无法协同工作。如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据—包括项目的整个变更历史,只剩下人们在各自机器上的单独快照。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
分布式版本控制系统
于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。在这类系统中,像Git、Mercurial、Bazaar以及Darcs等,客户端并不只是提取最新版本的文件快照,而是把代码仓库完整地映像下来,包括完整的历史记录。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
更近一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。
Git诞生
同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。
Linux 内核开源项目有着为数众多的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统。
自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。 它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统。
对Git进行总结,就是以下三点:
- Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源代码的版本控制软件。
- Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
- Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。
Git是什么?
直接记录快照,而非差异比较
Git和其它版本控制系统主要差别在于Git对待数据的方式。从概念上来说,其他大部分系统(包括 Subversion 和近似工具)以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce、Bazaar 等等),将它们存储的信息看作是一组基本文件和每个文件随时间逐步积累的差异(它们通常称作**基于差异(delta-based)**的版本控制)。
存储每个文件与初始版本的差异
Git不按照以上方式对待或保存数据。反之,Git更像是把数据看作是对小型文件系统的一系列快照。在Git中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。为了效率,如果文件没有修改,Git不再重新存储该文件,而是只保留一个链接指向之前存储的文件。Git对待数据更像是一个快照流。
存储项目随时间改变的快照
这是Git与几乎所有版本控制系统的重要区别。因此Git重新考虑了以前每一代版本控制系统延续下来的诸多方面。Git更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的VCS。
近乎所有操作都是本地执行
在Git中的绝大多是操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git在这方面会让你感觉到速度之神赐给了Git超凡的力量。因为你在本地磁盘上就有项目的完整历史,所以绝大部分操作看起来瞬间完成。
举个例子,要浏览项目的历史,Git不需要外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。你能立即看到项目历史。如果你想查看当前版本与一个月前的版本之间引入的修改,Git会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。
这也意味着你在离线或者没有VPN时,几乎可以进行任何操作。如你在飞机或火车上想做些工作,就能愉快地提交(到你的本地副本),直到有网络连接时再上传。使用其他系统地话,做到这些时不可能或很费力地。比如,用Perforce的话,没有连接到服务器时几乎不能做什么事了;而用Subversion和CVS的话,你能修改文件,但不能向数据库提交修改(因为你的本地数据库离线了)。
Git保证完整性
Git中所有的数据在存储前都计算校验和,然后以校验和来引用。这意味着不可能在Git不知情时更改文件内容或目录内容。这个功能建构在Git底层,是构成Gi哲学不可或缺的部分。若你在传送过程中丢失信息或损坏文件,Git就能发现。
Git用以计算校验和的机制叫做SHA-1散列(hash,哈希)。这是一个由40个十六进制字符组成的字符串,基于Git中文件的内容或目录结构计算出来。SHA-1哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
Git中使用这种哈希值的情况很多,你将经常看到这种哈希值。实际上,Git数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
Git一般只添加数据
你执行的Git操作,几乎只往Git数据库中添加数据,你很难使用Git从数据库中删除数据,也就是说Git几乎不会执行任何可能导致文件不可恢复的操作。同别的VCS一样,未提交更新时有可能丢失或弄乱修改的内容,但是一旦你提交快照到Git中,就难以再丢失数据,特别是如果你定期的推送数据到其它仓库的话。
这使得我们使用Git成为一个安心愉悦的过程,因为我们深知可以尽情做各种尝试,而没有把事情弄糟的危险。
三种状态
Git有三种状态,文件可能处于其中之一:已提交(committed)、已修改(modified)、和已暂存(staged)。
- 已修改表示修改了文件,但还没保存到数据库中。
- 已暂存表示对一个已修改文件的当前版本了标记,使之包含在下次提交的快照中。
- 已提交表示数据已经安全地保存在本地数据库中。
这会让我们的Git项目拥有三个阶段:工作区、暂存区以及Git目录
工作区是对项目的某个版本独立提取出来的内容。这些从Git仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在Git仓库目录中。按照Git的术语叫做“索引”,不过一般说法还是“暂存区”。
Git仓库目录是Git用来保存项目的元数据和对象数据库的地方。这是Git最重要的部分,从其他计算机克隆仓库时,复制的就是这里的数据。
基本的Git工作流程如下:
- 在工作区修改文件
- 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
- 提交更新,找到暂存区的文件,将快照永久性存储到Git目录。
如果 Git 目录中保存着特定版本的文件,就属于 已提交 状态。 如果文件已修改并放入暂存区,就属于 已暂存 状态。 如果自上次检出后,作了修改但还没有放到暂存区域,就是 已修改 状态。