【Git 内部原理】`.git` 是怎么记住所有版本的

`全网最细!! 入门必看Git教程链接:https://gitee.com/all-about-git` 全网 Git 入门教程 gitee.com。如果显示The requested URL returned error: 403错误,那就表示没有和window本地建立token令牌 阅读详情

​ 每次 git commit,Git 都说"已记录"。但你有没有想过:改了几十次、几百次,Git 是怎么全记住的?难道每次提交,它都复制一份完整项目?

​ 这篇文章不讲命令,也不背概念。 我们直接打开 .git 文件夹,看 Git 到底存了什么。读完你会明白三件事:

  • 为什么 Git 几乎不会变慢
  • 为什么分支创建几乎是瞬间完成
  • 为什么 Git 能精确还原任何历史版本

一、.git 文件夹里有什么

​ 当你执行 git init,项目根目录会多出一个 .git 文件夹。这个文件夹就是 Git 的全部家当。如果删掉它,相当于告诉 Git:“我从来没有版本历史。”

​ 如果只看关键部分,结构大致如下:

.git/
├── objects/     ← 所有版本数据都存在这里
├── refs/        ← 分支和标签的指针
├── HEAD         ← 当前在哪个分支
└── config       ← 仓库配置(比如 remote 地址)
  • 你可以在任意 Git 仓库里自己看一眼:

    tree .git -L 1
    

​ 会列出 .git 下的所有一级目录和文件,和上面的结构一一对应。

1.1 objects/:所有版本数据的仓库

最重要的目录,Git 记住所有版本的秘密全在这里。

​ 这是 .git 里最核心的目录。你提交过的每一份文件内容、每一次目录结构、每一个 commit,全都以对象的形式存在这里。 后面几节会详细讲 Git 是怎么组织这些对象的。

​ 你可以进去看看:

ls .git/objects/

在这里插入图片描述

​ 会看到一堆两位字母的文件夹(如 3a、f2),这些是对象哈希值的前两位,Git 用它来分散存储,避免单个目录下文件太多。

1.2 refs/:分支和标签的指针

​ refs/ 里存的是引用,本质上就是一个个小文件。
在这里插入图片描述

​ 可以看到,每个文件里只写了一行 commit 哈希值。

cat .git/refs/heads/main
9cf71a0e00a25a56c416113ce1a66c6c247aa2bf

​ 这就是 main 分支的"指针",它指向这个分支最新的一次 commit。你每次提交,Git 就把这个文件里的哈希值更新成新的 commit。

​ 标签也类似,存在 refs/tags/ 下。

1.3 HEAD:你现在在哪

​ HEAD 是一个特殊的文件,记录当前工作在哪个分支上:

cat .git/HEAD
ref: refs/heads/main

​ 意思是"我现在在 main 分支"。如果你 git checkout 到某个具体的 commit(而不是分支),HEAD 会直接存那个 commit 的哈希值,这就是所谓的"detached HEAD"状态。

1.4 config:仓库级别的配置

​ 这个文件存的是只对当前仓库生效的 Git 配置,比如远程仓库的地址:

cat .git/config

​ 你会看到类似 [remote "origin"] 和 url = ... 这样的内容。git remote add 其实就是在往这个文件里写东西。
在这里插入图片描述

​ 它和全局的 ~/.gitconfig 是分开的。仓库级配置优先级更高,会覆盖全局配置中的同名选项。

二、Git 存东西的方式:快照,不是差异

​ 很多人以为 Git 记录的是"每次改了什么,例如这次改了哪几行代码"(差异),其实不是。Git 记录的是每次提交时项目的完整快照(snapshot)。

​ 听起来很浪费?别急,Git 有个聪明的设计:相同内容只存一份。

2.1 举例

​ 假如有一个项目,里面有三个文件:

README.md
app.py
config.yaml
  • 两次commit

    • 第一次 commit,Git 把三个文件的内容都存进 objects/。第二次 commit,你只改了 app.py。
  • Git 会怎么做?

    • README.md 没变 → 不存,直接指向上次那份
    • config.yaml 没变 → 不存,直接指向上次那份
    • app.py 变了 → 存一份新的

    所以虽然 Git 记录的是"完整快照",但没变的文件零开销,只有真正改了的文件才会占空间。

在这里插入图片描述

左边是很多版本管理工具的做法,只记录每次的改动(diff);右边是 Git 的做法,记录完整快照,但没变的文件不重复存储,直接指回原来那份。

三、三种积木:blob、tree、commit

​ Git 的 objects/ 里只有三种东西,像三块积木,搭起了整个版本历史。

3.1 blob:文件内容

​ 每个文件的内容会被存成一个 blob(Binary Large Object)。注意,blob 只存内容,不存文件名。如果两个文件内容完全一样?Git 只存一个 blob,不管它们叫什么名字。

3.2 tree:目录结构

​ tree 记录的是"哪个文件名对应哪个 blob",相当于一张目录清单:

README.md  → blob-A
app.py     → blob-B
config.yaml → blob-C

​ 如果有子文件夹,tree 里还会嵌套 tree。

3.3 commit:一次快照

​ commit 把这些都串起来。 一个 commit 包含:

  • 指向一个 tree(这次提交时项目长什么样)
  • 指向父 commit(上一次提交是谁)
  • 作者、时间、提交信息

在这里插入图片描述

​ commit 指向 tree,tree 指向 blob,三层结构各司其职。commit 记录"谁在什么时候提交的",tree 记录"项目有哪些文件",blob 记录"文件里写了什么"。

四、用一个例子串起来

​ 假设你做了两次提交,第二次只改了 app.py。

  • 第一次 commit

    commit-1
      └── tree-1
            ├── README.md  → blob-A
            ├── app.py     → blob-B
            └── config.yaml → blob-C
    
  • 第二次 commit:

    commit-2(父节点 → commit-1)
      └── tree-2
            ├── README.md  → blob-A   ← 同一个 blob,没有重新存
            ├── app.py     → blob-D   ← 新 blob,内容变了
            └── config.yaml → blob-C   ← 同一个 blob
    

    进行了两次 commit,objects/ 里一共只有:

对象数量说明
commit2 个每次提交一个
tree2 个目录结构变了(app.py 指向不同 blob)
blob4 个A、B、C、D,其中 A 和 C 被两个 tree 共享

​ 一共 8 个对象。如果"每次都复制一份"需要 6 个 blob,现在只要 4 个。文件越多、改动越少,这个优势越明显。

在这里插入图片描述

两次提交产生了两个 commit 和两个 tree,但 blob-A(README.md)和 blob-C(config.yaml)因为内容没变,被两个 tree 共享,这就是 Git "快照但不浪费"的关键。

五、项目越来越大怎么办

​ 靠上面的"内容去重"已经省了很多空间,但项目跑久了对象还是会越来越多。Git 还有两招:

  • 压缩: 每个对象存入时会用 zlib 压缩。代码都是文本,压缩率很高,实际占用比原始文件小得多。

  • 打包(packfile): 当松散对象积累太多时,Git 会自动把它们打包成 .git/objects/pack/ 下的 packfile。打包时 Git 会计算相似对象之间的增量差异,只存"和上一版的区别",进一步压缩。

    你不需要手动触发这些,git push 和 git gc 时 Git 会自动处理。

    在这里插入图片描述

Git 先把每个对象单独压缩存放(松散对象),积累多了就自动打包成 packfile,利用相似对象之间的增量差异进一步节省空间。

六、动手看一看

​ 前面讲了这么多概念,不如自己动手验证一下。找一个有过提交记录的 Git 仓库,跟着下面的步骤操作。

​ 我们会用到一个底层命令 git cat-file。平时用不到它,但它是查看 Git 对象内容的万能工具,给它一个对象的哈希值(或者 HEAD 这样的引用),它就能把对象的内容打印出来。

6.1 Step1:查看当前commit的内容

# 看当前 commit 的内容
git cat-file -p HEAD

​ HEAD 指向当前分支的最新 commit,所以这条命令会打印出这个 commit 对象的内容。输出大致长这样:
在这里插入图片描述

​ 可以看到:commit 里记录了一个 tree 哈希值(这次提交时项目的目录快照)、parent(上一次 commit)、以及作者和提交信息。和之前介绍的完全一致。

6.2 Step2:查看tree

​ 把上一步输出中 tree 后面的哈希值复制过来。-p 参数的意思是"pretty-print",让输出更易读。

git cat-file -p <tree哈希值>

​ 这条命令打印的是目录快照,也就是提交时项目根目录下有哪些文件和子目录。输出类似:

在这里插入图片描述

​ 每一行就是一个条目:文件对应 blob,子目录对应另一个 tree。100644 是普通文件的权限,040000 是目录。

6.3 Step3:看blob里有什么

​ 再从上一步的输出中挑一个 blob 的哈希值:

git cat-file -p <blob哈希值>

​ 这里我们选择app.py对应的blob的哈希值,查看对应的文件内容:

在这里插入图片描述

​ 这次输出的就是文件的原始内容,一字不差。blob 就是这么简单。因为它只存内容,不存文件名(文件名记在 tree 里)。

​ 从 commit 到 tree 到 blob,可以一层层追下去。commit 告诉你"谁在什么时候提交了什么",tree 告诉你"项目有哪些文件",blob 告诉你"文件里写了什么"。这就是 Git 存储的全貌,没有其他魔法。

七、总结

  • .git/objects/ 是 Git 的核心仓库,所有版本数据都在这里
  • Git 记录的是完整快照,但相同内容只存一份,没改的文件零开销
  • 三种对象各管一件事:blob 存内容、tree 存目录、commit 存快照
  • 项目再大也不怕:zlib 压缩 + packfile 增量打包,Git 会自动管理空间

下次再 git commit 的时候,你就知道 Git 在背后做了什么了,不是复制粘贴,而是精打细算地存了一个快照。

Git+GitHub,构建自己的开源仓库之初识Git 阅读本文大概需要5分钟 初识Git最近在学习如何使用Git,一边学习,一边实践,一边记录,相信很多公司的小伙伴都还在使用SVN,我也一样,但是,现在的想法是能多掌握一种版本控制系统,万一下家公司用的是Git呢?毕竟Git现在如此流行;另外,也在准备自己做一个App,开源到GitHub上,积极参与开源项目,也好好的构建一下自己的GitHub主页,无论是提升自己的能力也好,还是为以后跳巢增加砝码也好,都 阅读详情

相关推荐

git init 执行后发生了什么?

首先在磁盘中创建一个新目录 Git,进入该目录后执行git init初始化。这个时候目录下会创建一个隐藏目录 ./git,这个./git 目录叫做 Git 版本库或者仓库$ git init。

wofacailo的博客 1363

Gradle Transform 与 字节码插桩

gradle用于构建项目,其plugin插件用于完成一特定功能,而有些时候我们希望在插件中完成对项目内容的一些更改,这就需要我们在gradle构建过程中,获取到源文件才能进行,所幸的是,gradle plugin从1.5.0版本开始,为我们提供了Transform功能,它可以以输入输出流的链式方式,供我们对源文件进行处理。 Transform功能的结构: QualifiedContent ...

做一个苦行僧的专栏 2156

3.使用 git init 初始化项目

使用 git init 命令来初始化一个项目。init 是 initialize 的简称。想用 git 去控制和管理项目,首先要做的就是去初始化一下。 在桌面上新建一个为 testing 的目录,把项目需要的所有的文件放在这个目录的下面。使用 mkdir testing 创建目录。 cd testing 进入目录,执行 git init 去初始化一下。 返回初始化了一个空白的 Git re...

叶涛的博客 2562

AI推介-大语言模型LLMs论文速览(arXiv方向):2025.06.01-2025.06.05

大型语言模型在特定领域任务中的表现需要微调,而微调的计算成本高昂,技术难度大。软提示是一种很有前途的方法,它通过学习一小部分参数集使预先训练好的模型适应下游任务。我们提出了一种具有自我关注机制(ID-SPAM)的新型输入相关软提示技术,它能根据输入令牌生成软提示,并以不同的重要性关注不同的令牌。我们的方法简单高效,可训练参数数量少。我们展示了所提出的方法与各种任务中的先进技术相比所具有的优点,并显示了改进的零镜头域转移能力。

小小帅 1466

Hi3516CV610 YOLOv8模型转换与部署实战:从源码到板端推理全流程解析

本文详细解析了Hi3516CV610开发板上YOLOv8模型的转换与部署全流程,包括模型转换、量化、编译和部署等关键步骤。通过实战案例和避坑指南,帮助开发者高效实现从PyTorch模型到嵌入式设备的推理优化,适用于智能门禁、视频分析等实时场景。

weixin_30790841的博客 654

Upload Symbols Failed:The archive did not include a dSYM for the LiveKitWebRTC.framework with the UU

你需要从源码构建 LiveKitWebRTC,而不是使用 CocoaPods 安装的预编译 .xcframework。Step 2: 修改 Podfile 使用本地路径引用(不是从源下载的 xcframework)方法 :强制从源码构建 LiveKitWebRTC.framework(推荐)这样它就会编译 WebRTC 并带上正确 UUID 的 dSYM。替换路径为你 clone 下来的 LiveKit 源码位置。Step 1: 克隆源码仓库。

baidu_40537062的博客 2136

undefined reference to `__imp_WSAStartup‘...Devc++报错解决方法

完美解决Devc++报错undefined reference to `__imp_WSAStartup'...

