本文由云+社区发表

作者:腾讯工蜂用户:王二卫

从不一样的视角了解git,以便更好的使用git

一、git & git 版本库认识

git 是一个内容寻址的文件系统,其核心部分是一个简单的键值对数据库(key-value data store),可以向该数据库插入任意类型的内容,它会返回一个40位长的哈希键值。并在此基础上提供了一个版本控制系统的用户界面。

git 版本库其实只是一个简单的数据库,其中包含所有用来维护与管理项目的修订版本和历史信息。其不同于subversion,git版本库不仅提供版本库中所有文件的完整副本,还提供版本库本身的副本。在git版本库中,git维护两个主要数据结构:对象库(object store),索引(index)。

从整体来看,一个项目的git仓库,就如一张带节点的渔网(该渔网是一张有向网),随着项目的不断推进,该渔网也将不断的向四周扩散。

渔网上的节点就像一个个的提交,从某一个正常的节点都能漫游至项目最开始的起点。而分支就如该网上不同节点上的一个特殊标记,分支的演变就是该标记不断的移至其他节点。 分支的合并,根据合并方式的不同,使得这一张网的交叉紧密度越来越高。

1.1git对象类型

对象库是git版本库实现的心脏,包含四种类型:

块(blob,binary lare object),文件的每一个版本表示为一个块。一个blob被视为一个存储任意数据,且内部结构被程序忽略的变量或文件的黑盒。一个blob保存一个文件的数据,但不包含任何关于这个文件的元数据(Metadata,描述数据的数据)。

目录树(tree), 一个目录树对象代表一层目录信息。它记录blob标识符、路径名和在一个目录里所有文件的一的元数据。它也可以递归引用其他目录树或子树对象,从而建立一个包含文件和子目录的完整层次结构。

提交(commit),一个提交对象保存版本库中每一次变化的元数据,每一个提交对象指向一个目录树对象,这个树对象在一张完整的快照中补货提交时版本库的状态。

标签(tag) ,一个标签对象分配一个可读的名字给一个特定的对象,通常是一个提交对象。

为了有效的利用磁盘空间和网络带宽名,git把对象压缩并存储在打包文件(pack file)里,这些文件也在对象库里。

1.2索引

索引是一个临时的、动态的二进制文件,不包含任何文件内容,它仅仅追踪你想要提交的那些内容。使得开发的推进与提交的变更之间能够分离开来。

1.3引用

引用(ref)是一个保存SHA-1值的文件,该文件的名字指针来替代原始的SHA-1值,一般指向提交对象。本地分支名称、远程跟踪分支名称和标签名都是引用。

.git/refs
.git/refs/heads
.git/refs/tags

1.3.1 创建一个引用

$ echo “1a410efbd13591db07496601ebc7a059dd55cfe9” > .git/refs/heads/master

现在可以通过新建的引用来代替SHA-1的值: $ git log —pretty=oneline master 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit cac0cab538b970a37ea1e769cbbde608743bc96d second commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

不提倡直接编辑引用文件,可以通过update-ref更新某个引用 $ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9

比如新建一个分支(git分支的本质:一个指向某一系列提交之首的指针或引用) $git update-ref refs/heads/feature-zhangsan cac0ca

1.3.2 符号引用

符号引用(symbolic reference),间接指向git对象,其实际也是一个引用,不像普通引用那样包含一个SHA-1值,它是一个指向其他引用的指针。 git自动维护几个用于特定目的的特殊符号引用,这些引用可以在使用提交的任何地方使用。

  • HEAD 始终指向当前分支的最近提交,不像普通引用那样包含一个 如: $ cat .git/HEAD ref: refs/heads/master

若执行 $ git checkout test,git会这样更新HEAD文件 ref:refs/heads/test

  • ORIG_HEAD 某些操作(如:merge、reset),会把调整为新值之前的先前版本的HEAD记录到OERG_HEAD中,只用其可以恢复或回滚之前的状态或做个比较
  • FETCH_HEAD git fech命令将所有抓取分支的头记录到.git/FETCH_HEAD中
  • MERGEHEAD 正在合并进HEAD的提交

1.3.3 远程引用

