git原理浅析

1.git概念

在这里插入图片描述

我们的项目一般由文件夹和文件组成,在文件系统中,基本都是树形结构, 在git中,文件夹称为 “tree” ,文件称为 “blob” ,顶层文件夹称为 “top-level tree” 。下方的目录结构是个例子而已:

. (top-level tree)
├── humx.txt (blob,内容为“111”)
└── test (tree)
    └── humx1.txt (blob,内容为“222”)
    └── humx2.txt (blob,内容为“222”)

整个树形结构我们称为"commit", 在git 系统中, 就是众多commit构成的

11316@DESKTOP-SMV4E4J MINGW64 /f/idea
$ mkdir test

11316@DESKTOP-SMV4E4J MINGW64 /f/idea
$ cd test

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test
$ git init
Initialized empty Git repository in F:/idea/test/.git/

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ cd .git/

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test/.git (GIT_DIR!)
$ tree
.
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-commit.sample
|   |-- pre-push.sample
|   |-- pre-rebase.sample
|   |-- pre-receive.sample
|   |-- prepare-commit-msg.sample
|   `-- update.sample
|-- info
|   `-- exclude
|-- objects
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    `-- tags


.git目录下结构功能:

  • HEAD 文件:指向当前所在分支。
  • config文件:包含了一些git的配置。
  • description文件:只有在GitWeb项目中才会用到,所以不用关注这个文件。
  • hooks文件夹:包含了一些钩子脚本, 触发某些特定事件触发
  • info文件夹:包含了.gitignore 文件中的信息, 忽略某些文件用的
  • objects文件夹:存放object的数据库,存放整个项目的所有数据。
  • refs文件夹:存放了指向objects的指针(如branches,tags,remotes等)

首先,我们先创建一个文件humx.txt 并写入111的内容,并执行git add ,得出以下:

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ echo '111' > humx.txt

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ git add .
warning: LF will be replaced by CRLF in humx.txt.
The file will have its original line endings in your working directory

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ tree .git/
.git/
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-commit.sample
|   |-- pre-push.sample
|   |-- pre-rebase.sample
|   |-- pre-receive.sample
|   |-- prepare-commit-msg.sample
|   `-- update.sample
|-- index
|-- info
|   `-- exclude
|-- objects
|   |-- 58
|   |   `-- c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    `-- tags

可以看到,在objects文件夹下多了一个58文件夹和一个名叫c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c文件,把两者看成一个整体就是一个git object,而且我们将它们的名字拼接起来就能得到一个40位的hashId,这个hashId是这个object的唯一标志符,git通过这个hashId来查找这个object。不过不能直接cat,因为文件是经过压缩的,不能直接阅读。通过 git cat-file命令查看可得到 111 的内容 和blob 的类型。并且多了index文件,这是我们git 的暂存区存放的文件,内容为(mode) (object) (stage) (file).

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ git commit -m"first commit"
[master (root-commit) a4f7afb] first commit
 1 file changed, 1 insertion(+)
 create mode 100644 humx.txt

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ tree .git/
.git/
|-- COMMIT_EDITMSG
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-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
|   |-- 56
|   |   `-- 63e48c7b2bc80c2cdc2990d11ba8594a87ba0f
|   |-- 58
|   |   `-- c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
|   |-- a4
|   |   `-- f7afb5eda5f66b249171a744feebf9a754a7ea
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    |   `-- master
    `-- tags

接下来提交文件,我们发现又多了两个object,其中:
56:(tree)
在这里插入图片描述
a4:(commit)
在这里插入图片描述
仔细观察,commit指向tree, tree指向blob,可以得出:
在这里插入图片描述

然后在创建一个test文件夹,文件夹下新建humx1.txt humx2.txt 内容分别为222 333

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test/text (master)
$ echo '222' > humx1.txt

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test/text (master)
$ echo '333' > humx2.txt

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test/text (master)
$ cd ..

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ git add .
warning: LF will be replaced by CRLF in text/humx1.txt.
The file will have its original line endings in your working directory
warning: LF will be replaced by CRLF in text/humx2.txt.
The file will have its original line endings in your working directory

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ git commit -m"second commit"
[master 8b2580a] second commit
 2 files changed, 2 insertions(+)
 create mode 100644 text/humx1.txt
 create mode 100644 text/humx2.txt

11316@DESKTOP-SMV4E4J MINGW64 /f/idea/test (master)
$ tree .git/
.git/
|-- COMMIT_EDITMSG
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-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
|   |-- 30
|   |   `-- ed92ac0fa69377e0f25cb339e1e5676a300c5a
|   |-- 55
|   |   `-- bd0ac4c42e46cd751eb7405e12a35e61425550
|   |-- 56
|   |   `-- 63e48c7b2bc80c2cdc2990d11ba8594a87ba0f
|   |-- 58
|   |   `-- c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
|   |-- 8b
|   |   `-- 2580a781611d1e7be39e42ab8bbf88181103bf
|   |-- a4
|   |   `-- f7afb5eda5f66b249171a744feebf9a754a7ea
|   |-- c2
|   |   `-- 00906efd24ec5e783bee7f23b5d7c941b0c12c
|   |-- df
|   |   `-- 15a9f7eb285f1384886435bf4055f12d1bb93a
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    |   `-- master
    `-- tags