飞影铠甲的博客 2933

01-认识脑电信号及相关信号的预处理过程

脑电信号处理过程脑电实验为什么要预处理预处理方法降采样如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 脑电实验 本博客主要是记录基于脑电信号的情绪识别方法的研究...

脑机交流者的博客 7660

@Scheduled教程1-- 指定线程池运行定时任务

1. 未指定线程池 需要注意的@Scheduled默认情况下只有一个线程,并不能同时运行多个任务,分析源码可以看到 运行demo 在控制台我们可以看到打印出来的线程名称 2. 指定线程池运行定时任务 自定义一个线程工厂继承ThreadFactory进行线程配置 package com.example.demo; import lombok.extern.log4j.Log4j; import java.util.concurrent.ThreadFactory; import

MyBoBo 4246

quartus 13.1常见报错解决方法

在使用quartus中会遇到很多由于安装或设置不规范造成的错误。我就目前我遇到的一些错误以及我的解决方法分享给大家,这个文章将会持续更新,如果读者有遇到软件中错误无法解决的问题,也欢迎在评论区留言。 一、编译出现lience错误 在对设计的电路编译时,系统在程序运行3秒内即结束并报错,大概意思为现有的证书不支持编译。这个常见问题源自破解不成功,这里读者很有可能犯的错误是在使用破解器的时候没有区分x86(即32-bit)和x64软件破解器对应的文件夹。 如图所示有两种破解器,分别对应32位和64位的软件,大部

肥宅的博客 3万+

【基于大模型 + FAISS 的本地知识库与智能 PPT 生成系统:从架构到实现】

本文介绍了一个基于大模型(Ollama+DeepSeek)和FAISS向量数据库的本地知识库与智能PPT生成系统。该系统采用前后端分离架构,包含文档管理、向量检索、PPT生成等核心模块,可实现文档智能解析、语义检索和结构化PPT自动生成。通过MVC设计模式,系统实现了从文档上传、向量存储到内容检索、PPT生成的全流程自动化,有效解决了企业知识管理效率低下的问题。架构设计清晰,功能模块完备,为企业和个人提供了高效的文档处理与二次创作解决方案。

聚焦大数据、AI、数据库三大核心领域,分享 Flink/Spark 实时计算、数仓架构设计、AI 模型落地、数据库性能调优的实战干货,附可直接运行的源码与方案。提供企业技术内训、项目咨询与定制化服务,助力开发者与企业高效搞定数智技术难题。 1600

配置和运行 LEADTOOLS JPIP Demos C# .NET 6

本教程介绍如何配置和运行 LEADTOOLS JPIP 客户端/服务器演示,该演示允许在服务器上存储非常大的压缩 JPEG2000 图像,并使用客户端指定的图像分辨率、质量和感兴趣区域 (ROI) 传输这些图像。

控件与插件之大全 985

Docker入门教程:在腾讯云轻量服务器上部署你的第一个容器化应用 (2025)

还在为“在我电脑上明明是好的”而烦恼?本篇Docker入门教程,用通俗易懂的比喻解释镜像、容器的核心概念,并手把手带你在腾讯云轻量服务器上,从运行Nginx到编写Dockerfile打包自己的Python应用,轻松掌握容器化部署。

Clownseven的博客 2085

【Git】Git 基础命令 ( Git 版本库概念 | 创建版本库 git init | 克隆版本库 git clone )

一、Git 版本库概念、 二、创建版本库、 三、克隆版本库

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 2118

stm32H743IIT6 cubemx HAL USB UVC摄像头基础

stm32性能已经很强了, 可以驱动ov5640摄像头, 所以可以把摄像头接在stm32上, 当作一个摄像头驱动器, 连接到电脑上 开发环境: stm32cubemx 6.0.1 单片机: 自己做的stm32h743iit6核心板,兼容正点原子 学习资料: 圈圈学USB 鱼鹰谈单片机 从零开始学习USBhttps://to-run-away.blog.csdn.net/article/details/85038393 整个项目是基于CDC串口上修改 1 stm32 USB 代码概览 ..

紫枫洛天的博客 7752

pytorch搭建SSAE实现预测分类代码模板完整代码

pytorch搭建SSAE实现预测分类代码模板完整代码

325

WSL Git Clone 项目识别 `.git` 问题记录

