专栏首页CDN及云技术分享探秘git隐藏文件夹
原创

探秘git隐藏文件夹

一、分布式管理

分布式的版本管理同svn集中式版本管理不同的是,本地维护一个版本库,所以不需要联网服务器就可以做开发版本管理。每个开发者对自己仓库拥有写权限,而对其他所有人仓库的读权限。同时有个代表“官方”项目的权威的仓库。

1.1 git的管理文件夹

在工程会有个管理仓库的文件夹.git。这个git保存了版本管理的所有数据。今天就来探秘这个.git文件夹的设计思想。

图1、git仓库文件夹

1.2 git的版本管理基本知识

  • commit:一次对本地仓库的提交。这个提交有个唯一识别id,最长40位,但是使用这个id,只要位数足够可以唯一代表这个commit,就不一定最长到40位。
  • branch/tag:一次仓库的副本,这个副本有一串历史commit,是仓库的另一种快照,tag是仓库的里程碑。处在分支的代码是安全的,否则不在分支的孤立commit可能被git当垃圾清理掉
  • 文件内容变更:变更保存的是内容的差异值
  • 提交历史:每个commit的有依赖父子关系,形成了一串提交历史
  • 本地/远程:本地对应的是本地仓库,并且维护了一个跟远端的关联关系
  • 映射关系:本地仓库和官方远程仓库进行同步,同步的方法:如分支的映射关系、当前提交

1.3 git仓库配置

git clone xxxx 克隆一个远端分支会产生一个./git/config。一个最简config配置如下:

图2、最简config文件配置

1.3.1 [core]分区

core存的是与分支无关的配置

  • filemode = true #忽略文件权限变化带来的diff
  • repositoryformatversion = 0, 一种配置风格
  • logallrefupdates = true, #记录所有ref的更新,什么是ref。ref是描述当前仓库所有本地分支,每一个文件名对应相应的分支,文件内部存储了当前分支最新的commit hash值。因此新建分支,只要往.git/refs/heads/分支名写入commit hash值就是这么简单。
  • core.bare=false # 默认不创建裸仓库,裸仓库是创建的仓库并不包含工作区 ,在裸仓库上执行Git 命令,而从裸仓库 clone 下来的本地仓库可以进行正常的 push 操作, 但是从一般仓库 clone 下来的本地仓库却不行。 所以裸仓库一般是作为远端的中心仓库。使用 git init --bare <repo> 可以创建一个裸仓库,并且这个仓库是可以被clone 和 push, 裸仓库不包含工作区,所以在裸仓库不能直接提交变更。

1.3.2 [remote]分区、[branch]分区

[remote "origin"]和[branch "master"]指的是本地如何与远程仓库做交互。

图3、remote配置同git fetch/pull命令的联系

[remote]的url选项指定远程拉取地址,fetch的字符串格式是 “+源分支:目的分支"。比如说例子的fetch = +refs/heads/*:refs/remotes/origin/*,意思是refs/heads/目录下的任意分支最新commit都关联到:refs/remotes/origin/*的同名文件。

[branch]使用remote指定master分支关联到remote/origin分支。merge指定了要merge的源。

这两个配置跟git fetch、git pul命令有关系,这两个命令就是在这个配置找映射关系。比如说

  • git fetch orign会查找.git/config文件中的[remote origin]的配置url,按照fetch规则把最新远端所有的分支的commit id更新到./git/refs/remotes/origin文件夹中。
  • git merge会去找./git/refs/remotes/origin/某个分支,合并到refs/heads/某个分支

二、git目录结构

2.1 refs文件夹(分支管理)

图4、本地分支和远端分支(绿色是当前头指针所在的分支)

在refs文件夹存在着3个子文件夹,分别是:

  • .git/refs/heads 本地分支
  • .git/refs/remotes 远端分支,remotes文件夹将所有(比如git remote)命令创建的所有远程分支存储为单独的子目录。在每个子目录中,可以发现被fetch进仓库的对应的远程分支。
  • .git/refs/tags 里程碑分支,描述当前仓库的tag信息,其工作原理与heads一致。
