二、Git内部原理

1 前言

  • Git使用比较灵活,达到相同结果有多种方式。
  • 靠记忆不同场景下的命令组合,会停留在“知其然,不知其所以然”的层次。
  • 只有理解Git内部原理和Git命令的底层操作,才能深入浅出的灵活运用Git。

2 Git内部原理

Git是由C语言开发的一套内容寻址文件系统,并在此之上提供了一个VCS用户界面。

2.1 Git目录结构

使用git init命令初始化当前目录,生成.git文件夹。

git init 初始化

  1. 工作区、暂存区和Git仓库
    • 工作区是当前目录(除去.git/),所有的编辑操作都在该目录进行。
    • 暂存区对应.git/index文件,它包含了当前暂存区的信息,由它可生成git的tree对象。(git init执行后并没有产生.git/index,而是在首次执行git add命令后才生成,并把由更新文件生成的blob对象放入.git/objects/内。)
      git add生成.git/index
    • git仓库对应.git/,它存储了项目的所有历史快照,以供需要的时候使用。
  2. .git/目录
    .git/包含了以下目录和文件:
    • branches/:新版本不再使用
    • description:仅供GitWeb程序使用
    • config:当前项目的配置选项
    • info/:不同于.gitignore文件,可配置本地的文件忽略模式,不会push到remote库而影响其他人。
    • hooks/:目录存放钩子脚本
    • objects/:目录存储所有数据内容
    • refs/:目录存储指向数据的commit对象的指针
    • HEAD:文件内容为当前分支
    • index:文件内容为暂存区的信息
2.2 Git命令

Git包含底层命令(Plumbing)和高层命令(Procelain)。

  1. 用户平时使用的Git命令一般为高层命令,如add、commit、checkout等;高层命令对用户友好,便于理解和操作。
  2. Git起初被设计为供VCS使用的工具集,这些工具也称为底层命令;底层命令一般不被用户直接使用,而是被shell或脚本调用。
    Git部分底层命令

    此处列举几个底层命令简要说明:

    • checkout-index:Copy files from the index to the working tree.
    • cat-file:Provide content or type and size information for repository objects.
    • hash-object:Compute object ID and optionally creates a blob from a file.
    • update-index:Register file contents in the working tree to the index.
    • write-tree:Create a tree object from the current index.
    • commit-tree:Create a new commit object.
2.3 Git对象

Git定义了4种对象:blob、tree、commit和tag,它们都位于.git/objects/目录下。git对象在原文件的基础上增加了一个头部,即对象内容 = 对象头 + 文件内容。这种格式无法直接通过cat命令读取,需要使用git cat-file这个底层命令才能正确读取。
对象头的格式为:对象头 = 对象类型 + 空格 + 数据内容长度 + null byte,例如一个文件内容为“hello world”,其blob对象头为"blob 11\000"。

git cat-file可读取git的4种对象

  • blob:工作区的文件以blob对象的形式进入git仓库,相当于UNIX中的inodes或文件内容。
  • tree:tree对象包含对blob对象以及其他tree对象的引用,相当于UNIX中的目录。
  • commit:包含了上一次commit对象的Hash串引用、该时间点项目快照的顶层tree对象的Hash串引用、作者/提交者信息、时间戳、空行,以及提交的注释信息。
    commit、tree、blob的引用关系
  • tag:包含一个commit的Hash串引用、标签名,以及其他信息(由标签类型决定)。
2.4 内容寻址
  1. 依赖底层命令git hash-object命令,对文件内容增加头信息后计算hash值并返回,增加-w参数后在git仓库内创建blob对象(blob对象 = 对象头 + 文件内容)。
  2. blob对象存储到git仓库目录(.git/objects/)时,依据40位(16进制字符)长度的hash串指定存储目录(hash串前2位)和命名文件(hash串后38位)。例如某blob对象的hash值为62/0d4582bfbf773ef15f9b52ac434906a3cdf9c3,那么它在git仓库中的路径为.git/objects/62/0d4582bfbf773ef15f9b52ac434906a3cdf9c3
  3. Git内容寻址本质是:Git根据由文件内容(增加文件头)产生的Hash值来标识和索引文件,另外进行命令操作时没有必要写完整的hash串,只要输入的hash串长度是唯一可识别和索引的即可。
    根据文件内容的hash值索引文件
  4. 无需考虑Hash碰撞的情况,在大型项目上也可以放心使用Git。因为在概率上SHA-1产生的哈希值碰撞的机会可以小到忽略。
