git对文件的处理
文件File
任何文件对计算机而言都为一系列的二进制数,文件的内容通常我们使用字节流来表示它,1字节 = 8bit(二进制位)。
而文件的元数据通常包括: 文件名、文件大小、文件内容、访问权限、修改时间等等。
文件版本
文件的版本指的是当文件在某一时间进行的修改所记录的文件状态信息,具体如何记录、记录什么内容视具体的版本控制软件而定。
假设有一个文件其初始内容为f1,下一个版本文件的内容变成f2,它们之间内容的差异记为△f,如下图所示
实现文件版本的记录无非两种方式:
1.记录文件不同版本间的差异,如果需要获取某个版本的文件,将该差异信息取出(迭代取出)通过算法与原文件进行合并,这就是SVN
的做法
2.每次对文件的修改都为文件做一份快照,即保存一份新的文件,如果需要取某个版本的文件,只需将该版本的快照文件直接取出即可,这就是Git
的做法
hash算法
通过hash
算法可以将一个文件的内容转换成一个固定长度且不重复的字节序列,从而唯一标识一个文件。
目前主流的hash算法有两种:
MD5
一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值,该算法已经被清华大学的某教授破解
SHA1
该算法会产生20个字节数组,最终得到40个字符长度的字符串,git所采用的就是该算法,将该hash值作为文件的指纹。通过hash值来引用一个文件,一旦文件内容发生了变化,那么生成的hash值就不同了。
文件系统
GIT作者曾提及git其实就是一个微型的文件系统,它是基于内容寻址的,内容的标识/依据就是通过hash
算法得到的一个值,也称为索引。通常在windows文件系统中,同目录下同样内容的文件可以存在并复制很多份,只要文件名不同即可。
而在git的文件系统中,相同内容的文件则只会保留一份,实际上是只保存1份文件内容和两个指向该内容的指针。
演示git对文件的处理
- 新建一个目录test,然后使用
git init
命令初始化一个git仓库,使用vim 或者直接在目录下创建一个readme.txt文件,写入内容“welcome to study git!”,使用git add
命令将文件添加到git暂存区,使用find .git/objects -type f
命令查看git对象,此时只有一个对象,该对象就是readme文件的引用,注意输出内容中的fa/bla...代表对象的hash值,git将hash值的前两个字符作为目录名。
使用cp
命令将文件复制一份,再次添加到git暂存区,此时git对象组中还是一个对象
- 使用
git commit
命令提交两个文件,使用git show hash值
可以查看指定git对象的内容,使用git hash-object file
查看两个文件的hash值,发现hash值完全一致,然后再次使用find
命令查看git对象,此时应该有3个,使用git ls-tree hash值
查看其中一个对象中的信息,该对象就保存了两个文件名及其引用文件内容的hash值等信息
GIT的三个区域
Repository
本地仓库,通过 git init
命令创建一个本地仓库,表现形式即为.git目录,它是 Git 用来保存元数据和对象数据库的地方。
该目录非常重要,每次通过 git clone
克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
Working Directory
工作目录,从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录,这些文件实际上都是从 Git 目录中的压缩对象数据库中提取出来的,工作目录的文件一旦提交到git仓库,那么该文件即使丢失或者删除,依然可以从git仓库中重新检出。
Staging area/Index
暂存区域,所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件。
执行 git add
操作会生成一个index
文件(如果文件已存在则添加内容),该文件就代表了暂存区域。
区域示意图
物理文件图
Git目录
执行 git init
命令就会生成一个.git目录,该目录初始状态如下
config
文件包含了项目特有的配置选项
description
文件仅供 GitWeb 程序使用,所以不用关心这些内容
info
目录保存了一份不希望在.gitignore
文件中管理的忽略模式 (ignored patterns) 的全局可执行文件
hooks
目录保存了客户端或服务端钩子脚本
四个重要的文件或目录:
HEAD 及 index 文件, objects 及 refs 目录。这些是 Git 的核心部分。
objects
目录存储所有数据内容
refs
目录存储指向数据 (分支) 的提交对象的指针
HEAD
文件指向当前活动分支
index
文件保存了暂存区域信息
网友评论