前言

通过对 Git 底层 API 的使用来了解其存储结构与工作原理,通过了解工作原理可以帮助我们更好地解决各类 Git 代码版本管理操作上的问题。

介绍

Git 是一个内容寻址(content-addressable)文件系统,Git 的核心部分是一个简单的键值对数据库(key-value data store)。你可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索(retrieve)该内容.

当在一个新目录或已有目录执行 git init 时,Git 会创建一个 .git 目录。 这个目录包含了几乎所有 Git 存储和操作的东西。 如若想备份或复制一个版本库,只需把这个目录拷贝至另一处即可。

FETCH_HEAD // 远程所有分支的head指针
HEAD // 当前分支head指针
ORIG_HEAD // 危险操作的回退指针
config // config 文件包含项目特有的配置选项
description // 仅供 GitWeb 程序使用,我们无需关心
hooks/ // hooks 目录包含客户端或服务端的钩子脚本(hook scripts)
index  // 暂存区tree指针
info/ // info 目录包含一个全局性排除(global exclude)文件, 用以放置那些不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns
logs/ // 记录所有操作,包含checkout merge reset等
objects/ // 内容文件夹
refs/ //refs 目录存储指向数据(分支、远程仓库和标签等)的提交对象的指针
config // 项目设置

hash

Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:

24b9da6552252987aa493b52f8696cd6d3b00373

规则**header + content**
  • 头部类型不同,数据对象是blob,树对象是tree,提交对象是commit
  • 数据内容不同,数据对象的内容可以是任意内容,而树对象和提交对象的内容有固定的格式。
文件存储

由hash前两位做目录,后38位做文件名存储在objects下面

ps: 如果真的计算的hash值一致的,就忽略

数据对象 blob

存储内容 hash-object

底层命令 git hash-object 可将任意数据保存于 .git/objects 目录(即 对象数据库),并返回指向该数据对象的唯一的键

echo 'aaa' | git hash-object -w --stdin // 从命令行输入内容存储
echo 'test.ts' | git hash-object -w --stdin-paths // 从已知文件输入内容存储
echo 'version 1' > test.txt & git hash-object -w test.txt // 从已知文件输入内容存储

取出内容 cat-file

git cat-file [hash] // -p(转换二进制到content) -s(查看size)-t(查看类型)

树对象 tree

Git 以一种类似于 UNIX 文件系统的方式存储内容,但作了些许简化。 所有内容均以树对象和数据对象的形式存储,其中树对象对应了 UNIX 中的目录项,数据对象则大致上对应了 inodes 或文件内容。 一个树对象包含了一条或多条树对象记录(tree entry),每条记录含有一个指向数据对象或者子树对象的 SHA-1 指针,以及相应的模式、类型、文件名信息.

加入暂存区
 git update-index v1.ts   git update-index --add v2.ts  // 没有索引的要加入索引// git status 查看
写入树对象

从当前的暂存区创建树对象

git write-tree // 输入 tree-hash
git cat-file [hash] -p // 查看树对象内容
git cat-file [hash] -t // 查看类型
读入树对象
git read-tree [tree-hash]
git read-tree --prefix=subdir [tree-hash] // 读入新目录

提交对象 commit-tree

提交对象的格式很简单:

  • 顶层树对象,代表当前项目快照; 然后是可能存在的父提交
  • 作者/提交者信息
  • 留空一行,最后是提交注释。

提交树对象
// 输入commit-hash
echo 'v1' | git commit-tree [tree-hash]
// -p 为提交对象添加父节点 可添加多个父节点
echo 'v2' | git commit-tree [tree-hash] -p [commit-hash]
模拟git commit
// 输入commit-hash
echo 'v1' | git commit-tree [tree-hash] -p [parent-commit-hash]
// 更新HEAD指针
git update-ref HEAD [commit-hash]
模拟git merge
echo 'v3' > v3.txt & git update-index --add v3.txt
git write-tree
git commit-tree
git update-ref HEAD [commit-hash]

查看所有对象
git cat-file --batch-check --batch-all-objects

git rev-list

git rev-list --all // 获取commit列表

git rev-parse

git rev-parse HEAD // 获取当前分支的commitID
git rev-parse --default HEAD --revs-only // 获取当前分支的commitID
git rev-parse --all // 获取所有分支,标签,远程分支commitID
git rev-parse --short [commit-hash] // 获取前7位的hash值
git rev-parse [branch]^{commit} // 获取branch的commit-hash
git rev-parse [branch] // 获取branch的commit-hash
git rev-parse "[commit-hash]^1" // ^1获取父节点的hash ^0获取当前节点的hash
git rev-parse "[commit-hash]^@" // 获取commit-hash的父节点(多个父节点)

# git rev-parse --verify

在这里是找到commitid的树对象的名称