2.5 Git版本机制
  1. HEAD指向当前分支。若master是当前分支,则HEAD文件内容为ref: refs/heads/master
    HEAD指向当前分支
  2. 分支(本地分支、远程分支、远程跟踪分支、跟踪分支)和标签(tag对象)都包含了对commit对象的引用。
    master分支引用了hash为1ad0的commit对象
  3. commit对象包含了上次commit对象的引用(类似单链表)和本次提交的顶级tree对象的引用。
    commit对象引用了tree对象
    • 每个顶级tree对象可看做是一个完整的版本。
    • 通过commit对象的链式结构进行串联,形成提交历史和版本历史。
  4. 总之:git的分支和标签通过引用commit对象来标注当前分支的版本信息。
    git的版本历史机制

    Note:凡是对Git对象的引用,都指的是Git对象的40位长度的Hash串。

2.6 引用规格(refspec)

引用规格指的是远程仓库分支和本地分支的映射,可表示为<src>:<dst>,这也暗示了数据流向为src → dst

  1. fetch和push命令
# 两命令都包含引用规格(refspec)来指定数据流向。
git fetch [remote repository] [remote branch]:[local branch]
git push [remote repository] [local branch]:[remote branch]
  1. config文件配置refspec
    当使用缺省的fetch/push命令时,Git会根据.git/config中的refspec配置进行操作。
  • 当通过git remote add命令添加一个远程分支的同时,会在.git/config文件中添加一个配置结点。
    git remote add
    fetch中的”+“是可选的,它告诉Git在不能fast-forward的情况下,也强制去更新它。此后执行git fetch orgin这个缺省命令时,会拉取origin远程仓库的所有分支。
  • 可通过git log origin/master来查看从远程仓库fetch的master分支。
# 以下三个命令是等价的,Git会把他们都扩展为refs/remote/origin/master
git log origin/master
git log remote/origin/master
git log refs/remote/origin/master
  • refspec指定分支映射
    1)可通过改写fetch行为fetch = +refs/heads/master:refs/remotes/origin/mymaster,指定把远程的master分支映射为本地的origin/mymaster分支。
    2)也可指定多个映射,一次拉取多个指定分支。
[remote "origin"]
      url = git@github.com:kivihub/test.git
      fetch = +refs/heads/master:refs/remotes/origin/master
      fetch = +refs/heads/experiment:refs/remotes/origin/experiment
      fetch = +refs/heads/qa/*:refs/remote/orgin/qa/*

3)可同时指定push的refspec
若要把本地的master分支push到远程的qa/master分支,可配置如下:

[remote "origin"]
      url = git@github.com:kivihub/test.git
      fetch = +refs/heads/master:refs/remotes/origin/master
      fetch = +refs/heads/experiment:refs/remotes/origin/experiment
      fetch = +refs/heads/qa/*:refs/remote/orgin/qa/*
      push = refs/heads/master:refs/heads/qa/master 

4)删除远程分支
通过命令git push origin :master可以删除远程origin库的master分支。因为refspec的格式为<src>:<dst>,通过把<src>置空表示把远程分支变为空,也就是删除它。

2.7 其他
  1. git gc垃圾回收命令用于压缩或删除数据,节省磁盘空间。
    • 将松散对象进行打包存入packfile。
    • 将不被任何commit引用并且已存在一段时间(数月)的对象删除。
参考文章
  1. GIT科普系列5:index in git
  2. 《Pro Git》相关章节内容
    2.1 Git 内部原理 - 底层命令和高层命令
    2.2 Git 内部原理 - Git 对象
    2.3 Git 内部原理 - Git 引用
    2.4 Git 内部原理 - 包文件
    2.5 Git 内部原理 - 引用规格
    2.6 Git 内部原理 - 传输协议
    2.7 Git 内部原理 - 维护与数据恢复
    2.8 Git 内部原理 - 环境变量
  3. Git命令列表
  4. .gitignore和exclude
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 谢谢一路有你陪伴
    WANCJF阅读 151评论 0 0
  • 看 你看你交的都是什么朋友 手握一百种理由 / 也不愿向你低头 看 你看你交的都是什么朋友 被人把心伤透 / 才回...
    efbec46c9d84阅读 568评论 0 0
  • 在现实生活中,购物车是在超市、商店购物时存放商品的地方,而在网络购物中,是对现实的购物车而喻,买家可以像在超市里购...
    Crazy2015阅读 5,726评论 0 3
  • Javascript是面向对象的语言,所以在Javascript语言中,万物皆是对象:字符串是对象、数字也是对象、...
    Melody_YM阅读 260评论 0 0