总结:阅读这篇文章需要20分钟

本文是转载自 滴滴WebApp架构组 的一篇文章,文章讲解了神秘的.git目录下的一些文件,最终阐述了git是如何存储数据,及git分支的相关内容。

git如何存储数据:

1.   .git文件夹下的objects文件夹存储了git文件系统的一些对象,他们的名称都是40位哈希值,

被分为:

  文件对象 blobl object
  树对象 tree object
  提交对象 commit object

2.通过某次提交对应的树对象可以找到该次提交的所有内容

git log命令可以查看到 commit object,通过提交对象,可以拿到上面记录的 tree object

git的分支:

分支是一个可变指针,指向分支最新的提交对象

1. .git文件夹下的refs文件夹存储了git分支的相关文件,

heads文件夹存储了本地分支,remotes文件夹存储了远程分支,

2. .git文件夹下的HEAD文件指向当前检出的分支或提交对象。

正文:

我们在切换不同分支,或者切换到指定历史提交记录时,Git 是怎么把文件的历史版本调出来的?也就是说:

  1. Git 把数据存储在哪里?
  2. 怎么存储文件的不同版本?
  3. 又是怎么让不同版本和指定提交记录关联起来的?

在开始前,为了避免部分歧义,先统一下对提交记录的认识:我们都知道每个提交记录都会有唯一对应的一个 40 位字符串,这是 Git 根据我们的提交内容利用 SHA-1 算法计算出来的哈希值。这个 40 位的字符串,有人称 id 或 哈希值 或 校验和 或 SHA-1 值。因为 SHA-1 是一种哈希算法,这里我们统一下,这个字符串就叫哈希值

Git 如何存储数据

Git 如何存储数据,答案主要就在 .git 文件夹的 objects 目录里。先不解释 objects 目录是什么,我们先看看里面都有什么东西。现在你可以随意打开一个 Git 仓库,找到其中 .git 目录(默认情况下它可能是隐藏的)下的 objects 文件夹。他应该是下面这样的:

这里面是很多以 2 个字符命名的文件夹,文件夹里又是若干数量以一串 38 个字符命名的文件。如果你足够敏感的话,或许能猜到这是一个类似指定 commit 提交记录的哈希值。借助 git cat-file 命令可以查看这个文件里的内容。结果如下所示:

 

这里的结果是我项目中 CHANGELOG.md 文件的内容。更确切的说,它是 CHANGELOG.md 文件在某一次提交时的完整内容。而这个 objects 文件夹,就是 Git 存放数据的地方。

Git 在每次提交时,会找到存在变更的文件,利用 SHA-1 算法根据内容计算出 40 位的字符串为文件命名,放在 objects 文件夹。 哈希值的前两个字符用于命名子目录,余下的 38 个字符则用作文件名。 也就是说,你的文件每次发生变更,都会有一个对应的快照,保存了该版本文件的所有内容。如果 Git 想恢复文件到某一次历史版本,只需要拿到这个文件该次版本对应的哈希值即可。

可是 Git 怎么知道某次提交时该文件对应版本的哈希值是什么呢?也就是说 Git 如何把文件不同版本和提交记录关联起来的?

实际上在 objects 文件夹里主要存放着三类信息,除了我们上面提到的文件内容信息,还有文件路径信息以及提交信息。它们分别以 文件对象 blob object、树对象 tree object、提交对象 commit object 的形式存在。每一个对象都是 objects 目录下的一个文件,和保存文件内容信息的方式类似,Git 会通过 SHA-1 算法根据对象内容计算哈希值,得到一串 40 位的字符串为文件命名,用于找到他们。 接下来我们来看树对象和提交对象都包含了哪些内容。

当你提交时,Git 会把你当前的目录结构保存下来,对应的 tree object 结构如下:

包含了文件的文件名和文件对象的哈希值,以及子文件夹名字和对应的树对象哈希值。也就是说,你只需要找到某次提交根目录的树对象哈希值,就能找到该次提交所有文件对应的文件名以及文件对象哈希值。 也就能得到文件的历史版本内容了。

那我如何找到这个树对象?

就是通过我们最熟悉的提交对象。git log 命令可以找到对应提交的哈希值,我们可以看到提交对象的结构如下:

tree 指向的就是当前提交的树对象。parent 是上一次提交的提交对象。其他的是作者,提交人,时间和描述信息。

现在我就可以知道某次提交的某个文件的历史版本内容是什么了。由此我们也大概了解了 Git 是怎么存储文件的历史版本,又是怎么把历史版本和提交记录联系起来的。

```

当使用 git commit 进行提交操作时,Git 会先计算每一个子目录的哈希值,然后在 Git 仓库中将这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含作者,时间和描述信息外,还包含指向这个树对象(项目根目录)的哈希值以及上一次提交的哈希值。如此一来,Git 就可以在需要的时候重现此次保存的快照。

```

git 的分支

现在我们知道,只要拿到某次提交对象的哈希值,我们就能得到该次提交对应的快照。如果我们想要某个分支的文件内容,又是如何做到的?

分支只是一个可变的指针,指向分支最新的提交对象。

在 .git 里的 refs 文件夹中,保存了所有分支对应的最新提交对象的哈希值。目录结构如下:

其中 heads 文件夹保存的是本地分支,remotes 文件夹保存的是远程分支。以本地分支 master 为例,利用 cat master 可以看到,该文件中保存了最新提交对象的哈希值。如果你现在进行一次新的提交,会发现该文件中的内容会变为新提交对象的哈希值。

同时 Git 中有一个特殊的引用 HEAD,它总是指向当前检出的分支或提交对象。HEAD 指向的提交对象保存在 .git 里的 HEAD 文件中。 如下图,HEAD 指向了 master 分支。如果我们手动检出 master 分支的上一次提交,会发现该文件指向了一个确定的提交对象,即上一次提交的提交对象。

最后,可以用一张图来概括分支和提交对象之间的关系。

 
 

你知道 Git 是如何做版本控制的吗?(转)的更多相关文章

  1. 在Xcode中使用Git进行源码版本控制

    http://www.cocoachina.com/ios/20140524/8536.html 资讯 论坛 代码 工具 招聘 CVP 外快 博客new 登录| 注册   iOS开发 Swift Ap ...

  2. 规范-Git打标签与版本控制

    Git打标签与版本控制规范 前言 本文适用于使用Git做VCS(版本控制系统)的场景. 用过Git的程序猿,都喜欢其分布式架构带来的commit快感.不用像使用SVN这种集中式版本管理系统,每一次提交 ...

  3. Git 对已经加入版本控制的文件,修改后希望不被提交办法

    参考网址:http://my.oschina.net/zlLeaf/blog/197740 问题举例:假设网站有一个数据库配置文件db.php,通过git做版本控制,已经将这个文件提交到git库中.但 ...

  4. 【转】使用XCODE 的SOURCE CONTROL 做版本控制 (1)

    原文网址:http://it.zhaozhao.info/archives/60469 有一次笔者在开心项目准备尝试新的练习的时候,赫然注意到在选择档案存放位置的时候,下面有个Source Contr ...

  5. 【转】在Xcode中使用Git进行源码版本控制 -- 不错

    原文网址:http://www.cocoachina.com/ios/20140524/8536.html 本文翻译自Understanding Git Source Control in Xcode ...

  6. Git打标签与版本控制规范

    前言 本文适用于使用Git做VCS(版本控制系统)的场景. 用过Git的程序猿,都喜欢其分布式架构带来的commit快感.不用像使用SVN这种集中式版本管理系统,每一次提交代码,都要为代码冲突捏一把冷 ...

  7. git忽略已添加版本控制的文件

    今天使用git做maven项目的版本控制,刚开始搭建项目后,把所有文件全部提交了. 已经提交的文件,gitignore中后配置也无效了. 所以使用以下命令来操作,操作后要提交哦. 1.执行 git r ...

  8. 《Pro Git》轻松学习版本控制

    转自 https://kindlefere.com/post/333.html 什么是“版本控制”?我为什么要关心它呢?版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统.在 ...

  9. 用Git管理项目进行版本控制

    一.安装 1.1windows 要在Windows系统中安装Git,请访问http://msysgit.github.io/,并单击Download.安装. 1.2 在 Linux 系统中安装 Git ...

随机推荐

  1. c#委托(Delegates)--基本概念及使用

    在我这菜鸟理解上,委托就是可以用方法名调用另一方法的便捷方法,可以简化switch等语句的重复.最近做项目的时候恰好需要用到委托,便来复习及学习委托的使用.嗯...本人以前并没有用过,只是稍微知道而已 ...

  2. VM439:1 https://unidemo.dcloud.net.cn 不在以下 request 合法域名列表中,请参考

    在编写uni-app编写代码时,pc端获取数据正常,但是小程序端却出现以下的错误. 解决方法如下: 将相应的选项勾选

  3. jsp-TagLib标签库

    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ t ...

  4. java软件设计模式只单例设计模式

    概述 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计 ...

  5. Python操作Redis,你要的都在这了!

    Redis是一个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单.本节中,我们就来介绍一下Python的Redis操作,主要介绍RedisPy这个库的用法. ...

  6. 007-SaltStack之修改salt-minion id

    1. 需求背景 之前使用saltstack添加的主机默认使用了hostname作为salt-minion id,而主机名如果没有做规范和规划,是比较难区分属于什么业务或者机器的.我们需要修改salt- ...

  7. SpringBootMybatis 关于Mybatis-generator-gui的使用|数据库的编码注意点|各项复制模板

    mysql注意点: .有关编码 create table user( id int primary key auto_increment, `name` varchar(), `password` v ...

  8. [易学易懂系列|rustlang语言|零基础|快速入门|(7)|函数Functions与闭包Closure]

    [易学易懂系列|rustlang语言|零基础|快速入门|(7)函数Functions与闭包Closure] 有意思的基础知识 函数Functions与闭包Closure 我们今天再来看看函数. 在Ru ...

  9. MySQL中添加、修改、删除约束

    https://blog.csdn.net/dz77dz/article/details/82119000 主要包含的约束: 非空.唯一.check.not null.默认值.主键.外键

  10. C++ 值传递、指针传递、引用传递

    1.值传递 (1)形参是实参的拷贝(这句话说明形参和实参是两个实体),改变形参的值并不会影响外部实参的值. (2)从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传 ...