// 找到commit-hash对应的tree-hash
git rev-parse --verify "[commit-hash]^{tree}"
// 检测hash的type和指定的type是否匹配
// type: object || commit || tree || blob 不匹配会报错
git rev-parse -q --verify "[hash]^{type}"

Git 引用

git update-ref

模拟git reset
// git reset 153c6e3673a7f15e9d57aa0d554f899669a1e91e
.git/refs/heads/master // 保存了master的ref
// 直接文件更新
echo '153c6e3673a7f15e9d57aa0d554f899669a1e91e' > .git/refs/heads/master
// update 安全更新
git update-ref refs/heads/master 153c6e3673a7f15e9d57aa0d554f899669a1e91e
模拟git checkout
// git checout  fix/1
echo 'ref: refs/heads/fix/1' > .git/HEAD
标签引用

标签引用存储在 .git/refs/tags

Pack

Git 每一个不同版本的快照记录下来,每一个文件存储成 Blob 对象,无论是与之前的一个文件有细微的差别,对于 Git 来说都是与之前的一个不同版本,,所以会原封不动的储存下来,这样的方式使我们切换版本速度更快、构成 Git 可分配式的基础等等,但可以长期下来控制 Git的资料库不免笨重起来,特别是长期稳定的大文件,每次仅仅修改一点点的时候。

Git 会不定时地自动运行一个叫做 “auto gc” 「垃圾收集」的命令。 大多数时候,这个命令并不会产生效果。 然而,如果有太多松散对象(不在包文件中的对象)或者太多包文件–7,000 个左右的loose object(即没有被压缩成packfile 的Git Object)或是50 个packfile,Git 会运行一个完整的 git gc 命令。 这个命令会做以下事情:收集所有松散对象并将它们放置到包文件中, 将多个包文件合并为一个大的包文件,移除与任何提交都不相关的陈旧对象

  1. 将所有有被参考的loose objects 封装成packfiles

  2. 将较小的packfiles 合并成一个大的packfiles

  3. 将所有的Git Reference 档案合并成 packed-refs 档案

  4. 将不被参考的Git Objects 删除,像是:

    1. 没有被任何Tree Object 参考的Blob Object
    2. 没有被任何Commit Object 和Tree Object 参考的Tree Object
    3. 没有被任何Git Reference 参考的Commit Object
git gc

两个文件名和大小都差不多的情况下执行git gc进行打包,避免git占内存

执行git gc后,.git/objects下只剩下悬空节点(没有被Reference的commit节点 ),所有的Object都被打包到pack文件夹中

git verify-pack

查看打包后的.pack文件

git verify-pack .git/objects/pack/pack-62be68f9db548aedcaab7 -v

  • 第一列表示hash
  • 第二列是对象的类型
  • 第三列是文件大小
  • 最后是引用文件

可以看出2daa旧版本,e069新版本,git在记录的方式是完整的保留新版本,用新版本做基础来patch,原始的版本反而是以差异方式保存的——这是因为大部分情况下需要快速访问文件的最新版本

Git 时常会自动对仓库进行重新打包以节省空间。当然你也可以随时手动执行 git gc 命令来这么做

提交丢失

如果提交已经丢失了(git reset操作)——没有分支指向这些提交且记不起这些提交的hash值

git reflog
// reflog会找找出位于 .git/log/refs下的文件,这个文件会记录你对HEAD的所有操作
// 也可以直接去.git/log/refs/[branch]去找
git reflog
git log -g // 详细信息

这个提交只是ref被删除了,log日志还在的时候,如果log日志也不在了,hh

git fsck

git fsck 会检查数据库的完整性。 如果使用一个 --full 选项运行它,它会向你显示出所有没有被其他对象指向的对象(悬空节点)

错误查询

有时候我们会遇到这么一种情况,大家一起合作开发一个项目,你完成一个功能后commit了,过了好一段时间,你再去验证这个功能时,发现不工作了(应该是别人的某次提交导致的)。what?提交代码后就没有再修改那个功能模块的代码,它怎么能不work了呢?当然也有可能你不知道修改了什么导致的。我想这时候的你肯定是崩溃的,so,git bisect能帮你快速定位到是哪一次commit导致这个功能出问题的。然后你看下这次commit改了些啥就很快能找到问题了。

git biset

将代码提交的历史,按照两分法不断缩小定位。所谓"两分法",就是将代码历史一分为二,确定问题出在前半部分,还是后半部分,不断执行这个过程,直到范围缩小到某一次代码提交, 最后该分支会定格在出错的commit上

// git bisect start [终点] [起点]
git bisect start HEAD 4d83cf // 开始查错
git bisect good // 标识该段提交不包含错误提交
git bisect bad // 标识该段提交包含错误提交
git bisect reset // 差错结束后会停留在错误commit,reset回到最原始commit

Git 底层数据存储结构与工作原理介绍相关推荐

  1. 【mysql】-【innodb数据存储结构】

    文章目录 数据库的存储结构:页 磁盘与内存交互基本单位:页 页结构概述 页的大小 页的上层结构 页的内部结构 File Header(文件头部)和File Trailer(文件尾部) File Hea ...

  2. CPU结构及工作原理

    CPU结构 中央处理器(CPU,Central Processing Unit)的功能主要是解释计算机指令以及处理计算机软件中的数据. 图1 CPU主要结构 CPU从逻辑上可以划分成3个模块,分别是控 ...

  3. 显卡的结构和工作原理及发展历史与现状

    显卡的结构和工作原理及发展历史与现状 一.显卡的基本结构 1.线路板. 目前显卡的线路板一般采用的是6层或4层PCB线路板.显卡的线路板是显卡载体,显卡上的所有元器件都是集成在这上面的,所以PCB板也 ...

  4. 显卡结构及工作原理详细解读

    显卡结构及工作原理详细解读 标签: 显卡三维图像 2016-01-16 20:58 864人阅读 评论(0) 收藏 举报  分类: 3D原理(11)  什么是显卡? 显卡的工作非常复杂,但其原理和部件 ...

  5. 简述8086计算机工作原理,8086/8088CPU的基本结构与其工作原理

    之前,我们给出了计算机系统模型,它并不特别针对某一种具体的计算机系统,只是为读者快速搭建一个深入计算机系统底层的框架.并且,在上一章中,着重介绍的是整个计算机系统的基本组成以及它的工作原理.由于CPU ...

  6. 磁盘结构和工作原理以及LBA与CHS

    硬盘(英语:Hard Disk Drive,缩写:HDD) 硬盘(英语:Hard Disk Drive,缩写:HDD)是电脑上使用坚硬的旋转盘片为基础的非易失性存储器,它在平整的磁性表面存储和检索数字 ...

  7. (八)InnoDB数据存储结构

    InnoDB数据存储结构 1.数据库的存储结构:页 1.1.磁盘与内存交互基本单位:页 1.2.页的结构概述 1.3.页的大小 1.4.页的上层结构 2.页的内部结构 第1部分:File Header ...

  8. [硬件] 简单介绍磁盘结构及工作原理

    一.前言 最近学习DOS下的汇编语言用到了很多与硬件相关的指令,比如上一期写的int 13h(直接磁盘服务),其中接口参数中就有驱动器号,磁头,磁道,扇区的概念,对于一个计算机组成原理丢了一年的人来说 ...

  9. InnoDB数据存储结构

    数据库的存储结构:页 索引结构给我们提供了高效的索引方式,不过索引信息以及数据记录都是保存在文件上的,确切说是存储在页结构中.另一方面,索引是在存储引擎中实现的,MySQL服务器上的存储引擎负责对表中 ...

最新文章

  1. 一维码Codabar简介及其解码实现(zxing-cpp)
  2. 在服务器无root权限手动安装texlive
  3. 图灵十月书讯 ——小长假过后的程序员学习指南
  4. 青龙羊毛——顺丰科勒(搬运)
  5. php fckeditor,php --- fckeditor
  6. java中的Volatile关键字使用
  7. usb连接不上 艾德克斯电源_STM32F7 电源控制器(PWR)
  8. java因子的计算方法是_JAVA分解质因子
  9. Spring Cloud与微服务学习总结(1)——Spring Cloud及微服务入门
  10. PS网页设计教程V——如何在Photoshop中创建一个商业网站布局
  11. 多平台移动项目开发工具Elements发布v9.1,支持Visual Studio 2017
  12. 小米机器狗CyberDog
  13. php服务器代维,服务器维护
  14. Win10 中U盘不识别的解决方法
  15. ASP.NET MVC5 使用NPOI导出ExceL 返回浏览器下载
  16. java applet插件_Atitit.java的浏览器插件技术 Applet japplet attilax总结
  17. java File 读取本地文件 增删改查
  18. python参考书推荐--父与子的编程之旅
  19. 【C语言】常见关键字
  20. 用计算机怎么计算sh 3,舍伍德数Sh计算公式与在线计算器_三贝计算网_23bei.com

热门文章

  1. 【iOS】KVC 与 KVO
  2. Netty UDP 接收缓冲区 报文截取问题
  3. [LeetCode javaScript] 495. 提莫攻击
  4. 12123选牌漏洞_交管12123自编选号攻略,12123自主选号成功技巧
  5. MIS外汇平台荣获“2013年全球最佳STP外汇交易商”
  6. Python连锁门店的数据分析
  7. java windows域_JAVA windows 域认证指南
  8. java调用保存好的神经网络模型
  9. 第一届“百度杯”信息安全攻防总决赛
  10. hmailserver php 收邮件,hMailServer 接收外部邮件并分发到内部邮箱