浅谈 git 底层工作原理

系统复习到这里也快差不多了,大概就剩下两三个 sections,这里学习一下 git 的 hashing 和对象。

当然,跳过问题也不大。

config 文件

这里还是会用 redux 的项目,先看一下基本信息:

➜  redux git:(master) cd .git
➜  .git git:(master) ls
COMMIT_EDITMSG config         index          objects
HEAD           description    info           packed-refs
ORIG_HEAD      hooks          logs           refs
➜  .git git:(master) cat config
[core]repositoryformatversion = 0filemode = truebare = falselogallrefupdates = trueignorecase = trueprecomposeunicode = true
[remote "origin"]url = https://github.com/reduxjs/redux.gitfetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]remote = originmerge = refs/heads/master

config 文件保存的就是所有的本地文件,如更新了本地的用户名,config 也会有对应的更新:

➜  .git git:(master) cat config
[core]repositoryformatversion = 0filemode = truebare = falselogallrefupdates = trueignorecase = trueprecomposeunicode = true
[remote "origin"]url = https://github.com/reduxjs/redux.gitfetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]remote = originmerge = refs/heads/master
[user]name = GA

下面提一个简单的例子,默认的 git branch 看起来是这个样子的:

通过简单的配置:

[color]ui = true
[color "branch"]local = cyancurrent = yellow bold

可以将 UI 改成下面这个样子:

具体的设定很多,可以看下 reference,如果真的感兴趣的话……有些配置真的可以搞得很风骚,但我确实是用的不太多……

ref