图5、refs目录结构
图6、refs分支heads和remotes的关联关系

git branch创建一个分支(git branch tmp),这里其实就是在ref/heads创建一个文件,这个文件内容是个当前commit id。ref是描述当前仓库所有本地分支,每一个文件名对应相应的分支,文件内部存储了当前分支最新的commit hash值。因此新建分支,只要往.git/refs/heads/分支名写入commit hash值就是这么简单。

图7、ref的叶子文件的内容就是一个commit id

没被关联的分支执行fetch会报错

图8、fetch不到的分支报错

2.2 objects文件夹(文件内容管理)

2.2.1 object角色

git定义的object有3种角色:commit、tree、blob。角色类型可以用git cat-file -t <commit id>找到。角色间的关系如下:commit对应一个tree,tree里面可以包含另一个tree,tree的叶子节点是一个blob,blog代表这个文件的某个版本状态。

图9、commit、tree和blob的关系

白色的节点是blob,blob存储存储的是文件的内容,只要文件内容一样是,不管文件名不一样,blob一样的,这是git的一个设计特点。同一个文件有不同的状态,用version区分,tree表示的git当时的一个快照状态。这个状态描述了各个文件的状态。

tree是文件夹,blob是文件

图10、一个merge的例子
图11、tree的快照
图12、tree和文件夹的关系

2.2.2 objects目录结构

  • objects/ folder存储了对象信息。包括文件内容,提交id,树,和tag里程碑信息。
  • objects/[0-9a-f][0-9a-f] folders

存放文件内容的object,unpacked或者loss 对象,这些对象放在256个子目录。子目录名的前两个字母是commit id的头两个字母,commit id剩下的字符串作为文件名字。

图13、文件夹名字+文件名字=comit ID

2.3 hooks文件夹(脚本设置管理)

钩子函数脚本用于自定义一些git命令执行时候触发的脚本

比如commit、applypatch、push、rebase被执行会触发相关脚本。要使这些生效,把文件的sample后缀去掉

图14、命令触发脚本

2.4 logs文件夹(日志管理)

logs目录夹存储refs文件夹的改变,这些日志包括commit关联关系、提交人、提交时间等,执行git reflog命令可以管理查看这些log

  • logs/refs/heads/ folder 分支管理
  • logs/refs/tags/ folder tag管理

2.5 pack-refs文件(缓存效能管理)

是个git效能优化的文件,文件包含索引并且压缩object,能达到随机访问对象.

branch和tag的变更(统称为 ref)是每个ref在目录下的(子)目录中存储一个文件$GIT_DIR/refs 。尽管许多分支往往会经常更新,但是大多数tag和某些分支从未更新。当存储库具有成百上千个ref时,这种“每个引用一个文件”的格式既浪费存储空间,又损害性能。pack文件夹正是为了解决这个问题而生。

此命令用于通过将ref存储在单个文件中来解决存储和性能问题 $GIT_DIR/packed-refs。当传统$GIT_DIR/refs目录层次结构中缺少ref时,将在此文件中查找该引用并在找到后使用。

分支的持续commit总是在$GIT_DIR/refs目录层次结构下创建新文件 。有过多ref的存储库的做法是将其ref打包--all一次,并偶尔运行git pack-refs。根据定义,tag是固定的,并且不会更改。branch头将带有首字母pack-refs --all,但只有当前分支head将被解包,而下一个pack-refs(不带--all)将使它们解包。

2.6 objects/info文件夹

存储额外扩展信息

info/exclude用于配置本地分支的 例外文件夹,该文件夹不会被git仓库管理。.和gitignore的区别在于,exclude只在本地分支生效,不会被同步到 远端的repo。

2.7 index文件(暂存区管理)

