Git 作为一种分布式版本控制系统,和 CVS、SVN 等集中化版本控制系统有显著的区别。本系列文章是《Pro Git》的读书笔记,通过阅读这本书,将学习掌握 Git 的工作原理、使用方法。
关于版本控制
版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。对于程序员而言,通常是对保存着软件源代码的文件作版本控制。但实际上,你可以对任何类型的文件进行版本控制。
接下来将介绍各种类型的版本控制系统(VCS)。
本地版本控制系统
每个人应该都有这样的经历:用复制整个项目目录的方式来保存不同的版本,或许还会加上备份时间以示区别。这么做唯一的好处就是简单,但是特别容易犯错,例如不小心混淆所在的工作目录等。
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
其中最流行的一种就是 RCS。它的工作原理基本上就是保存并管理文件补丁(patch)。文件补丁是一种特定格式的文本文件,记录着对应文件修订前后的内容变化。所以 RCS 可以通过不断应用补丁,计算出各个版本的文件内容。
集中化的版本控制系统
但是接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统(Centralized Version Control Systems,CVCS)应运而生。这类系统,诸如 CVS、Subversion 以及 Perforce 等等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法。
通过 CVCS,每个人都可以在一定程度上看到项目中的其他人正在做些什么。管理员也可以轻松掌控每个开发者的权限,并且管理一个CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。但是这么做最显而易见的缺点是中央服务器的单点故障。
分布式版本控制系统
于是分布式版本控制系统(Distributed Version Control System,DVCS )面世了。在这类系统中,像 Git,Mercurial,Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。通过这种方法,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。
Git 简史
Linux 内核开源项目有着为数众多的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(2002 年之前)。到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 社区免费使用 BitKeeper 的权利。这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统。它们对新的系统制定了若干目标:
- 速度
- 简单的设计
- 对非线性开发模式的强力支持(允许上千个并行开发的分支)
- 完全分布式
- 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
基于这个目标,2005 年 Git 诞生。
Git 是什么
如果你理解了 Git 的思想和基本工作原理,对 Git 的使用就会知其所以然,游刃有余。在学习 Git 时,请尽量理清你对其它版本管理系统已有的认识,如 CVS、Subversion 或 Perforce,这样能帮助你使用工具时避免发生混淆。尽管 Git 用起来与其它的版本控制系统非常相似,但它在对信息的存储和认知方式上却有很大差异,理解这些差异将有助与避免使用中的困惑。
直接记录快照,而非比较差异
Git 和其他版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。从概念上说,其他大部分系统以文件变更列表的方式存储信息。这类系统(CVS、Subversion、Perforce、Bazaar等等)将它们保存的信息看做一组基本文件和每个文件随时间逐步累积的差异(通常称作基于差异的版本控制)。
Git 不按照以上方式对待或保存数据。反之,Git 更像是把数据看做是对小型文件系统的一组快照。在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。为了高效,如果文件没有修改,Git 不再存储该文件,而是只保留一个链接指向之前存储的文件。Git 对待数据更像是一个快照流。
这是 Git 与几乎所有其它版本控制系统的重要区别。Git 更像是一个小型文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的VCS。
近乎所有操作都是本地执行
在 Git 中,绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。因为你在本地磁盘上有项目的完整历史,所有大部分操作看起来瞬间完成。
Git 保证完整性
Git 中所有的数据在存储前都计算校验和,然后以校验和来引用。这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。这个功能构建在 Git 底层,是构成 Git 哲学不可或缺的部分。若你在传送过程中丢失信息或损坏文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1 散列(哈希,hash)。这是一个由 40 个十六进制字符组成的字符串,基于 Git 中文件的内容或目录结构计算出来。
实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
Git 一般只添加数据
你执行的 Git 操作,几乎只往 Git 数据库中添加数据。你很难让 Git 执行任何不可逆操作,或者让它以任何方式清除数据。同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容。但是一旦你提交快照到 Git 中, 就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
三种状态
Git 有三种状态,你的文件可能处于其中之一:已修改(modified)、已暂存(staged)和已提交(committed):
- 已修改表示修改了文件,但还没保存到数据库中
- 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
- 已提交表示数据已经安全的保存在本地数据库中
这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录:
- 工作区是对项目的某个版本独立提取出来的内容。这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改
- 暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。按照 Git 的术语叫做
索引
,不过一般说法还是叫暂存区
- Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据
基本的 Git 工作流程如下:
- 在工作区中修改文件
- 将你下次想要提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区
- 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录
命令行
Git 有多种使用方式。你可以使用原生的命令行模式,也可以使用 GUI 模式,这些 GUI 软件也能提供多种功能。但是,只有在命令行模式下你才能执行 Git 的所有命令。而且不同的人常常会安装不同的 GUI 软件,但所有人一定会有命令行工具。
安装 Git
在开始使用 Git 前,需要将它安装在计算机上。即便已经安装,最好将它升级到最新的版本。可以通过软件包或者其它安装程序来安装 Git,或者下载源码编译安装。
在 Linux 上安装
如果想在 Linux 上用二进制安装程序来安装基本的 Git 工具,可以使用发行版包含的基础软件包管理工具来安装。
在 Windows 上安装
在 Window 上安装 Git 有几种安装方法。官方版本可以在 Git 官方网站上下载。要注意这是一个名为 Git for Windows 的项目(也叫做 msysGit),和 Git 是分别独立的项目。
另一个简单方法是安装 GitHub Desktop。该安装程序包含图形化和命令行版本的 Git。它也能支持 PowerShell,提供了稳定的凭证缓存和健全的换行设置。
从源码安装 Git
有人觉得从源码安装 Git 更实用,因为你能得到最新的版本。二进制安装程序可能有些滞后,当然近几年 Git 已经成熟,这个差异不再显著。
如果你想源码安装Git,需要安装 Git 依赖的库:autotools、curl、zlib、openssl、expat 和 libiconv。当你安装好所有的必要依赖,你可以继续从几个地方来取得最新发布版本的 tar 包。 接着编译并安装。
完成后,你可以使用 Git 来获取 Git 的更新:
1 | git clone git://git.kernel.org/pub/scm/git/git.git |
初次运行 Git 前的配置
在系统上安装 Git之后,需要做几件事来定制你的 Git 环境。每台计算机上只需要配置一次,程序升级时会保留配置信息。你可以在任何时候再次通过运行命令来修改它们。
Git 自带一个 git config
工具来帮助设置控制 Git 外观和行为的配置变量。这些变量存储在三个不同的位置:
- /etc/gitconfig 文件:包含系统上每一个用户及他们仓库的通用配置。如果在执行
git config
时带上--system
选项,那么它就会读写该文件中的配置变量。由于它是系统配置文件,因此需要超级用户权限来修改它 - ~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。你可以传递 –global 选项让 Git 读写此文件,这会对你系统上所有的仓库生效
- 当前使用仓库的 Git 目录中的 config 文件(即 .git/config):针对该仓库。你可以传递 –local 选项让 Git 强制读写此文件,虽然默认情况下用的就是它
每一个级别会覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。
可以通过以下命令查看所有的配置以及他们所在的文件:
1 | $ git config --list --show-origin |
用户信息
当安装完 Git 之后,应该做的第一件事就是设置你的用户名称和邮件地址。这一点很重要,因为每一个 Git 的提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
1 | git config --global user.name "fuchencong" |
如果使用了 –global 选项,那么该命令只需要运行一次,因为之后无论你在该系统中做任何事情,Git 都会使用那些信息。当你想针对特定项目使用不同的客户名称与邮件地址时,可以在那个项目目录下运行没有 –global 选项的命令来配置。
很多 GUI 工具都会在第一次运行时帮助你配置这些信息。
文本编辑器
可以配置默认文本编辑器,当 Git 需要你输入信息时会调用它。如果未配置,Git 会使用操作系统默认的文本编辑器。如下命令将默认文本编辑器设置为 Vim:
1 | git config --global core.editor vim |
检查配置信息
如果你想要检查你的配置,可以使用 git config --list
命令来列出所有 Git 当时能找到的配置。你可能会看到重复的变量名,因为 Git 会从不同的文件中读取同一个配置,例如 /etc/gitconfig 与 ~/.gitconfig。这种情况下,Git 会使用它找到的每一个变量的最后一个配置。
可以通过 git config
1 | $ git config user.name |
由于 Git 会从多个文件中读取同一配置变量的不同值,因此你可能会在其中看到意料之外的值而不知道原因。 此时,你可以查询 Git 中该变量的 原始
值,它会告诉你哪一个配置文件最后设置了该值:
1 | $ git config --show-origin user.name |
获取帮助
当你使用 Git 时,如果需要获取帮助,有三种方法可以找到 Git 命令的使用手册:
1 | $ git help <verb> |
另外,如果你不需要全面的手册,只需要可用选项的快速参考,那么可以用 -h 选项获取更简明的 help 输出。