如果你添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在 refs/remotes 目录下。 如:$cat .git/refs/remotes/origin/master ca82a6dff817ec66f44342007202690a93763949 发现添加的远程origin远程库的master分支锁对应的SHA-1值,就是最近一次与服务器通信时master分支所对应的SHA-1值。 远程引用和分支(位于 refs/heads 目录下的引用)之间最主要的区别在于,远程引用是只读的。 虽然可以git checkout 到某个远程引用,但是 Git 并不会将 HEAD 引用指向该远程引用。 因此,你永远不能通过commit 命令来更新远程引用。 Git 将这些远程引用作为记录远程服务器上各分支最后已知位置状态的书签来管理。

二、git底层命令

  • cat-file 展示git仓库对象实体的类型、大小和内容
  • ls-remote 显示远程库信息
  • ls-files 显示由工作目录中添加到缓存中的文件的相关信息
  • ls-tree 列出树对象内容
  • read-tree 将给出的树写入索引但不写入缓存
  • write-tree 按照索引区内容创建树对象
  • symbolic-ref 同步引用信息
  • update-index 更新树对象内容至索引

三、.git 结构说明

  • HEAD 指示目前被检出的分支
  • index 保存暂存区信息
  • config* 包含项目特有的配置选项
  • description 仅供gitweb程序使用,用户一般不需要关注。
  • hooks 包含客户端和服务端的钩子
  • info 包含全局排除(global excude)文件,存放那些不希望被记录在.gitignore中的忽略模式
  • objects 存储所有数据内容
  • refs 存储指向数据(分支)的提交对象的指针

四、git 版本演变

准备工作:创建一个没有任何文件的git初始库 $ git init test Initialized empty Git repository in /data/work/test/test/.git/

4.1 git数据存储演示

  • hash-object 存储任意类型数据至数据库,并返回hash 键值

$ echo ‘test conten’ | git hash-object -w —stdin

d670460b4b4aece5915caf5c68d12f560a9fe3e4

   -w 执行写入数据库操作,若不指定该选项,只会返回hash,不会写入数据库。--stdin 标准输入输出读取默认存入是blob类型,通过-t 参数指定

$ find .git/objects/ -type f .git/objects//d6/870460b4b4aece5915caf5c68d12f560a9fe3e4

  • 一个文件对应一条内容,这个内容的名称以该文件内容加上特定头部信息一起的sha-1校验和。

头部信息-对象类型(blob或tree或commit)+一个空格+数据内容长度+一个空字节 git 会通过zlib将文件内容和头部信息拼接一起的内容进行压缩写入磁盘某个对象,并用计算出的sha-1值的前两个字符串作为目录名称,后38个字符串作为子目录内文件的名称。

$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4

test content

4.2 简单版本控制演示

4.2.1 创建初始版本

$ echo ‘version 1’ > test.txt

$ git hash-object -w ./test.txt 83baae61804e65cc73a7201a7252750c76066a30

4.2.2 更新版本

$ echo ‘version 2’ > test.txt

$ git hash-object -w ./test.txt 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

此时数据库已经存储了test.txt两个不同的版本,如下:

$ find .git/objects/ -type f .git/objects//1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a .git/objects//83/baae61804e65cc73a7201a7252750c76066a30

可以通过cat-file -p查看内容,以上都是数据(blob)对象。可以使用 cat-file -t查看。

4.3 树对象引入

树对像(tree object) 解决文件名和目录保存问题。一个树对象包含了一条或多条树对象记录,每条记录包含一个指向数据对象或子树对象的sha-1指针,以及相应的模式/类型/文件信息。

如下所示:


$ git cat-file -p master^{tree}

100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README 100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib

$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb

master^{tree}指向master分支最新提交所指的树对象。 数据对象几种类型

  • 100644: 表示一般文件
  • 100755: 表示可执行文件
  • 120000: 表示 指针
  • —add: 将未跟踪文件加入缓存区
  • —cacheinfo 将数据对象文件加入工作区

4.3.1 将文件加入暂存区

$ git update-index —add —cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt

4.3.2 生成树对象

创建第一个树 $ git write-tree 将暂存区内容生成一个树对象,并输出树对象SHA-1 d8329fc1cc938780ffdd9f94e0d364e0ea74f579

4.3.3 演变一个复杂的树

$ echo ‘new file’ > new.txt

$echo ‘test file2’ > test.txt

$git update-index —cacheinfo 100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt

$ git update-index test.txt

$ git update-index —add new.txt

创建第二个树

$ git write-tree 0155eb4229851634a0f03eb265b69f5a2d56f341

$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt

此时发现,第一个树丢了,并没有跟第一个树有关系,通过 read-tree进行链接 $ git read-tree —prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579

$ git write-tree 3c4e9cd789d88d8d89c1073707c3585e41b0e614

$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614 040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt

4.3.4 查看我们生成的树

4.4 提交对象引入

通过commit对象将这些树对象串起来。 创建第一个提交 $ echo ‘first commit’ | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 fdf4fc3344e67ab068f836878b6c4951e3b15f3d

创建第二个提交 $ echo ‘second commit’ | git commit-tree 0155eb -p fdf4fc3 cac0cab538b970a37ea1e769cbbde608743bc96d

创建第三个提交 $ echo ‘third commit’ | git commit-tree 3c4e9c -p cac0cab 1a410efbd13591db07496601ebc7a059dd55cfe9

版本库目录变化` **$ find .git/objects -type f** .git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2 .git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3 .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2 .git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3 .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1 .git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2 .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # ‘test content’ .git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1 .git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt .git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1 `提交版本图

没有执行read-tree
$ git log --stat 92387
commit 923879712b02f980a2edbe1cee315d883ee72503
Author: erweiwang <erweiwang@tencent.com>
Date:   Tue Jul 17 15:55:53 2018 +0800second commitnew.txt  | 1 +test.txt | 2 +-2 files changed, 2 insertions(+), 1 deletion(-)commit e624badd39a25484a08ae74231be65ea50a0fe32
Author: erweiwang <erweiwang@tencent.com>
Date:   Tue Jul 17 15:54:20 2018 +0800first committest.txt | 1 +1 file changed, 1 insertion(+)

五、包文件

Git 最初向磁盘中存储对象时所使用的格式被称为“松散(loose)”对象格式。 但是,Git 会时不时地将多个这些对象打包成一个称为“包文件(packfile)”的二进制文件,以节省空间和提高效率。 当版本库中有太多的松散对象,或者你手动执行 git gc 命令,或者你向远程服务器执行推送时,Git都会这样做。

git 打包对象时,会查找命名及大小相近的文件,并只保存文件不同版本之间的差异内容和文件最新版本的完整内容。

六、引用规格

引用规格的格式由一个可选的 + 号和紧随其后的 : 组成,其中 是一个模式(pattern),代表远程版本库中的引用; 是那些远程引用在本地所对应的位置。 + 号告诉 Git 即使在不能快进的情况下也要(强制)更新引用。

[remote "origin"]url = https://github.com/schacon/simplegit-progitfetch = +refs/heads/*:refs/remotes/origin/*

如果想让git每次只拉取远程master分支,而不是所有分支,可以将引用规格那一行修改为: fetch = +refs/heads/master:refs/remotes/origin/master

七、git clone代码库过程

执行git clone后,

  • 拉取info/refs文件 => GET info/refs ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
  • 确定HEAD引用,明确检出至工作目录的内容 => GET HEAD ref: refs/heads/master 以上说明完成抓取后需要检出master分支
  • 从info/refs文件中所提到的ca82a6提交对象开始 => GET objects/ca/82a6dff817ec66f44342007202690a93763949 (179 bytes of binary data)
  • 根据ca82a6提取的的父提交对象和树对象开始遍历整个完整版本库。

在遍历过程中,若是未能直接找到(非松散对象)某些对象,会去替代版本库或某个包文件获取。

八、git推送远端库过程

为了上传数据至远端,Git 使用 send-pack 和 receive-pack 进程。 运行在客户端上的 send-pack 进程连接到远端运行的 receive-pack 进程。

九、扩展知识

9.1维护

git gc —auto //整理松散对象并放置包文件,将多个包文件合并为一个大的包文件,移除与任何提交不相关的陈旧对象

9.2数据恢复

  • 确定需要恢复的版本 git reflog 查看git默默记录的每一次你改变的HEAD的值。 git log -g 可以详细的查看引用日志中各个版本的信息,风方便确定要恢复的提交。 如下所示 commit 1a410efbd13591db07496601ebc7a059dd55cfe9 Reflog: HEAD@{0} Reflog message: updating HEAD third commit commit ab1afef80fac8e34258ff41fc1b867c702daa24b Reflog: HEAD@{1} Reflog message: updating HEAD modified repo.rb a bit
  • 创建用于恢复的临时分支

