ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 6.1.2 Git基本原理 ### 6.1.2 Git基本原理 与CVS或者SVN等一般软件不同,要想掌握Git,必须对其实现原理有深入的理解;否则只能停留在表面的简单操作上,一旦遇到复杂问题就无从下手。 了解Git的基本原理需要从Git仓库的数据结构入手。Git把一个项目所有的版本和历史数据保存在一个Git仓库(Repository)里。仓库通常位于项目根目录下的.git子目录,主要包含两种数据:对象存储(object store)和索引(index)。 #### 对象存储(object store) 对象存储(object store)中的对象共有4种类型: - Blob - 一个blob对象对应着项目的一个文件的内容(不包括文件名、创建/修改时间等信息)。项目的每个文件的每个不同版本都对应着一个blob对象。 - Tree - 一个tree对象对应着一层目录结构,每个目录项包括文件名、文件对应的blob对象或者另一个tree对象的引用(可以看出,利用blob和tree对象可以实现一个完整的多层次目录结构)。 - Commit - 一个commit对象保存着用户的一次commit数据,包括:作者信息、提交日期和日志,以及一个指向tree对象的引用(注意commit对象并不包括版本之间的diff差异,后详)。 - Tag - 一个tag对象含有对另一个对象(通常是commit对象)的引用,以及一些附加的注释信息,主要供人类阅读。 举例来说,假设一个项目的目录结构如下: ``` . ├── hello.txt └── sub-dir └── world.txt ``` 同时假设这个项目只进行了一次commit(包含以上两个文件和一个子目录)并且该commit上有一个tag,那么项目对应的Git对象存贮应如下图所示: ![git-object-store](https://box.kancloud.cn/813b7a369239e23774390ec4a386f014_354x453.png) 需要注意的是:我们在前面提到的“对像引用”,如Tree对象代表的目录中每个目录项含有的对blob或者另一个tree对象的引用,commit对象含有的对tree对象的引用,以及tag对象对一个commit对象的引用,不是依赖文件系统的路径来表示的,而是由对象内容得出的SHA1哈希代码,类似`670a245535fe6316eb2316c1103b1a88bb519334`。因此上图可修正如下: ![git-object-store2](https://box.kancloud.cn/814d115737b15510914b47f52e60d98d_400x472.png) 在项目的根目录下执行 ``` find .git/objects/ ``` 可以看到项目的Git仓库中的所有的对象,类似如下: ``` .git/objects/ .git/objects//0d .git/objects//0d/8ad63a4626b776800bbed61c2dac660c5037e2 .git/objects//1d .git/objects//1d/5230840c2573394e0ae86f30e01e03062804fc .git/objects//38 .git/objects//38/8df287b748962a5a1e28a36172fce3240b53e8 .git/objects//4a .git/objects//4a/3ec58d02c75da1d303d55a08cd172508256525 .git/objects//5a .git/objects//5a/e514f1ff6a54b6de97db5418038b4b183d8764 .git/objects//67 .git/objects//67/0a245535fe6316eb2316c1103b1a88bb519334 .git/objects//info .git/objects//pack ``` 它们都是以对象内容的SHA1码来命名的。你还可以通过以下命令查看其内容: ``` git cat-file -p 0d8ad63 ``` 以上`0d8ad63`是`0d8ad63a4626b776800bbed61c2dac660c5037e2`的短前缀——在不引起冲突的情况下可以使用短前缀代替完整的SHA1码。 Git通过对象内容的SHA1哈希码来存贮、引用对象的方式称作**通过内容寻址的存贮(Content-Addressable Storage)**。 现在我们进一步:如果修改了`hellt.txt`的内容并提交一个新的commit,对象存贮如何变化?它变成了这样: ![git-object-store3](https://box.kancloud.cn/32161a7b7dd90234f64f08892f1caf94_544x474.png) 仔细对比一下前后两张图,*Git的奥妙就在这里*: - `hello.txt`的内容改变后,Git新增了一个blob对象,对应着`hello.txt`的新内容,并且有一个新的SHA1代码。 - 由于`hello.txt`的SHA1代码改变了,因此包含这个blob对象的tree对象也发生了变化(因为要更新对`hello.txt`的引用)。同样地,Git也新增了一个tree对象对应着新内容。同时,`sub-dir`的内容没有变化,因此新的tree对象仍然引用着原来的`sub-dir`对象(而不是拷贝一份)。 - 新的commit对象包含这个新的tree对象的引用,同时指向之前的commit对象——这样就形成了历史版本纪录,并且你随时可以恢复任何一个历史版本! #### 索引(index) 根对象存贮相比,索引就简单的多:它不过是一个临时文件,用来存贮你对项目做的一些改变,如增加、删除、修改文件或者目录等。一般地,你使用`git add`、`git rm`等命令把你修改过的文件、目录提交到这个索引中去,然后应用`git commit`命令来完成一次提交。