Git 是一个分布式版本控制系统
- 从根本上来讲 Git 是一个内容寻址 (content-addressable)文件系统,并在此之上提供了版本控制系统的用户界面
一,Git 对象
数据对象(blob object)
先引入几个命令:
- git init 上层命令,将一个目录转变成一个 Git 仓库
- git hash-object 底层命令,用于向Git数据库中写入数据
- git cat-file 底层命令,用于查看Git数据库中数据
1,在 git-learning 目录下执行命令 git init ,查看 .git 目录结构
$ git init
Initialized empty Git repository in D:/tools/git-learning/.git/
$ find .git
.git
.git/config
.git/description
.git/HEAD
.git/hooks
.git/hooks/applypatch-msg.sample
.git/hooks/commit-msg.sample
.git/hooks/fsmonitor-watchman.sample
.git/hooks/post-update.sample
.git/hooks/pre-applypatch.sample
.git/hooks/pre-commit.sample
.git/hooks/pre-merge-commit.sample
.git/hooks/pre-push.sample
.git/hooks/pre-rebase.sample
.git/hooks/pre-receive.sample
.git/hooks/prepare-commit-msg.sample
.git/hooks/update.sample
.git/info
.git/info/exclude
.git/objects
.git/objects/info
.git/objects/pack
.git/refs
.git/refs/heads
.git/refs/tags
- hooks 目录包含客户端或服务端的钩子脚本(hook scripts)
- info 目录包含一个全局性排除(global exclude)文件 , 用以放置那些不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns)
- config 文件包含项目特有的配置选项
- description 文件仅供 GitWeb 程序使用,我们无需关心
- objects 目录存储所有数据内容
- refs 目录存储指向数据(分支、远程仓库和标签等) 的提交对象的指针
- HEAD 文件指向目前被检出的分支
- index 文件保存暂存区信息
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
- git hash-object 会接受你传给它的东西,而它只会返回可以存储在 Git 仓库中的唯一键
- -w 选项会指示该命令不要只返回键,还要将该对象写入数据库中
- --stdin 选项则指示该命令 从标准输入读取内容;若不指定此选项,则须在命令尾部给出待存储文件的路径
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
$ git cat-file -t d670460b4b4aece5915caf5c68d12f560a9fe3e4
blob
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
$ find .git/objects -type f
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
树对象(tree object)
- Git利用树(tree)对象解决了文件名保存的问题,将多个文件组织在一起
- Git通过树(tree)对象将数据(blob)对象组织起来,这很类似于一种文件系统——blob对象对应文件内容,tree对象对应文件的目录和节点
- 一个树(tree)对象包含一条或多条记录,每条记录含有一个指向 blob 对象或 tree 对象的SHA-1指针,以及相应的模式、类型、文件名
- Git根据某一时刻暂存区所表示的状态创建并记录一个对应的树对象,如此重复便可以依次记录一系列的树对象
- Git的暂存区是一个文件——.git/index
同样,我们引入几个命令:
- git update-index 底层命令,用于创建暂存区
- git ls-files --stage 底层命令,用于查看暂存区内容
- git write-tree 底层命令,用于将暂存区内容写入一个树对象
1,创建暂存区 git update-index
$ find .git/index
find: ‘.git/index’: No such file or directory
$ git update-index --add test.txt
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory
我们注意 .git/index 文件的变化。在添加 test.txt 到暂存区前,index 文件并不存在,这说明暂存区还没有创建。添加 test.txt 到暂存区的同时,index 文件被创建
2,查看暂存区内容 git ls-files --stage
$ git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 test.txt
- 100644 文件模式,表明这是一个普通文件。 其他选择如:100755,表示一个可执行文件
- 83baae... 指向数据对象或者子树对象的 SHA-1 指针
- 0 这个指的是什么?
- test.txt 文件名
3,写入树对象 git write-tree
$ git write-tree 83baae61804e65cc73a7201a7252750c76066a30
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ find .git/objects -type f
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
- 100644 文件模式,表明这是一个普通文件。 其他选择如:120000,表示一个符号链接
- blob 文件类型
- 83baae... 指向数据对象或者子树对象的 SHA-1 指针
- test.txt 文件名
在执行 git write-tree 之后,git数据库中多出了一条新的记录,键值为 d8329fc1cc938780ffdd9f94e0d364e0ea74f579
git 数据库中新增加的记录是一个 tree 对象,该 tree 对象指向一个blob对象,hash键值为 83baae61804e65cc73a7201a7252750c76066a30,文件名为 test.txt
至此,在 git 数据库中,我们可以完整的记录文件的状态、文件夹的状态;并且可以把多个文件或文件夹组织在一起,记录他们的变更过程
接下来,我们只要把数据库中各个版本的时序关系记录下来,再把对每一个版本更新的注释记录下来
这就要引入另一个 git 数据对象——提交对象(commit object)
提交对象(commit object)
- commit 对象能够帮你记录什么时间,由什么人,因为什么原因提交了一个新的版本,这个新的版本的父版本又是谁
- Git 提供了底层命令 commit-tree 来创建提交对象(commit object)
- 我们需要为这个命令指定一个被提交的树对象的 hash 键值,以及该提交对象的父提交对象(如果是第一次提交,不需要指定父对象)
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 -m "first commit"
8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
$ git cat-file -t 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
commit
$ git cat-file -p 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author maling <744632504@qq> 1610162941 +0800
committer maling <744632504@qq> 1610162941 +0800
first commit
$ echo "version 2" > test.txt
$ git update-index test.txt
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory
$ git write-tree
2f39845a4a2c3ad86adebb00b1ddabd959c131c4
$ git commit-tree 2f39845a4a2c3ad86adebb00b1ddabd959c131c4 -p 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0 -m "second commit"
f5a3262459510d25c013e3ffeb3cf83fe60ca416
$ git cat-file -t f5a3262459510d25c013e3ffeb3cf83fe60ca416
commit
$ git cat-file -p f5a3262459510d25c013e3ffeb3cf83fe60ca416
tree 2f39845a4a2c3ad86adebb00b1ddabd959c131c4
parent 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
author maling <744632504@qq> 1610163455 +0800
committer maling <744632504@qq> 1610163455 +0800
second commit
$ find .git/objects/ -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/2f/39845a4a2c3ad86adebb00b1ddabd959c131c4
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/8c/14d0e26c4baded3ad6bbf88cb40660bd3544d0
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579
.git/objects/f5/a3262459510d25c013e3ffeb3cf83fe60ca416
$ git log f5a3262459510d25c013e3ffeb3cf83fe60ca416
commit f5a3262459510d25c013e3ffeb3cf83fe60ca416
Author: maling <744632504@qq>
Date: Sat Jan 9 11:37:35 2021 +0800
second commit
commit 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
Author: maling <744632504@qq>
Date: Sat Jan 9 11:29:01 2021 +0800
first commit
小结
1,Git 对象有:blob,tree,commit
blob 如:
$ git cat-file -t 83baae61804e65cc73a7201a7252750c76066a30
blob
$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30
version 1
暂存区(index)如:
$ git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 test.txt
tree 如:
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
commit 如:
$ git cat-file -t 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
commit
$ git cat-file -p 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author maling <744632504@qq> 1610162941 +0800
committer maling <744632504@qq> 1610162941 +0800
first commit
$ git cat-file -t f5a3262459510d25c013e3ffeb3cf83fe60ca416
commit
$ git cat-file -p f5a3262459510d25c013e3ffeb3cf83fe60ca416
tree 2f39845a4a2c3ad86adebb00b1ddabd959c131c4
parent 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
author maling <744632504@qq> 1610163455 +0800
committer maling <744632504@qq> 1610163455 +0800
second commit
2,工作区与暂存区
直观上看,.git 目录外的部分是工作区
.git/index 文件为暂存区
3,目前已经介绍了
- objects 目录存储所有数据内容
- index 文件保存暂存区信息
- refs 目录存储指向数据(分支、远程仓库和标签等) 的提交对象的指针
- HEAD 文件指向目前被检出的分支
二,Git 引用
- git update-ref
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f
$ git update-ref refs/heads/master f5a3262459510d25c013e3ffeb3cf83fe60ca416
$ cat .git/refs/heads/master
f5a3262459510d25c013e3ffeb3cf83fe60ca416
HEAD 引用
- 将 HEAD 想象为当前分支的别名
$ cat .git/HEAD
ref: refs/heads/master
标签引用
- 前面我们刚讨论过 Git 的三种主要的对象类型( 数据对象 blob、树对象 tree 和 提交对象 commit ),然而实际上还有第四种
- 标签对象( tag ) 非常类似于一个提交对象——它包含一个标签创建者信息、一个日期、一段注释信息,以 及一个指针
- 主要的区别在于,标签对象通常指向一个提交对象,而不是一个树对象
- 它像是一个永不移动的分支引用——永远指向同一个提交对象,只不过给这个提交对象加上一个更友好的名字罢了
$ git update-ref refs/tags/v1.0 f5a3262459510d25c013e3ffeb3cf83fe60ca416
$ cat .git/refs/tags/v1.0
f5a3262459510d25c013e3ffeb3cf83fe60ca416
$ git log v1.0
commit f5a3262459510d25c013e3ffeb3cf83fe60ca416 (HEAD -> master, tag: v1.0)
Author: maling <744632504@qq>
Date: Sat Jan 9 11:37:35 2021 +0800
second commit
commit 8c14d0e26c4baded3ad6bbf88cb40660bd3544d0
Author: maling <744632504@qq>
Date: Sat Jan 9 11:29:01 2021 +0800
first commit
三,参考资料(推荐电子书 progit)
- https://blog.youkuaiyun.com/ahaotata/article/details/84391018
- https://blog.youkuaiyun.com/weixin_42762133/article/details/105458252
- https://blog.youkuaiyun.com/ITleaks/article/details/32303689
- https://blog.youkuaiyun.com/Tencent_TEG/article/details/103485723
- https://blog.youkuaiyun.com/sinat_33224091/article/details/99670014
- https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1
- https://blog.youkuaiyun.com/优快云___LYY/article/details/100939749
PS:这个工具好用!
$ tree . -al
.
|-- .git
| |-- HEAD
| |-- config
| |-- description
| |-- hooks
| | |-- applypatch-msg.sample
| | |-- commit-msg.sample
| | |-- fsmonitor-watchman.sample
| | |-- post-update.sample
| | |-- pre-applypatch.sample
| | |-- pre-commit.sample
| | |-- pre-merge-commit.sample
| | |-- pre-push.sample
| | |-- pre-rebase.sample
| | |-- pre-receive.sample
| | |-- prepare-commit-msg.sample
| | `-- update.sample
| |-- index
| |-- info
| | `-- exclude
| |-- logs
| | |-- HEAD
| | `-- refs
| | `-- heads
| | `-- master
| |-- objects
| | |-- 1f
| | | `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
| | |-- 2f
| | | `-- 39845a4a2c3ad86adebb00b1ddabd959c131c4
| | |-- 4b
| | | `-- 825dc642cb6eb9a060e54bf8d69288fbee4904
| | |-- 83
| | | `-- baae61804e65cc73a7201a7252750c76066a30
| | |-- 8c
| | | `-- 14d0e26c4baded3ad6bbf88cb40660bd3544d0
| | |-- d6
| | | `-- 70460b4b4aece5915caf5c68d12f560a9fe3e4
| | |-- d8
| | | `-- 329fc1cc938780ffdd9f94e0d364e0ea74f579
| | |-- f5
| | | `-- a3262459510d25c013e3ffeb3cf83fe60ca416
| | |-- info
| | `-- pack
| `-- refs
| |-- heads
| | `-- master
| `-- tags
| `-- v1.0
`-- test.txt
20 directories, 30 files
windows 系统需要额外工具来执行 tree 命令
https://blog.youkuaiyun.com/qq_36852780/article/details/104447068