• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • Git 工作原理

    那么,简单地说,Git 究竟是怎样的一个系统呢?请注意,接下来的内容非常重要,若是理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。在开始学习 Git 的时候,请不要尝试把各种概念和其他版本控制系统(诸如 Subversion 和 Perforce 等)相比拟,否则容易混淆每个操作的实际意义。Git 在保存和处理各种信息的时候,虽然操作起来的命令形式非常相近,但它与其他版本控制系统的做法颇为不同。理解这些差异将有助于你准确地使用 Git 提供的各种工具。


    直接记录快照,而非差异比较

    Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。


    Git 保存每次更新时的文件快照:


    这是 Git 同其他系统的重要区别。它完全颠覆了传统版本控制的套路,并对各个环节的实现方式作了新的设计。Git 更像是个小型的文件系统,但它同时还提供了许多以此为基础的超强工具,而不只是一个简单的 VCS。稍后在第三章讨论 Git 分支管理的时候,我们会再看看这样的设计究竟会带来哪些好处。


    近乎所有操作都是本地执行

    在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。但如果用 CVCS 的话,差不多所有操作都需要连接网络。因为 Git 在本地磁盘上就保存着所有当前项目的历史更新,所以处理起来速度飞快。

    举个例子,如果要浏览项目的历史更新摘要,Git 不用跑到外面的服务器上去取数据回来,而直接从本地数据库读取后展示给你看。所以任何时候你都可以马上翻阅,无需等待。如果想要看当前版本的文件和一个月前的版本之间有何差异,Git 会取出一个月前的快照和当前文件作一次差异运算,而不用请求远程服务器来做这件事,或是把老版本的文件拉到本地来作比较。

    用 CVCS 的话,没有网络或者断开 VPN 你就无法做任何事情。但用 Git 的话,就算你在飞机或者火车上,都可以非常愉快地频繁提交更新,等到了有网络的时候再上传到远程仓库。同样,在回家的路上,不用连接 VPN 你也可以继续工作。换作其他版本控制系统,这么做几乎不可能,抑或非常麻烦。比如 Perforce,如果不连到服务器,几乎什么都做不了(译注:默认无法发出命令 p4 edit file 开始编辑文件,因为 Perforce 需要联网通知系统声明该文件正在被谁修订。但实际上手工修改文件权限可以绕过这个限制,只是完成后还是无法提交更新。);如果是 Subversion 或 CVS,虽然可以编辑文件,但无法提交更新,因为数据库在网络上。看上去好像这些都不是什么大问题,但实际体验过之后,你就会惊喜地发现,这其实是会带来很大不同的。


    时刻保持数据完整性

    在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为 Git 的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。

    Git 使用 SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个 SHA-1 哈希值,作为指纹字符串。该字串由 40 个十六进制字符(0-9 及 a-f)组成,看起来就像是:

    24b9da6552252987aa493b52f8696cd6d3b00373

    Git 的工作完全依赖于这类指纹字串,所以你会经常看到这样的哈希值。实际上,所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。


    多数操作仅添加数据

    常用的 Git 操作大多仅仅是把数据添加到数据库。因为任何一种不可逆的操作,比如删除数据,都会使回退或重现历史版本变得困难重重。在别的 VCS 中,若还未提交更新,就有可能丢失或者混淆一些修改的内容,但在 Git 里,一旦提交快照之后就完全不用担心丢失数据,特别是养成定期推送到其他仓库的习惯的话。

    这种高可靠性令我们的开发工作安心不少,尽管去做各种试验性的尝试好了,再怎样也不会弄丢数据。至于 Git 内部究竟是如何保存和恢复数据的,我们会在第九章讨论 Git 内部原理时再作详述。


    Git 四个工作区域

    从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 Git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。每个项目都有一个 Git 目录(.git目录),它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。

    Git总共有 4 个区域:

    • 工作目录(Working Directory):本地项目存放项目代码的地方。也叫工作区(Working Workspace)。
    • 暂存区(Stage/Index):存放临时的改动,事实上它只是一个文件,保存即将提交到文件列表信息。在工作目录下有一个.git的目录,里面有个index文件,存储着关于暂存区的内容。git add命令将工作区内容添加到暂存区。
    • 本地仓库(Repository):本地版本控制系统的仓库。安全存放数据的位置,这里面有提交到所有版本的数据。其中 HEAD 指向最新放入仓库的版本。在工作目录下有一个.git的目录,这个目录下的内容,便是仓库的数据信息。其中index文件,是暂存区。
    • 远程仓库(Remote Directory):托管代码的服务器,可以简单的认为是项目组中的一台电脑,用于远程数据交换。

    项目中新建、修改的文件都在工作区,都需要先git add到暂存区,再git commit到本地仓库,再git push到远程仓库。



    文件的状态

    Git 工作基本流程如下:

    1. 在工作目录中修改某些文件。
    2. 对修改后的文件进行快照,然后保存到暂存区域。
    3. 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。

    对于任何一个文件,每一个文件共有四种状态:

    • 未跟踪(Untracked):新建文件:状态是未跟踪(Untracked)。
    • 已暂存(staged):使用git add命令,将新建的文件加入到暂存区:状态是已暂存(staged)。
    • 已提交(committed):使用git commit命令,将暂存区的文件提交到本地仓库:状态是已提交(committed)。
    • 已修改(modified):如果对已提交(committed)状态的文件进行修改:状态是已修改(modified)。如果对已提交(committed)状态的文件进行remove操作:状态是未跟踪(Untracked)。

    由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录、暂存区域、本地仓库。所以,我们可以从文件所处的位置来判断状态:如果是 Git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。



    • 未跟踪(Untracked):文件在项目工作目录中,新创建的文件,那么它还没进入版本管理中,处于未跟踪的状态。使用命令git add,提交到暂存区,状态变为已暂存(staged)。
    • 已暂存(staged):文件已经加入到版本管理中。表示把已修改的文件放在下次提交时要保存的清单中。
      • 经过提交git commit,进入本地仓库中,这时库中的文件和本地文件又变为一致,状态变成已提交(committed)。
      • 经过重置git reset HEAD filename,取消暂存,文件状态为已修改(modified)。
      • 如果它被修改,而变为已修改(modified)。
      • 如果使用git rm --cached,从暂存区域移除,则成为未跟踪(Untracked)文件。
    • 已提交(committed):文件已经加入到版本管理中,加入到 Git 本地仓库中。表示该文件已经被安全地保存在本地数据库中了。
    • 已修改(modified):文件已经加入到版本管理中。文件已修改,仅仅是修改,并没有进行其他的操作。这个文件也有两个去处:
      • 经过提交git add,进入暂存区,状态变成已暂存(staged)。
      • 经过切出git checkout,则丢弃修改过,状态返回到未修改(Unmodify)。这个git checkout即从库中取出文件,覆盖当前修改。

    上篇:什么是 Git?

    下篇:Git 安装