Git目录结构(git bash中执行git init)
- Objects:
存储对象的目录,本地仓库,git中对象分为三种:commit对象,tree对象(多叉树),blob对象;
- Refs:
存储指向branch的最近一次commit对象的指针,也就是commit对象的sha-1值(就是hash值,sha-1是一种散列算法),refs的目录下包括以下目录(git init后并没有remotes和stash,需要有从remote地址中pull code等交互性操作才会出现remotes目录,stash文件则是做了stash操作才会出现):
heads是存储本地仓库每一个分支最近一次commit对象的sha-1值;
remotes则是存储最近一次push到远程仓库的commit对象的sha-1值;
tags(首先要与branch区分开来,tags本质就是branch,与branch不同的是tag是不会有改动的,是历史版本记录)则是存储历史版本的最后一次commit对象的sha-1值
stash存储藏匿处(就是GitExtentsions中的stash操作)的对象sha-1值;
- HEAD文件:
该文件表示当前本地签出的分支,例如存储的值:ref: refs/heads/master;
- Index文件:
存储缓冲区(GitExtensions中的stage)的内容,内容包括它指向的文件的时间戳、文件名、sha1值等;(git三大区域:工作区,缓冲区,历史记录区,如下图)
(可对比Git Extensions的操作进行理解)
Git对象
git是一套内容寻址系统,它是怎样寻址的呢?Git从核心上来看不过是简单地存储键值对(key-value)(hashmap),大概结构如下:
Key=sha-1(file header + file content)
Value=压缩(file content)
Key是一个40位字符的校验和,前2位作为子目录,后38位作为文件名保存在子目录下。
在了解git对象前,先了解学习下git的底层命令。Git分为porcelain命令和plumbing命令,前者是基于后者实现的,porcelain是更高层的命令,举个例子:例如git add, git commit, git push, git pull等命令,而plumbing命令是底层的命令。下面通过plumbing命令进行演示提交。
blob对象 (Plumbing命令演示整个commit过程, 以此理解三大对象)
对象创建:
如何创建一个blob对象呢?
$ echo “version 1” >test.txt
$ git hash-object -w test.txt =》hash-object创建对象,-w表示存储对象,write
$ echo “version 2” >test.txt
$ git hash-object -w test.txt
如何查看创建后的blob对象内容?
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
=>cat-file命令可将内容取出来,然后放到test.txt文件中,类比linux的cat命令,版本恢复是通过这种方式做到的,根据不同的key进行内容获取,如上面所说内容放在objects目录下;
如何查看对象的类型?
$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
=> -t可以让git返回任何对象类型
对象加入缓冲区:
如何将blob对象加入缓冲区?
git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt
update-index会把对象放到缓冲区/暂存区域,即索引库. 该命令表示将test.txt指定的版本加入到缓冲区/暂存区域, key: 83baae61804e65cc73a7201a72 52750c76066a30就是它的版本。注意,这里一定要带上—add选项,因为由于该文件原先并不在暂存区域中 (甚至就连暂存区域也还没被创建出来呢) ,必须传入 --add 参数,而—cacheinfo选项则指出该文件的文件类型,100644表示普通文件.这个时候可以看看Git Extensions的stage区域以及index文件内容。
Git add命令
Git add = git hash-object + git update-index(相当于Git Extensions的add stage操作)
Tree对象
在Git中,所有的内容以tree或者BLOB对象进行存储。可以与linux系统进行类比,tree相当于目录,blob相当于文件内容。一个tree对象可以包含blob对象以及子tree对象。Tree对象是什么时候创建的呢?它的创建介于add和commit命令之间。为什么要创建tree?因为commit对象其实就是指向tree对象的,表明该次commit所对应的tree有哪些内容。
创建tree对象命令:
git write-tree =>它会将缓冲区的内容写成tree对象
把version 1的test.txt加入到缓冲区并且写成tree后,结构如下:
把version 2的test.txt加入到缓冲区:
$ git update-index --add --cacheinfo 100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git write-tree
Commit对象
现在有两个tree对象,它们指向了你要跟踪的项目的不同快照,可是先前的问题依然存在:必须记往三个SHA-1 值以获得这些快照。你也没有关于谁、何时以及为何保存了这些快照的信息。commit对象为你保存了这些基本信息。
如何创建commit对象?
git commit-tree key =>需要指定key,例如上面的两个版本,第一个版本和第二个版本的test.txt的key是不一样的;
echo "first commit" | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
如何指定commit对象的parent: 通过-p ?
echo "second commit" | git commit-tree 2f39845a4a2c3ad86adebb00b1ddabd959c131c4 -p 364118
结构图如下:
最后执行git commit -m “test”就可以提交了。
Git官网:
https://git-scm.com/book/zh/v1/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1