摘要:在WSL环境下从远程仓库克隆项目后,Git将整个项目标记为未跟踪文件,尽管.git文件夹完整且仓库无损坏。问题源于WSL对Linux文件权限的严格检测,导致Git索引与工作目录权限不一致。解决方法包括关闭文件模式检测(git config core.fileMode false)并重置索引(git reset --hard HEAD),使Git正确识别仓库内容。该方案适用于WSL下所有类似情况,确保Git正常识别.git目录且不影响提交历史。

gxy03的博客 656

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Git .Git目录详解|Git目录扫描|gitpillage使用

Git是什么 Git是一个VCS(Version Control System),即版本控制系统 版本控制系统从字面意思来看,它的用途就是管理/控制文件的版本,使用它,可以方便的知道一个文件在什么时间被谁修改了什么内容,这样,如果文件被改动后出了什么问题,就可以快速的定位问题,从而解决问题 版本控制系统中可以保持任何的文件,比如文档,代码等 版本控制系统主要有如下优点: 1,方便多人协作 2,方便管理/控制/查看历史版本信息 常见的版本控...

qq_60115503的博客 2473

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

git版本穿梭

git文件夹了解记录。在将项目进行初始化本地库之后会生成一个隐藏的.git文件。这里面没事是尽量不要去动的。里面几个东西是干什么我在此记录一下。假设我现在处于第三个版本,现在我不满意,`想要回到第一个版本``。首先我需要知道第一个版本的版本号.这个一路走下去也可以看见一个master文件,这里存放的是所有的记录。默认的放在master分支。这个master里面就存放你当前所在版本。

weixin_44239550的博客 280

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Apache Airflow GitDagBundle 新增 `prune_dotgit_folder` 选项:默认裁剪版本目录中的 `.git` 文件夹以降低磁盘占用

本篇文章聚焦 Apache Airflow 的一项行为变更(对应 [newsfragment](https://link.gitcode.com/i/6d902fc134b350cd1b555fc62de79102)):Git provider 的 `GitDagBundle` 在 Airflow 3 的 DAG Bundle 体系中新增了 `prune_dotgit_folder` 选项,默认会

gitblog_00313的博客 1054

Git -- 如何删除本地仓库

首先可以创建一个文件夹,进入文件夹,并初始化一个本地仓库[python] view plain copy Administrator@GVA7WCIPQXBLZ67 MINGW64 ~  $ mkdir test1    Administrator@GVA7WCIPQXBLZ67 MINGW64 ~  $ cd test1    Administrator@GVA7WCIPQXBLZ67 MING...

bloombud的博客 15万+

实用手册:`.git`

git目录中的文件大致可区分为配置、状态和核心数据三类。就配置而言,是项目级本地配置的核心;用于仓库本地的私有忽略规则;服务于仓库描述展示;则服务于多工作树场景。明确这些文件的职责边界,有助于避免将个人规则误写入共享配置,也有助于准确理解 Git 的配置层级与忽略机制。

言之。 217

CentOS7直接升级Git版本

CentOS7直接升级Git版本

江天飞鸟的博客 945

一篇搞懂Git 和 SVN 的区别

前言 Git和SVN都是版本管理系统,但是他们 命令区别后面会简单进行一个对比,我们先从原理的角度分析 git和svn命令 先来复习哈命令 作用 git svn 版本库初始化 git init svn create clone git clone svn co(checkout) add git add (.除去.gitignore,*所有的文件) svn add commit git commit svn commi.

高先生的猫 2万+

Centos7升级最新git

1. 百度搜索"git官网"(https://git-scm.com/) 2. 点击 "Downloads" 3. 点击“Linux/Unix” 可以看到这里没有Centos升级方法 4. 向下拉页面选择"the IUS Community Project"点击进入 5. 点击"Get Started" 6. 复制运行命令 7. 查看是否安装IUS成功 yum repolist 8. 查看最新版本 yum provides git 9. 安装最新g.

墨痕诉清风的博客 2433

开源项目 `api.git` 使用教程

开源项目 api.git 使用教程 1. 项目介绍 api.git 是一个开源的API开发框架,旨在简化API的创建、管理和维护。该项目提供了丰富的工具和库,帮助开发者快速构建高性能、可扩展的API服务。api.git 支持多种编程语言和框架,适用于各种规模的项目。 2. 项目快速启动 2.1 环境准备 在开始之前,请确保您的开发环境已经安装了以下工具: Node.js (推荐版本: 14.x ...

gitblog_00330的博客 641
上一篇: 从服务器拷文件到本地:scp 与 rsync 实战
下一篇: 【Git基础】02——分支:在不破坏主线的情况下做实验
未收敛
博客等级 码龄11年 3317粉丝 163原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未收敛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值