refs 下面有三个文件夹:

  • heads

    保存所有的 head,每个文件都以分支的名称结尾,每个文件里面只包含最后一个 commit 的 hash 值:

    ❯ cd heads
    ❯ ls
    master     new-branch
    ❯ cat master
    49d04ce8e7ec22917d09eb50f61fe464f324b65f
    
  • remotes

    如文件夹名称所示,这里会下载所有的 remotes,每一个 remote 是一个单独的文件夹,每个文件夹下面包含下载的远程分支。

    ❯ cd remotes
    ❯ ls
    origin
    ❯ cd origin
    ❯ ls
    HEAD
    ❯ cat HEAD
    ref: refs/remotes/origin/master
    

    这也是为什么 git 知道分支已经不同了,正巧今天 redux 又做了一个 commit,可以做一个 demo:

    ❯ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 2 commits.
    (use "git push" to publish your local commits)
    ❯ git fetch
    remote: Enumerating objects: 17, done.
    remote: Counting objects: 100% (17/17), done.
    remote: Compressing objects: 100% (17/17), done.
    remote: Total 17 (delta 9), reused 0 (delta 0), pack-reused 0
    Unpacking objects: 100% (17/17), 15.73 KiB | 644.00 KiB/s, done.
    From https://github.com/reduxjs/redux
    662f1ed1..1d0e692e  master     -> origin/master
    ❯ git status
    On branch master
    Your branch and 'origin/master' have diverged,
    and have 2 and 3 different commits each, respectively.
    (use "git pull" to merge the remote branch into yours)nothing to commit, working tree clean
    ❯ cat .git/refs/remotes/origin/master
    1d0e692e5e9c6c030523791b107ca536ca2823fd❯ git log --oneline
    49d04ce8 (HEAD -> master, new-branch) add new message
    11d94c7a add text to readme
    662f1ed1 Merge pull request #4532 from jimhigson/patch-1
    34308acd (tag: v5.0.0-alpha.6) Merge pull request #4531 from MahendraBishnoi29/patch-5
    378c9276 Fix missing call to `getDefaultMiddleware`
    9944978b update React Fragment link
    8782a6e8 update tutorial link HTML & CSS url (#4528)
    

    在拉取了远程的代码后,远程获得了一个新的 commit hash,这个在本地上是没有的,通过对比后,git 能够获得这样的信息:

    通过对比 hash value,git 可以准确的判断,在 662f1ed1 之后,远程和我本地分别有了不同的 commits,而这就造成了不同的分支,因此它提出了 diverged branch 这个说法。

  • tags

    tags 和 refs 相似。

    里面除了保留远程的 tags 之外,还会保留本地的 tags。

    ❯ cd tags
    ❯ ls
    diff-tag       v5.0.0-alpha.6
    ❯ cat diff-tag
    30635355a0f4fb6783420273f8d09c0893d52424
    

HEAD

HEAD 是一个文件追踪目前的 HEAD 指向什么地方,一般是一个 branch,或是一个 commit。

二者的区别在 Git 时间线管理 有提到过。

❯ cat .git/HEAD
ref: refs/heads/master
❯ git checkout new-branch
Switched to branch 'new-branch'
❯ cat .git/HEAD
ref: refs/heads/new-branch
❯ git checkout b05e4325
Note: switching to 'b05e4325'.You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:git switch -c <new-branch-name>Or undo this operation with:git switch -Turn off this advice by setting config variable advice.detachedHead to falseHEAD is now at b05e4325 Release 5.0.0-alpha.5
❯ cat .git/HEAD
b05e43256ec2bed86e1180be978b41313cf6529d

objects

objects 包含了所有 git 的备份文件、commits 等,这些文件通常都是压缩且加密(目前 git 用的是 SHA-1 进行加密)的,加密过后的 40 位的值可以用来获取当时这个 commit 文件,文件包含内容如下:

❯ ls .git/objects
11   1b   23   30   45   63   78   96   b5   b9   df   f4   pack
17   1d   26   31   49   67   7d   a3   b7   dc   ee   info
❯ ls .git/objects/11
d94c7a0fb7ef4fdcefb6e701674c2449804abd
❯ ls .git/objects/pack
pack-6e1431c4d78908ed9d91bfa34c7d22b1da735487.idx  pack-6e1431c4d78908ed9d91bfa34c7d22b1da735487.pack
❯ ls .git/objects/info

其加密的语法位 git hash-object <file>,git 将输入的文件加密,并返回 hashed value 作为 key,这样之后可以回溯这时传过去的文件。

# 仅仅是将其输出到 console,用 -w flag 会将这个 command 会写到 objects 里
❯ echo 'hello' | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a
❯ ls .git/objects
11   1b   23   30   45   63   78   96   b5   b9   df   f4   pack
17   1d   26   31   49   67   7d   a3   b7   dc   ee   info
# 下面的记录会写到 objects 中
❯ echo 'hello' | git hash-object --stdin -w
ce013625030ba8dba906f756967f9e9ca394464a
❯ ls .git/objects
11   1b   23   30   45   63   78   96   b5   b9   dc   ee   info
17   1d   26   31   49   67   7d   a3   b7   ce   df   f4   pack
❯ ls .git/objects/ce
013625030ba8dba906f756967f9e9ca394464a

git 获取了 2 位字符用作文件夹的名称,随后存储了后 38 位作为单独文件的名称,这样通过 文件夹+文件 的方式,git 就能够获取 40 位 16 进制的值作为 key,去回溯当时提交的文件内容。

想要追溯加密的文件可以用 git cat-file <object-hash>,如:

# -p 代表 pretty print
❯ git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
hello
# redux 成员做的 commit
❯ git cat-file -p b951537de944539df9e9526be0429234dc2b7aad
tree 67bdbc53959986be15daf89bc98534f4cdaa5fa1
parent 8782a6e8321cb97ce0e21f77874b360593f4363c
author browny <mahendrabrowny@gmail.com> 1682414114 +0530
committer GitHub <noreply@github.com> 1682414114 +0530
gpgsig -----BEGIN PGP SIGNATURE-----wsBcBAABCAAQBQJkR5oiCRBK7hj4Ov3rIwAANKgIACwdeWIZ6u8W3YOBLNupe8jrB/q/feV5W41G+3+0iCx+JcWtbdCl0H79OCvj0Xm/decgbbf0IO5RYdQ0lOuIXvQukXlNREGTIdlkh7gv2WoRRNjBWL6fQ5LybbmooOcJwCB4f47MMsj5HKnRsKl0XWYWC8qn2eQbs09QST4vv4baldTFPVzW4ajL+KHCwXZG76Ou7OFbPzIR7RGg6QPPK0av3P30HMeXSlDyxaHLkb6OHIEAyK+Yl+nDvLwdORFeu8wTpe2B0MNiZBqugzerBmwfEn8ZOht33YKRGwuXbW42qSI2Hi0BJNgUpCnDFaEDyUl2ezicQxhTQPKhWBXrEVw==qYQZ-----END PGP SIGNATURE-----update thunk link%

为了表示 git 会将当前文件 hash 进去,这里继续举个例子:

❯ echo "new file \n love it" > new.txt
❯ cat new.txt
new filelove it
❯ git hash-object new.txt
dcad5c179ec08ab9dbc57a9dc4be586fb3caaf15
❯ git hash-object new.txt -w
dcad5c179ec08ab9dbc57a9dc4be586fb3caaf15
❯ ls .git/objects/
11   1b   23   30   45   63   78   96   b5   b9   dc   ee   info
17   1d   26   31   49   67   7d   a3   b7   ce   df   f4   pack
❯ ls .git/objects/dc
2c05886aa4658763804b3c295c3e3cadea3414 ad5c179ec08ab9dbc57a9dc4be586fb3caaf15
❯ git cat-file dcad5c179ec08ab9dbc57a9dc4be586fb3caaf15 -p
new filelove it

这也是为什么在之后的 commit 中删除文件,checkout 到之前的 commit 还是可以追溯回当时的版本的原因。

blob

使用命令行保存的二进制文件就是 blog,blob 中不包含文件名,只保存所有的内容。

每一个 blob 也会有自己的 hash value。

tree

tree 解决了文件名的问题——blob 是不保存文件名的,同时它可以存储多个文件——每个 blob 只保存单独文件的内容。git 中所有的文件都会被保存成 blob 或者 tree,tree 可以对标成文件夹,blob 对标为单独的一个文件。

假设保存的文件结构如下:

|- index.html
|- main.js
|- styles
|  |- main.css
|  |- index.css

对应的储存的 blog 结构如下:

#mermaid-svg-k5jn4byWSxr3AYun {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-k5jn4byWSxr3AYun .error-icon{fill:#552222;}#mermaid-svg-k5jn4byWSxr3AYun .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k5jn4byWSxr3AYun .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-k5jn4byWSxr3AYun .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k5jn4byWSxr3AYun .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k5jn4byWSxr3AYun .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k5jn4byWSxr3AYun .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k5jn4byWSxr3AYun .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k5jn4byWSxr3AYun .marker.cross{stroke:#333333;}#mermaid-svg-k5jn4byWSxr3AYun svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k5jn4byWSxr3AYun .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-k5jn4byWSxr3AYun .cluster-label text{fill:#333;}#mermaid-svg-k5jn4byWSxr3AYun .cluster-label span{color:#333;}#mermaid-svg-k5jn4byWSxr3AYun .label text,#mermaid-svg-k5jn4byWSxr3AYun span{fill:#333;color:#333;}#mermaid-svg-k5jn4byWSxr3AYun .node rect,#mermaid-svg-k5jn4byWSxr3AYun .node circle,#mermaid-svg-k5jn4byWSxr3AYun .node ellipse,#mermaid-svg-k5jn4byWSxr3AYun .node polygon,#mermaid-svg-k5jn4byWSxr3AYun .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k5jn4byWSxr3AYun .node .label{text-align:center;}#mermaid-svg-k5jn4byWSxr3AYun .node.clickable{cursor:pointer;}#mermaid-svg-k5jn4byWSxr3AYun .arrowheadPath{fill:#333333;}#mermaid-svg-k5jn4byWSxr3AYun .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k5jn4byWSxr3AYun .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k5jn4byWSxr3AYun .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-k5jn4byWSxr3AYun .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-k5jn4byWSxr3AYun .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k5jn4byWSxr3AYun .cluster text{fill:#333;}#mermaid-svg-k5jn4byWSxr3AYun .cluster span{color:#333;}#mermaid-svg-k5jn4byWSxr3AYun div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-k5jn4byWSxr3AYun :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

index.html
main.js
styles
main.css
index.css
tree
blob
blob
tree
blob
blob

每个 tree 都会保存对应 blob 和 tree 的引用,除此之外还会保存 mode、type 和文件名,如:

mode type hashed value filename
100644 blob e03d902a8d2a53c889cc129bda1b0ce2cf718a67 .babelrc.cjs

查看当前 tree 的指令为 git cat-file -p <branch | commit hash>^{tree},如:

❯ git cat-file -p master^{tree}
100644 blob e03d902a8d2a53c889cc129bda1b0ce2cf718a67    .babelrc.cjs
040000 tree a2f9306f466054b9d6bac7fec0a5ea8b4eb13637    .codesandbox
100644 blob ed5699bd11a460bab0476e42d9d879ffd7d0580b    .editorconfig
100644 blob efac9cb61b3e7b4501710a9c70ddf81c7e41d7bb    .eslintignore
100644 blob 8f8944f970ee5c1ae83432ec7a1822a56d43cc4d    .eslintrc.cjs
100644 blob 72cfe1819b98b78c1cb600bcbf46702673077b34    .git-blame-ignore-revs
100644 blob cc4ea460c9dc256ad6d38bd605006239d23b1541    .gitbook.yaml

commit

commit 的结构稍微复杂一些,它会包含更多的信息:tree(当前 repo 下的所有内容),parent,author,committer,message,如上面查看过的:

❯ git cat-file -p b951537de944539df9e9526be0429234dc2b7aad
tree 67bdbc53959986be15daf89bc98534f4cdaa5fa1
parent 8782a6e8321cb97ce0e21f77874b360593f4363c
author browny <mahendrabrowny@gmail.com> 1682414114 +0530
committer GitHub <noreply@github.com> 1682414114 +0530
gpgsig -----BEGIN PGP SIGNATURE-----wsBcBAABCAAQBQJkR5oiCRBK7hj4Ov3rIwAANKgIACwdeWIZ6u8W3YOBLNupe8jrB/q/feV5W41G+3+0iCx+JcWtbdCl0H79OCvj0Xm/decgbbf0IO5RYdQ0lOuIXvQukXlNREGTIdlkh7gv2WoRRNjBWL6fQ5LybbmooOcJwCB4f47MMsj5HKnRsKl0XWYWC8qn2eQbs09QST4vv4baldTFPVzW4ajL+KHCwXZG76Ou7OFbPzIR7RGg6QPPK0av3P30HMeXSlDyxaHLkb6OHIEAyK+Yl+nDvLwdORFeu8wTpe2B0MNiZBqugzerBmwfEn8ZOht33YKRGwuXbW42qSI2Hi0BJNgUpCnDFaEDyUl2ezicQxhTQPKhWBXrEVw==qYQZ-----END PGP SIGNATURE-----update thunk link%

其中 gpgsig 以下的部分是实际的 commit message,PGP SIGNATURE 是一个 e-verify 加密,redux 除了 git 之外应该还有其他的验证。

reference

  • git-config - Get and set repository or global options

  • Github merkle DAG

  • How is git commit sha1 formed

  • How is the Git hash calculated?

  • Git Internals - Git Objects

浅谈 git 底层工作原理相关推荐

  1. 浅谈人工智能的工作原理

    众所周知人工智能现在快速发展,并且为众人所熟知,不仅如此,人工智能也在各行各业中广泛使用.那么人工智能的工作原理是什么呢? 浅谈人工智能的工作原理 人类智能由三个部分构成(还有些其他生物学和科学现象也 ...

  2. 伺服驱动器生产文件_浅谈伺服驱动器的工作原理

    原标题:浅谈伺服驱动器的工作原理 目前,主流的伺服驱动器均采用数字信号处理器(DSP)作为控制核心,可以实现比较复杂的控制算法,实现数字化.网络化和智能化.功率器件普遍采用以智能功率模块(IPM)为核 ...

  3. 11旋转编码器原理图_plc编程入门:浅谈编码器的工作原理!

    编码器(encoder)是将信号(如比特流)或数据进行编制.转换为可用以通讯.传输和存储的信号形式的设备.编码器把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺. 编码器主要是由码盘(圆光栅 ...

  4. 1流式细胞术荧光比值计算_浅谈流式细胞仪的工作原理和应用

    流式细胞术(Flow Cytometry, FCM)是七十年代发展起来的一项高科学技术,80年代开始从基础研究发展到临床医学研究及疾病的诊断和治疗监测,我国在80年代初引进了第一台流式细胞仪. 它集光 ...

  5. 浅谈航管二次雷达工作原理

    航管二次雷达工作原理与应用 1.航管二次雷达简介: 航管应答一般称为空中交通管制雷达系统(Air Traffic Control Radar Beacon System)或二次航管雷达,是用来提供地面 ...

  6. 高效学习传感器|浅谈CCD的工作原理以及常用波

    介绍了CCD的MOS光敏单元的结构.CCD电荷存储的原理.CCD电荷转移的原理.CCD信号电荷的输出等知识.介绍了声波和电磁波的基础知识,可以了解声波和电磁波的异同,了解声波和电磁波的波段划分,了解不 ...

  7. 浅谈Git的基本工作流程与简单的Git命令

    浅谈Git的基本工作流程与简单的Git命令 什么是GIT? 定义:是一个开源的分布式版本控制工具 功能: 代码备份 版本控制 协同开放 代码追溯 场景: 团队协同开放项目 Git仓库工作流程 Git常 ...

  8. 深入浅谈,CPU设计原理

    首先,声明这是一篇转载文,这篇文章是,从卡饭论坛 看到的一篇文章<深入浅谈,CPU设计原理>,是一篇连载,文章,卡饭论坛,是我高中的时候,经常去的论坛,里面有很多好的文章,推荐给大家.也许 ...

  9. 动态磅是怎么原理_浅谈动态地磅的原理及未来发展方向

    浅谈动态地磅的原理及未来发展方向: 文章介绍了动态地磅的结构和工作原理,针对动态地磅的分类做了全面的概述,分别对不同的动态地磅做了对比及详细的阐述,说明选择和使用动态地磅器的注意事项,凸显了轴组式动态 ...

最新文章

  1. SQL与NoSQL区别-扩展方式
  2. [新功能]根据预览图片选择Skin
  3. mysql按月分列统计_实现mysql按月统计的教程
  4. 面向对象之迪米特法则
  5. Android之 FLAG_ACTIVITY_CLEAR_TASK
  6. 从浏览器端JavaScript代码进行服务器端日志记录
  7. c++程序设计中多态与虚函数知识点
  8. SpringCloud 从菜鸟到大牛之一 微服务介绍
  9. 基于DEAP库的python进化算法-6.遗传算法中的约束处理
  10. 电工学习笔记————稳压二极管伏安特性
  11. 一套很好的51单片机教程,云龙51单片机视频教程(王云)
  12. OsiriX User Manual - 3D Curved MPR(曲面重建)
  13. 【转】D3DXLoadSkinMeshFromXof函数及.x在不同dx版本中
  14. 汽车零部件智能工厂MES生产进度管理系统
  15. Digispark ATtiny85 单片机点灯大师之圣诞节彩灯Merry Christmas
  16. java7 diamond_java7新特性之Diamond syntax
  17. aics6圆角插件怎么安装_AI圆角插件!助你更快设计字体 Xtream Path1.4的安装使用教程...
  18. 关于树莓派DSI屏幕触摸不准的问题
  19. 离散实验一 油管铺设 (求最小生成树的Prim算法的实际应用)
  20. js实现点击按钮,弹出新窗口

热门文章

  1. c语言判断二级指针是否为空,真正明白c语言二级指针
  2. 雷达图像中地物目标的极化信息
  3. python开源web项目-Python开源项目Top30
  4. Jmeter压力测试报告案例
  5. 免费的项目管理软件有哪些
  6. Matlab 小球落地问题
  7. AutoHotkey2的helloworld
  8. 实现倒计时的几种方案汇总
  9. NiFi Processors之ReplaceText
  10. ‘annotationProcessor‘ dependencies won‘t be recognized as kapt annotation processors. Please change