$ git branch recover-branch ab1afef

  • 通过git fsck检查数据库的完整性(当reflog 也不存在需要恢复的版本)

当引用日志所在目录.git/logs/ 被不小心清空时

$ git fsck —full Checking object directories: 100% (256/256), done. Checking objects: 100% (18/18), done. dangling blob d670460b4b4aece5915caf5c68d12f560a9fe3e4 dangling commit ab1afef80fac8e34258ff41fc1b867c702daa24b dangling tree aea790b9a58f6cf6f2804eeac9f0abbe9631e4c9 dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293

9.3移除对象

该操作使用须谨慎,会导致提交历史不被重写。应用场景,必须对已上库的某些文件(因文件太大或保密信息)进行彻底移除可以使用。

  • 定位出问题文件名 保密文件一般是已知的,若是误提交的文件较大需要删除,但又不知道是哪些文件,且又执行过git gc可以通过类似以下命令定位: $ git verify-pack -v .git/objects/pack-29…69 .idx | sort -k 3 -n | tail -3** dadf7258d699da2c8d89b09ef6670edb7d5f91b4 commit 229 159 12 033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5 blob 22044 5792 4977696 82c99a3e86bb1267b236a4b6eff7868d97489af1 blob 4975916 4976258 1438 **$ git rev-list —objects —all | grep 82c99a3 82c99a3e86bb1267b236a4b6eff7868d97489af1 git.tgz
  • 从过去所有树中移除这个文件 查看哪些提交对这个文件做过改动 $ git log —oneline —branches — git.tgz** dadf725 oops - removed large tarball 7b30847 add git tarball 从7b30847之后的所有提交历史中完全移除该文件 **$ git filter-branch —index-fileter ‘git rm —ignore-unmatch —cached git.tgz’ — 7b30847^… Rewrite 7b30847d080183a1ab7d18fb202473b3096e9f34 (1/2)rm ‘git.tgz’ Rewrite dadf7258d699da2c8d89b09ef6670edb7d5f91b4 (2/2) Ref ‘refs/heads/master’ was rewritten --index-filter 只修改暂存区或索引中的文件 --cached 需要从索引中移除,使得在运行过滤器是,并不会将每个修订版本检出到磁盘 --ignore-unmatch 如果尝试删除的模式不存在时,不提示错误 filter-branch 用于指定从那个提交以来的历史
  • 重新打包日志 执行上面操作,本地历史不在包含那个文件的引用,但是,引用日志和 .git/refs/original 通过 filterbranch选项添加的新引用中还存有对这个文件的引用,必须移除它们后重新打包数据库。 $ rm -Rf .git/refs/original $ rm -Rf .git/logs/** $ git gc
  • 彻底移除$ git prune --expire now $ git count-objects -v

此文已由腾讯云+社区在各渠道发布

获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号

想晋级高级工程师只知道表面是不够的!Git内部原理介绍相关推荐

  1. 人类想要拥有金钱、权力、美丽、永生、幸福……但海龟只想做一只海龟

    人类想要拥有金钱.权力.美丽.永生.幸福--. 但海龟只想做一只海龟! 写这篇文章的初心,原是为了介绍5月23日(世界海龟日)在广东被放归大海的那些乌龟壳上的二维码(兼具"导航"和 ...

  2. python123百钱买百鸡_求解百钱买百鸡问题。假设大鸡 5 元一只,中鸡 3 元一只,小鸡 1 元三只,现有 100 元钱想买 100 只鸡,有多少种买法?截图代码和运行结果_学小易找答案...

    [填空题]表达式 'apple.peach,banana,pear'.find('ppp') 的值为 ________ . [填空题]已知 x = '123' 和 y = '456' ,那么表达式 x ...

  3. 本周AI热点回顾:「时空版」Transformer训练速度远超3D CNN;拒绝内卷的AI狼火了!不想抓羊只想躺!...

    ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍点击左上方蓝字关注我们 01 「时空版」Transformer训练速度远超3D CNN,提速3倍! Facebook AI推出了全新的视频理解架构TimeSform ...

  4. 题目95:百鸡问题:一只公鸡值5元,一只母鸡值3元,而1元可买3只小鸡,用百元买百鸡。现有n元钱,想买n只鸡。问有多少种买法?(钱要用完)

    题目转载:http://python.wzms.com/s/1/85 题目描述: 百鸡问题:一只公鸡值5元, 一只母鸡值3元,而1元可买3只小鸡,用百元买百鸡. 现有n元钱,想买n只鸡. 问有多少种买 ...

  5. 在一个笼子里同事养着一些鸡和兔子,你想了解有多少只鸡和兔,主任对你说:我只告诉你鸡和兔的总头数是16和总脚数是40,你能不能自己计算有多少只鸡和多少只兔?

    帮一个小妹妹解决了一个C语言的编程题,题目如下 在一个笼子里同事养着一些鸡和兔子,你想了解有多少只鸡和兔,主任对你说:我只告诉你鸡和兔的总头数是16和总脚数是40,你能不能自己计算有多少只鸡和多少只兔 ...

  6. 题目54:百钱百鸡 一只公鸡值5元,一只母鸡值3元,而1元可买3只小鸡,用百元买百鸡。现有n元钱,想买n只鸡。问有多少种买法?(钱要用完)

    题目转载:http://python.wzms.com/s/1/50 题目描述: 百鸡问题:一只公鸡值5元, 一只母鸡值3元,而1元可买3只小鸡,用百元买百鸡. 现有n元钱,想买n只鸡. 问有多少种买 ...

  7. 我想成为一只IT小小鸟

    我想成为一只IT小小鸟 --读<我是一只IT小小鸟>有感 书上讲的很多的人和事其实和我的现实都很相识,可是我自己在这样的环境下,我真的是很担心自己以后能不能成为一个IT人士.自己也抱怨学校 ...

  8. 百钱买百鸡python编程列表推导式_使用循环和列表推导式两种方法求解百钱买百鸡问题。假设大鸡5元一只,中鸡3元一只,小鸡1元三只,现有100元钱想买100只鸡,有多少种买法?...

    [程序题]编写一个函数,输入n为偶数时,调用函数求1/2+1/4+...+1/n,当输入n为奇数时,调用函数 1/1+1/3+...+1/n [单选题]患者男,67岁.确诊为原发性支气管肺癌,为行手术 ...

  9. 数字时代,你想成为一只“弱鸡”,还是一个“超级个体”?

    电话延伸了人类的耳朵,屏幕延伸了人类的眼睛,汽车这样的交通工具延伸了人类的腿脚,人类的生存能力开始变得和技术相关,而这个趋势仍在加剧. 如今,Web3延伸了人的综合体验,AI延伸了人类的大脑,它们正以 ...

最新文章

  1. 250相当于什么显卡_GTX1660Ti显卡搭配知识:GTX1660Ti配什么CPU和主板?
  2. simpack导入实际线路激励
  3. 全球及中国生物质发电行业项目可行性及十四五运营前景研究报告2022-2027年
  4. python安装email模块_Python使用SMTP模块、email模块发送邮件
  5. java注册登录客户端_GitHub - a-voyager/LoginSystem_Client: 登录注册系统(桌面客户端)——Java课程实践...
  6. 解决研发管理问题的途径
  7. JavaScript之实例练习(正反选、二级联动)
  8. ajax中json响应
  9. LINUX doubango编译中prefix参数无效
  10. Golang操作数据库
  11. 常用的工业控制计算机有哪几类,工业控制计算机在行业应用中都有哪些特点?...
  12. Chapter3.3:时域分析法
  13. 数据结构视频教程 -《[北大张铭 精品课程版]数据结构与算法(C++)》
  14. 3d工口医Android,3d口工医完整版
  15. Python数据库sqlite3详解
  16. Debian 10上设置和配置证书颁发机构(CA)
  17. ESXi虚拟机磁盘格式转换与减小硬盘容量的方法
  18. 高级查询组件下拉框联动(三)
  19. 在一张表里实现省市区镇村五级联动
  20. 离职前一定要删除这几个文件,不然你的微信聊天记录全被别人看了

热门文章

  1. kali linux 安装搜狗输入法(解决安装后只有搜狗五笔的问题)
  2. 自动进行统计分析的工具
  3. ffplay flv mp4 转_ffmpeg转换mp4到flv的使用笔记
  4. sdcms实现根据不同的分类下边显示子分类的导航
  5. 小米2013校园招聘笔试题
  6. amd显卡没有屏幕旋转快捷键,自己写一个
  7. 如何做微博营销 你知道吗?
  8. InfiniBand网络简介
  9. 同步助手java_QQ同步助手Java版发布
  10. [转贴]大量.net学习资源