index是个二进制文件夹,对应着暂存区。暂存区:英文叫stage, 或index。一般存放在 ".git目录下" 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。暂存区保存元数据包括时间戳、文件名、SHA id。

图15、暂存区和工作区的关系
图16、仓库示意图

2.8 头指针相关(HEAD、FETCH_HEAD、ORIG_HEAD等)

  • HEAD记录了当前的头指针所对应的commit id。比如说我们想退回到当前提交的前一个提交,就可以这样方便表示:git reset HEAD^
  • FETCH_HEAD记录了远端获取到的状态。最新从远程分支获取的分支。FETCH_HEAD 文件存储的是远程分支的最新的commit信息。
  • ORIG_HEAD记录了HEAD的状态,比如说要进行一些改变头指针的命令。例如像merge、rebase或reset之類
  • 可以中途恢复到原来HEAD状态
  • MERGE_HEAD:执行merge操作的commit id。比如说交互操作时,用于记住这个merge状态。
  • CHERRY_PICK_HEAD:执行cherry_pick,意义同MERGE_HEAD。
  • 类似的还有BISECT_HEAD,REVERT_HEAD,REJECT_NON_FF_HEAD

2.9 modules文件夹

modules包含了第三方的库,比如一个工程包含了另一个repo

图17、module里面展示了另一个repo

2.10 decription文件

decription用于git在web界面用于搜索

三 git的一些设计思想

git是Linux出自同一作者。git的设计者应用现在编程的很多思想。

头指针:HEAD指针,当前仓库所在的commit位置。

git的gc:清理commit,请求文件object

git的文件管理:一个object代表着一个文件的一种版本。

git的缓存:

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • git问题场景和log颜色查看

    正常我们使用git,比如git add ,git commit , git push这些完成个人的代码暂存、修改、提交和推送操作。

    mariolu
  • 也谈谈c语言的协程

    服务器并发场景是在程序IO密集型有优势。因为IO操作速度远没有CPU的计算速度快。程序阻塞IO将浪费大量CPU时间。程序计算密集型的,并发编程反而没有优势。

    mariolu
  • 高效协同开发

    假设服务机器开通sambas服务端口,并且windows防火墙允许访问。这时候可以在windows打开网盘一样,打开sambas共享的服务器文件夹,把代码工程放...

    mariolu
  • 华为路由交换技术 | IPV6技术详解与案例分析

    ipv6 无状态自动配置:PC 会通过发送特定类型的icmp 报文请求路由器接口的前缀,结合自己的mac地址自动生成全球独一无二的ipv6 地址。

    网络技术联盟站
  • Maven仓库安装

    1.昨日内容回顾 商城项目已完结,将在后面的项目实战中回顾。 Maven:项目管理工具。 管理jar包,管理项目 JDK必须先搭建 Maven环境变量...

    ChinaManor
  • jeecg-boot怎么导入到本地运行?

    因为jeecg-boot使用的是前后端分离的。前端使用的是VUE。所以需要按照VUE环境。

    凯哥Java
  • DELL存储SCv2000/2020基础配置与使用

    描述: 最近业务需要需要将原本装有VMware vSphere的机器直接安装Ubuntu 20.04 TLS,并通过SAS线缆从DELL Storage SCv...

    WeiyiGeek
  • git: git add --ignore-removal & git add --all 区别

    在仓库中删除文件后,试图直接用 git add . 将所有删除工作提交暂存区,结果遇到了报错:

    JNingWei
  • 一篇文章,教你学会Git

    在日常工作中,经常会用到Git操作。但是对于新人来讲,刚上来对Git很陌生,操作起来也很懵逼。本篇文章主要针对刚开始接触Git的新人,理解Git的基本原理,掌握...

    java架构师
  • 重量级!Maven史上最全教程,看了必懂

    如果项目非常庞大,就不适合使用package来划分模块,最好是每一个模块对应一个工程,利于分工协作。

    良月柒

扫码关注云+社区

领取腾讯云代金券