Skip to content
Published at:

Git内部原理

Git官网:https://git-scm.com/

Git:How it works?

版本控制系统VCS

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

版本控制系统:是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统

存储系统

项目里的文件/目录怎么存?存在哪里?怎么去检索?

以Git对象(文件内容对象、目录对象、提交对象)方式存储;本地存放在.git目录;通过Hash Code去检索

根本上来讲:Git 是一个内容寻址 (content-addressable)文件系统。可以理解成:Git一个Key-Value数据库

  • Key:Hash Code,就是查看git日志里面的一串码(hash算法基于内容计算出来)
  • Value:Git对象(文件对象blob,目录对象tree,提交对象commit)

Git项目目录

仓库.git目录介绍

bash
$ tree .git         # 输出:有省略部分内容
.git
├── HEAD            # 记录工作区的代码版本
├── config          # 项目配置
├── objects         # Git对象存储目录(核心目录)
└── refs            # 引用:存放分支、远程分析、Tags
    ├── heads       # 本地分支存放的目录
    │   └── master
    ├── remotes
    │   └── origin
    └── tags

推理

底层命令和高层命令

Git最开始工具集(多个命令工具),后面整合改成了git的子命令

  • 高层命令:简单的理解成,工作常用的这些命令:initcloneaddcommitpushpullcheckout......
  • 底层命令:可以去操作Git的KV数据库,完成底层工作的命令:cat-filehash-objectwrite-treecommit-tree

关系

高层命令会去调用条底层命令;底层命令可以窥探 Git 内部的工作机制,通过底层命令帮助理解Git是如何运作的

Git 对象

核心Git对象类型:

  • 文件内容对象blob:文件内容
  • 目录对象tree:可包含文件或目录
  • 提交对象commit:包含目录和其它信息(git log内容)

底层命令cat-file

用于查看Git对象:

bash
$ git cat-file -t ${hash_code}  # 查看Git对象的类型 type
$ git cat-file -p ${hash_code}  # 查看Git对象的内容 pretty-print
$ git cat-file -s ${hash_code}  # 查看Git对象的大小 size

实践

通过使用底层命令完成高层命令的工作流(initaddcommitlog),来理解Git内容原理

项目准备git init

bash
$ mkdir git_demo && cd git_demo                 # 创建项目目录

$ mkdir .git                                    # 创建.git目录
$ mkdir .git/objects                            # 创建Git数据库目录
$ mkdir -p .git/refs/heads                      # 创建本地分支目录
$ touch .git/HEAD                               # 创建Git工作区文件
$ echo "ref: refs/heads/master" > .git/HEAD     # 指定当前工作区是指向master分支

Git文件内容对象Blob:

文件内容,在项目代码里面指代码内容;不包含文件名,文件名保存在Tree对象中

bash
$ git hash-object ${file_name}                  # 计算文件内容的Hash Code
$ git hash-object -w ${file_name}               # 计算文件内容的Hash Code,并写入Git对象存储目录
$ echo "hello git" | git hash-object --stdin    # 从标准输入 计算内容的Hash Code

$ git cat-file -t ${hash_code}                  # 查看Git对象的类型
$ git cat-file -p ${hash_code}                  # 查看Git对象的内容
$ git cat-file -s ${hash_code}                  # 查看Git对象的大小

Git树对象Tree:

目录:包含Blob对象,以及Tree对象

bash
$ git update-index --add ${file_name}           # 将文件添加到暂存区
$ git write-tree                                # 从暂存区创建树对象(root跟目录树对象)
  • read-tree :可以把树对象读入暂存区

Git提交对象Commit:

基本就是用git log可以看到的内容

bash
$ git commit-tree ${tree_hash} -m ${commit_message}                             # 创建提交对象;第一次提交
$ git commit-tree ${tree_hash} -m ${commit_message} -p ${parent_commit_hash}    # 创建提交对象;第二次提交

$ git log --stat ${commit_hash}

$ echo "${commit_hash}" > .git/heads/master                                     # 指定master分支指向的提交对象

Git存储结构图

对象存储

Git 是如何存储其对象(blob对象、tree对象、commit对象)

bash
# store = header + content
blob 16\u0000what is up, doc?
  • 存储格式:header + content,比如:"blob 16\u0000what is up, doc?"
    • header格式:比如文件blob #{content.length}\0
  • 对内容计算SHA1
  • 用zlib压缩内容
  • 用前面计算出来的SHA1,创建目录和文件
  • 把内容写到文件中去

理解分支、Tag是如何实现的?

就是.git目录下的一个文件,文件名就是分支名,文件内容就是该分支的最后一个Commit对象;比如:dev分支文件.git/heads/dev

Ref:

Updated at: