[!quote] 如果是兴趣的角度推荐学一下,但是功利的角度就算了
基本说明
- git 和svn的方式不同,主要使用文件快照的形式储存,更加快速索引
- git 使用sha1生成hash,使用zlib的deflate算法压缩文件
- 具有相同hash值的文件,git认为为同一个文件,只生成一份obj
目录分析
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ ├── feat
│ │ └── example
│ └── master
├── objects
│ ├── 18
│ │ └── 605c522c56ea4ebbf2ee17ae702c38e89f37e2
│ ├── 21
│ │ └── c8f9899198b747f5a44bc5f837a15dbe8de639
│ ├── 30
│ │ └── 532454c17b108960baaa779156d4ed06f4700a
│ ├── af
│ │ └── 81445cd6401f394045651a876e9196db61e912
│ ├── e5
│ │ └── 3be081fdea9000576336d0d0dee7b326915ff2
│ ├── info
│ └── pack
└── refs
├── heads
│ ├── feat
│ │ └── example
│ └── master
├── remotes
│ └── origin
│ ├── HEAD
│ └── main
└── tags
└── v0.0.1
description
- 暂时没发现有什么用(似乎和gitweb有关)
branches
- 暂时没发现有什么用
COMMIT_EDITMSG
- 最近的commit 的msg信息,没什么用
info/exclude
- 和.gitignore类似,用于忽略文件
区别在于这个文件不会被提交,一般用于本地忽略文件
hooks
- 一些git的脚本,可以自己进行编写,在对应的时机调用,默认不使用,需要使用去除文件后缀
config
- 配置文件,有关项目git的配置都在这里
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = git@github.com:chenxuan520/gitlearn.git
fetch = +refs/heads/*:refs/remotes/origin/*
HEAD
- 用于指示目前所在的分支以及位置
ref: refs/heads/master
logs
HEAD
- HEAD 记录所有更改记录,包括切换分支
- reflog 查看的位置
0000000000000000000000000000000000000000 21c8f9899198b747f5a44bc5f837a15dbe8de639 chenxuan <1607772321@qq.com> 1668685183 +0800commit (initial): init
21c8f9899198b747f5a44bc5f837a15dbe8de639 21c8f9899198b747f5a44bc5f837a15dbe8de639 chenxuan <1607772321@qq.com> 1668685269 +0800checkout: moving from master to feat/example
21c8f9899198b747f5a44bc5f837a15dbe8de639 21c8f9899198b747f5a44bc5f837a15dbe8de639 chenxuan <1607772321@qq.com> 1668686611 +0800checkout: moving from feat/example to master
refs
- 对该分支的变化操作记录
0000000000000000000000000000000000000000 21c8f9899198b747f5a44bc5f837a15dbe8de639 chenxuan <1607772321@qq.com> 1668685183 +0800commit (initial): init
21c8f9899198b747f5a44bc5f837a15dbe8de639 2f161ef1b5de6a71321f508f91fcb9c95c4e42f7 chenxuan <1607772321@qq.com> 1668687323 +0800commit: 2th
2f161ef1b5de6a71321f508f91fcb9c95c4e42f7 21c8f9899198b747f5a44bc5f837a15dbe8de639 chenxuan <1607772321@qq.com> 1668687356 +0800reset: moving to 21c8f9899198b747f5a44bc5f837a15dbe8de639
objects
info pack
- 用于gc打包文件
- 似乎使用了增量储存的形式(不确定)
使用gc 后
├── objects
│ ├── info
│ │ ├── commit-graph
│ │ └── packs
│ └── pack
│ ├── pack-87f50f248e000319b69ffc78501ce7c4f2e53999.idx
│ └── pack-87f50f248e000319b69ffc78501ce7c4f2e53999.pack
其他
- git 的obj 分有三种
- blob 文件类型
- tree 树类型(文件夹)
- commit 提交类型
- 全都是通过sha1 计算hash值再进行zlib压缩得到
文件权限存储在tree中,这样改动文件权限改动较少
refs
- 里面内容都是引用类型(都是hash指针)
head
- 所有的分支指针位置
remote
- remote指针的位置以及远程分支内容
tags
- tags 所指向提交内容
index
- git 暂存区信息
git过程分析
add
before
├── objects
│ ├── info
│ └── pack
after
├── objects
│ ├── 9c
│ │ └── 595a6fb7692405a5c4a10e1caf93d7a5bd9c37
│ ├── af
│ │ └── 81445cd6401f394045651a876e9196db61e912
│ ├── e5
│ │ └── 3be081fdea9000576336d0d0dee7b326915ff2
│ ├── info
│ └── pack
git reset
├── objects
│ ├── 9c
│ │ └── 595a6fb7692405a5c4a10e1caf93d7a5bd9c37
│ ├── af
│ │ └── 81445cd6401f394045651a876e9196db61e912
│ ├── e5
│ │ └── 3be081fdea9000576336d0d0dee7b326915ff2
│ ├── info
│ └── pack
git reset 不会删除生成的内容 add 似乎只计算文件的hash,不会递归构建tree的obj
- 计算文件hash值,并递归构建tree,生成多个obj
- 更新index的内容
commit
after commit
├── objects
│ ├── 13
│ │ └── d298626d6cad66fd224fe25106a6def17ca9d1
│ ├── 18
│ │ └── 605c522c56ea4ebbf2ee17ae702c38e89f37e2
│ ├── 30
│ │ └── 532454c17b108960baaa779156d4ed06f4700a
│ ├── 9c
│ │ └── 595a6fb7692405a5c4a10e1caf93d7a5bd9c37
│ ├── af
│ │ └── 81445cd6401f394045651a876e9196db61e912
│ ├── e5
│ │ └── 3be081fdea9000576336d0d0dee7b326915ff2
│ ├── info
│ └── pack
- 类似add,生成commit的obj,指针前移
- 更新对应值
过程总结
commit
- 保存提交的msg根目录所有文件和目录的hash
tree
- 保存目录所有文件和目录的hash
blob
- 保存文件压缩后内容
index
- 保存暂存区的文件hash列表
计算过程
- 递归寻找根目录下所有文件
- 计算文件的hash,压缩文件并生成obj,向上传递hash
blob
$ git cat-file -p af81
## this is a log for lean git
- 目录将文件的hash包含,生成类似目录列表,压缩文件并生成obj,并对自己内容生成hash,向上传递
tree
$ git cat-file -p 3053
100644 blob af81445cd6401f394045651a876e9196db61e912 README.md
040000 tree 18605c522c56ea4ebbf2ee17ae702c38e89f37e2 example
- commit 收集hash,像tree一样生成列表,再加上提交信息和上一次提交的hash,压缩文件并生成obj,继续向上传递
commit
$ git cat-file -p 13d2
tree 30532454c17b108960baaa779156d4ed06f4700a
author chenxuan <1607772321@qq.com> 1668689153 +0800
committer chenxuan <1607772321@qq.com> 1668689153 +0800
init
- 修改HEAD的值,并将提交信息记录
QA
如何避免碰撞
- 基本无需避免,两个不同文件产生同一个hash几率太小了.而且git有自动gc功能,进一步避免冲突
恶意碰撞
- git 不会发现文件的不同,因此不会处理hash相同的文件,不会生成obj
为什么空文件夹不会被识别
- tree obj存在意义在于记录包含的文件和文件夹,如果不包含任何文件和文件夹,那么这个tree完全为空就无法计算hash值
参考列表
https://git-scm.com/docs https://git-scm.com/book/zh/v2 https://zhuanlan.zhihu.com/p/106243588 https://developer.aliyun.com/article/761663