前面一篇文章介绍了Git对象模型,接下来我们就进入".git"目录看看到底有什么东西,目录中哪些东西又跟Git对象模型相关。结合这个目录,我们将进一步了解Git的工作原理。

.git目录

下面就开始进入.git目录了,通过"ls"命令可以看到.git目录中的文件和子目录:

对于这些文件和目录,下面给出了一些基本的描述。在后面后有logs、objects、refs、index和HEAD更详细的介绍

  • (D) hooks:这个目录存放一些shell脚本,可以设置特定的git命令后出发相应的脚本;在搭建gitweb系统或其他git托管系统会经常用到hook script
  • (D) info:包含仓库的一些信息
  • (D) logs:保存所有更新的引用记录(会在后面介绍引用)
  • (D) objects:所有的Git对象都会存放在这个目录中,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名
  • (D) refs:这个目录一般包括三个子文件夹:heads、remotes和tags,heads中的文件标识了项目中的各个分支指向的当前commit
  • (F) COMMIT_EDITMSG:保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
  • (F) config:这个是Git仓库的配置文件
  • (F) description:仓库的描述信息,主要给gitweb等git托管系统使用
  • (F) index:这个文件就是我们前面文章提到的暂存区(stage),是一个二进制文件
  • (F) HEAD:这个文件包含了一个当前分支(branch)的引用,通过这个文件Git可以得到下一次commit的parent
  • (F) ORIG_HEAD:HEAD指针的前一个状态

Git引用

Git中的引用是个非常重要的概念,对于理解分支(branch)、HEAD指针以及reflog非常有帮助。

Git系统中的分支名、远程分支名、tag等都是指向某个commit的引用。比如master分支,origin/master远程分支,命名为V1.0.0.0的tag等都是引用,它们通过保存某个commit的SHA1哈希值指向某个commit。

重新认识HEAD

HEAD也是一个引用,一般情况下间接指向你当前所在的分支的最新的commit上。HEAD跟Git中一般的引用不同,它并不包含某个commit的SHA1哈希值,而是包含当前所在的分支,所以HEAD直接指向当前所在的分支,然后间接指向当前所在分支的最新提交。

为了更形象的解释上面的描述,我们首先查看".git/HEAD"的内容:

ref: refs/heads/master

这就表示HEAD是一个指向master分支的引用,然后我们可以根据引用路径打开"refs/heads/master"文件,内容如下:

4ea6c317a67e73b0befcb83c36b915c1481f2efe

根据前面一片文章的介绍,我们通过这个哈希值查看对象的类型和内容,可以看到这个哈希值对应一个commit,并且通过"git log"可以发现这个commit就是master分支上最新的提交。

所以可以看到,所有的内容都是环环相扣的,我们通过HEAD找到一个当前分支,然后通过当前分支的引用找到最新的commit,然后通过commit可以找到整个对象关系模型,看下图:

引用和分支

直到现在我们都没有开始介绍分支(branch),这里也不准备介绍分支,只是想大概展示一下引用和分支的关系。

假设我们现在除了master分支,又创建了一个release-1.0.0.1的分支,再次查看".git/refs/heads/"目录,可以看到除了master文件之外,又多了一个release-1.0.0.1文件,查看给文件的内容也是一个哈希值。

通过"git show-ref --heads"命令就可以产看所有的头,这些都是HEAD的候选值:

根据前面的讲解,这个commit就是就是release-1.0.0.1分支上最新的提交。同样,当我们把当前分支切换到release-1.0.0.1的时候,HEAD文件的内容也会相应的变成:

ref: refs/heads/release-1.0.0.1

再看reflog

看过第二篇文章的同学一定还记得我们是怎么根据reflog去得到一个commit哈希值,然后把repo退回到一个指定的状态。

接下了,我们进入".git/logs"文件夹,可以看到这个文件夹也有一个HEAD文件和refs目录,些就是记录reflog的地方。

查看HEAD文件的内容,发现这个文件将会包含所有分支的reflog记录:

 601b527296fea232c84b3661abcbff0576b1272c WilberTian <Wilber***.com>  +    commit (initial): add calc.py into repo
601b527296fea232c84b3661abcbff0576b1272c c2163e267380f71373f29f922e7089abbb741772 WilberTian <Wilber***.com> + commit: add sub function in calc.py
c2163e267380f71373f29f922e7089abbb741772 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> + commit: add app.py, __init__.py and calc.py
4ea6c317a67e73b0befcb83c36b915c1481f2efe 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> + checkout: moving from master to release-1.0.0.1

