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

从不一样的视角了解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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyggx.html