30:(tree)
在这里插入图片描述
55:(blob)
在这里插入图片描述
8b:(commit)
在这里插入图片描述
c2:(blob)
在这里插入图片描述
df:(tree)
在这里插入图片描述
我们可以发现:hashId为df的tree是新生成的,是因为根目录下的文件/文件夹列表发生了变化。git并没有直接在56 的tree中直接修改,是因为如果我们需要跳回第一次commit的内容时,直接使用56的tree就可以了,这样就很方便。
还有值得注意的是:由于humx.txt文件内容未改变,df tree直接引用了58这个object,这样的策略可以为.git目录节省很大的空间,如下图:
在这里插入图片描述

2.Reference

git是通过hashId来查找某个commit的,这对于计算机来说是件非常简单的事,但对于人来说,要记住这么长的hashId,真不是件容易的事。如果能给这些hashId取一些“简单的名字”(比如master、dev、HEAD等),那就非常容易记忆了。在git术语中,这些“简单的名字”被叫做 “reference” 。这些reference主要保存在.git/refs文件夹中

2.1 Branch
这里我们新建一个名为test1的仓库,并创建humx3.txt文件,进行commit后,cd 到.git/refs/head路径下,会有一个master文件,直接查看文件可以得到这个commit的hashId,可以直接找到这个提交,下面清楚的标记了这次提交的 tree ,作者信息,并且类型为 commit。git管理的项目原本没有master分支,当我们提交第一个commit后,git会自动给我们创建master分支,并将它指向第一个commit。
在这里插入图片描述

.
|-- COMMIT_EDITMSG
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-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
|   |-- ce  
|   |   `-- 013625030ba8dba906f756967f9e9ca394464a
|   |-- e2
|   |   `-- 244575c8f723ffb6de45fac2b34391cc1b074d
|   |-- fb
|   |   `-- e2b124907eb30c097cec796f60dfb9c18df8ff
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    |   `-- master
    `-- tags

ce:
在这里插入图片描述

e2:
在这里插入图片描述

fb:
在这里插入图片描述
在这里插入图片描述
这里我们修改一下开始文件的内容,追加“world”进去,发现新的提交指向了新的地址,但文件名还是之前的文件名。
在这里插入图片描述

.git/
|-- COMMIT_EDITMSG
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-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
|   |-- 47
|   |   `-- 0b84f11fafd7e2a29a54d9cefd9fa199b9f3d3
|   |-- 94
|   |   `-- 954abda49de8615a048f8d2e64b5de848e27a1
|   |-- b7
|   |   `-- e48a1342abdc6c13ff4c10993625d6a60d836e
|   |-- ce
|   |   `-- 013625030ba8dba906f756967f9e9ca394464a
|   |-- e2
|   |   `-- 244575c8f723ffb6de45fac2b34391cc1b074d
|   |-- fb
|   |   `-- e2b124907eb30c097cec796f60dfb9c18df8ff
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    |   `-- master
    `-- tags

47:
在这里插入图片描述

94:
在这里插入图片描述

b7:
在这里插入图片描述

接下来,我们在切换分支,并新增humx4.txt文件写入内容hahaha
在这里插入图片描述

.git/
|-- COMMIT_EDITMSG
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-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
|           `-- text
|-- objects
|   |-- 44
|   |   `-- 5a69c00e48288ac420a2ead9ae5a1cb4cd36d4
|   |-- 47
|   |   `-- 0b84f11fafd7e2a29a54d9cefd9fa199b9f3d3
|   |-- 94
|   |   `-- 954abda49de8615a048f8d2e64b5de848e27a1
|   |-- b7
|   |   `-- e48a1342abdc6c13ff4c10993625d6a60d836e
|   |-- bf
|   |   `-- 06cff14be1e67be65b50f333cbf1d13270662e
|   |-- ce
|   |   `-- 013625030ba8dba906f756967f9e9ca394464a
|   |-- dc
|   |   `-- 55dcba685e66992a5379e393a4fb2de1738d92
|   |-- e2
|   |   `-- 244575c8f723ffb6de45fac2b34391cc1b074d
|   |-- fb
|   |   `-- e2b124907eb30c097cec796f60dfb9c18df8ff
|   |-- info
|   `-- pack
`-- refs
    |-- heads
    |   |-- master
    |   `-- text
    `-- tags

44:
在这里插入图片描述
bf:
在这里插入图片描述
dc:
在这里插入图片描述
在这里插入图片描述

HEAD文件保存着当前所在分支的路径,git通过这个路径访问到对应分支文件,然后通过这个分支文件保存的hashId就能获取到对应commit,这样git就能构建出当前commit对应的目录结构,如图:
在这里插入图片描述

3.总结

  • objects文件夹是git最重要的数据库,所有的文件内容,及各个版本的内容,都保存在objects文件夹中。
  • objects文件夹中主要保存三类object:commit、tree、blob,它们都由一个文件夹和文件组成,文件夹和文件的名字拼接成40位的hashId,这个hashId就是这个object的唯一标识符,git通过这个hashId来查找某个object。另外,这些文件都是经过压缩的,不能直接供人阅读,需要通过git cat-file指令查看。
  • 每一个commit都对应一个top-level tree,以top-level tree为根节点,可以构造出当前版本的目录结构,通过访问blob类的object,就能读取到对应文件的具体内容。另外,多个版本之间的文件如果是完全相同的,git只会生成一个blob对象,多个版本引用同一个blob对象,这可以大大节省磁盘空间。
  • referrence都是基于commit来实现的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值