进入".git/logs/refs"目录,同样会有master和release-1.0.0.1两个文件,两个文件将会保存各自分支的reflog记录

master的内容:

 601b527296fea232c84b3661abcbff0576b1272c WilberTian <Wilber***.com>  +    commit (initial): add calc.py into repo
601b527296fea232c84b3661abcbff0576b1272c c2163e267380f71373f29f922e7089abbb741772 WilberTian <Wilber***.com> + commit: add sub function in calc.py
c2163e267380f71373f29f922e7089abbb741772 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> + commit: add app.py, __init__.py and calc.py

release-1.0.0.1的内容:

 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com>  +    branch: Created from master

Git索引(index)

前面文章我们也提到过index/stage,就是更新的暂存区,下面就来看看index文件。

index(索引)示一个存放了已排序的路径的二进制文件,并且每个路径都对应一个SHA1哈希值。在Git系统中,可以通过"git ls-files --stage"来显示index文件的内容:

从命令的输出可以看到,所有的记录都对应仓库中的文件(包含全路径)。通过"git cat-file"命令查看app.py对应的哈希值,可以看到这个哈希值就是代表app.py的blob对象。

现在我们更新app.py文件,加上一个"div(16, 4)"的调用并通过"git add"添加到暂存区,这时发现index中app.py对象的哈希值已经变化了。

通过这个例子,我们也可以理解diff操作应该会有怎样的输出了:

  • git diff:比较WorkSpace和stage,add之前有diff输出;add之后没有diff输出
  • git diff HEAD:比较WorkSpace和repo,add之前之后都有diff输出
  • git diff --cached:比较stage和repo,add之前没有diff输出;add之后有diff输出

对象的存储

前面提到所有的Git对象都会存放在".git/objects"目录中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。

所以,我们前面提到的master上最新的commit对象的哈希值是"4ea6c317a67e73b0befcb83c36b915c1481f2efe",那么这个对象会被存储在".git/objects/4e/a6c317a67e73b0befcb83c36b915c1481f2efe"。进入objects目录后,我们确实找到了这个文件。

在Git系统中有两种对象存储的方式,松散对象存储和打包对象存储。

松散对象(loose object)

松散对象存储就是前面提到的,每一个对象都被写入一个单独文件中,对象SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。

打包对象(packed object)

对于松散存储,把每个文件的每个版本都作为一个单独的对象,它的效率比较低,而且浪费空间。所以就有了通过打包文件(packfile)的存储方式。

Git使用打包文件(packfile)去节省空间.。在这个格式中,,Git只会保存第二个文件中改变了的部分,然后用一个指针指向相似的那个文件。

一般Git系统会自动完成打包的工作,在已经发生过打包的Git仓库中,".git/objects/pack"目录下会成对出现很多"pack-***.idx"和"pack-***.pack"文件。关于打包就介绍这么多了,暂时还没有去研究两个文件的内容和原理。

总结

这篇文章结合了前一篇的Git对象模型,探索了.git文件夹,通过引用,reflog以及索引的介绍,相信会对Git的工作原理有了更多的了解。

通过这两篇文章介绍下来,感觉对谜一样的Git也慢慢的熟悉了起来。

Git Step by Step – (4) 探索.git目录的更多相关文章

  1. Git Step by Step

    原文地址:http://www.cnblogs.com/wilber2013/category/643754.html 1.Git简介 2.Git本地仓库 3.Git对象模型 4.探索.git目录 5 ...

  2. Git Step by Step – (8) Git的merge和rebase

    前面一篇文章中提到了"git pull"等价于"git fetch"加上"git merge",然后还提到了pull命令支持rebase模式 ...

  3. 探索.git目录

    .git目录 下面就开始进入.git目录,通过“ls”命令可以看到.git目录中的文件和子目录: 对于这些文件和目录,下面给出了一些基本的描述. hooks:这个目录存放一些shell脚本,可以设置特 ...

  4. Git Step by Step – (7) Git远程仓库(续)

    上一篇文章介绍了Git远程仓库的一些使用,但是还是有些东西需要补充一下,所以有了这个续篇. .gitignore 前一篇中,我们介绍了Git的patch功能,当我们生成patch之后,"gi ...

  5. Git Step by Step – (6) Git远程仓库

    前面文章中出现的所有Git操作都是基于本地仓库的,但是日常工作中需要多人合作,不可能一直都在自己的代码仓库工作.所以,这里我们就开始介绍Git远程仓库. 在Git系统中,用户可以通过push/pull ...

  6. Git Step by Step – (5) Git分支(branch)

    在前面两盘文章中介绍了Git的基本原理,都是理论知识.这篇文章我们再次回到实践中,看看Git分支(branch)的使用. 在代码版本控制工具中,都会有branch的概念.刚开始建立版本仓库的时候,我们 ...

  7. Git Step by Step – (2) 本地Repo

    前面一篇文章简单介绍了Git,并前在Windows平台上搭建了Git环境,现在就正式的Git使用了. Git基本概念 在开始Git的使用之前,需要先介绍一些概念,通过这些概念对Git有些基本的认识,这 ...

  8. Git Step by Step – (1) Git 简介

    由于工作的需要,代码版本控制工具要从Perforce换成Git.说实话,刚开始真的很不适应,要从一个可以很好的支持用户界面的工具转到一个命令行工具,而且Git中有几百个命令,一下子就傻眼了. 但是经过 ...

  9. Git Step by Step – (3) Git对象模型

    前面一篇文章介绍了本地仓库的一系列操作,下面我们将进一步了解Git的工作原理,介绍Git对象模型. 刚开始使用Git的时候,对Git对象模型.工作原理并不理解,但是经过一段时间的使用.熟悉之后,然后再 ...

随机推荐

  1. 【C】——线程引入 pthread_self 和 pthread_equal 原因 ——解决不同平台的问题!

    1.引入pthread_equal的原因: 在线程中,线程ID的类型是pthread_t类型,由于在Linux下线程采用POSIX标准,所以,在不同的系统下,pthread_t的类型是不同的,比如在u ...

  2. js 实现遮罩某一部分

    借用一下喜马拉雅的效果图,比较一下下面的遮罩层的高度:       我们可以看到它的遮罩层一直都是只遮住的下面.开始想着怎么用CSS实现,几经测试也没弄出来,如果正在看这篇文章的你,知道如何用CSS达 ...

  3. 微信小程序——时间戳的转换及调用

    开发微信小程序网盘功能模块的时候,需要获取到网盘文件夹创建的时间.如下图: 但是请求返回的数据是一段时间戳,如下图: 所以我们只能通过js把时间戳转换成时间格式. 在小程序官网的Demo的utils. ...

  4. <转> linux进程状态的说明

    我只是做一个mark,为了日后复习:http://blog.csdn.net/tianlesoftware/article/details/6457487 他写得非常的详细,值得推荐. 补充一点什么是 ...

  5. Web服务端开发需要考虑的问题(续)

    方案汇总API设计应用架构代码库管理工具链工作计划目标预期关键过程service-driver接口定义及实现web样例 方案汇总 API设计 基于https. 只提供纯数据. 基于一开始提出的rest ...

  6. Windows 7 incorrectly reports "No Internet Access"

    PROBLEM DESCRIPTION Windows 7 may sometimes report that it has "No Internet Access"; this ...

  7. QTcpSocket使用过程中的一些问题记录

    目前,在将原来C的socket通讯改为使用Qt类库QTcpSocket通讯,在修改过程中遇到不少问题,在此将问题一并记录,以备后面使用. 采用的通讯方式:QTimer定时器.QThread多线程和QT ...

  8. 解决DoubanFM第三方客户端UI线程与工作线程交互问题

    最新文章:Virson's Blog 首先要感谢yk000123的慷慨开源,开源地址见:http://doubanfm.codeplex.com/ 最近正好在学习WPF,然后在Codeplex上找到了 ...

  9. Java如何获取正在运行的线程的名称?

    在Java编程中,如何获取正在运行的线程的名称? 以下示例显示如何获取正在运行的线程的名称. package com.yiibai; public class GettingThreadName ex ...

  10. WCF Service 配置文件注释(转)

    VS 2008 SP1(不确定是否不打SP1是否有)自带的一个编辑工具,可以更快的帮助定制配置文件, 以前看到过没有注意, 昨天正好一个同事提起, 这里记录一笔:打开VS 2008->Tools ...