Git 如何保存文件

其它版本管理系统通常会保存所有文件及其历次提交的差异(diff / revision),通过 merge 原始文件与各阶段的差异就能获取任何版本的状态

而 Git 保存的是每一次提交时所有文件的快照(snapshot),对于发生改变(modified)的文件会生成新的快照,而对于未发生改变的文件,其新版本快照为上一个版本的快照的索引(图中虚线框所示),这样可以减小版本库的体积

这里比较费解的是:快照究竟是什么?

简单的理解:快照就是压缩文件,只不过 git 会将文件内容压缩为 blob 格式,例如仅含一段 hello world 的 txt 文件压缩后的内容为:

7801 4bca c94f 5230 3462 c848 cdc9 c957
28cf 2fca 49e1 0200 4411 0689

所有文件快照都会被储存在 .git 仓库文件夹下的 objects 目录中

经测试,一份 200k 的未经压缩的代码文件,其文件快照大小约 65k

文件名 eef...542 是根据内容生成的 40 位哈希字符串,文件名 + 文件本身就构成了一组键值对。所有文件都以这种形式保存,而 objects 目录就是一个以键值对形式保存文件的数据库

可以想象,随着版本不断迭代,.git 仓库目录的体积往往会超过工作区所有文件的体积之和,因为哪怕只做了一丁点的改变,git 都会重新生成快照。如下图所示,我仅仅删掉了 vue.runtime.js 的一行注释,然后执行 `git add -A`,.git 中就重新生成了一份快照

一个长期维护的代码库,其代码总量可能只有几 MB,但 .git 完全可能大到以 G 计

比起其它版本管理系统仅仅记录差异,git 的这种做法不是显得更浪费空间吗?git 之所这么设计,是出于“空间换时间”的考虑。用过 SVN 的人都知道要从一个几百 MB 的项目库开出一个分支是多么费时,而使用 git 开分支,无论体积有多大,都是一瞬间的事情

Git 如何保存文件版本

理解了 git 保存文件的方式,就很容易理解其保存版本的方式:采用一个树对象来表示目录结构与文件

root: {

sub1: {

hash

hash

...

}

sub2: {

hash

hash

...

}

}

根据文件索引就可以直接从数据库中取出文件,然后再按树对象表征的目录结构进行组合排列,就很容易恢复出一套文件版本

每次 commit 除了保存树对象以外,还会记录提交的作者、批注、上一次提交的索引等信息,每个 commit 都会根据内容生成一个 hash 作为其唯一的索引

可以看到,所有的 commit 形成了一个链表,而这个链表有一个形象的名称:分支

Git 开分支的原理

git 分支的本质,就是指向某个特定 commit 的指针,假设当前只有一个分支,默认就叫做 master,当前已经是第三个提交了:

{

master: commit-3

}

那么开一个分支,无非就是新创建一个指针:

{

master: commit-3

dev: commit-3

}

当前用户处于哪个分支,需要用另一个指针来表示:

{

HEAD: master

}

执行 `git checkout dev` 切换分支后:

{

HEAD: dev

}

在 dev 分支提交一次 commit 后:

{

master: commit-3

dev: commit-4

}

切回 master,执行 `git merge dev` 合并分支:

{

master: commit-4

dev: commit-4

}

执行 `git branch -d dev` 删除分支:

{

master: commit-4

}

master 分支其实并没有什么特殊之处,不仅可以将其它分支合并入 master,也可以在其它分支上将 master 分支合并进来

Git 合并分支的策略

两个分支的合并只有两种情况:无分叉、有分叉

无分叉的情形最简单,合并分支就把 master 指向的 commit 更换为最新的 commit

{

master: commit-3

dev: commit-4

}

merge:

{

master: commit-4

dev: commit-4

}

这种策略被称为 fast forward

有分叉的情况稍微麻烦一些,git 会将两个分支的分叉点和头部的 commit 做一次三方合并,然后形成一个新的 commit:

显然第一种方式最简便,那有没有办法在分叉的情况下仍然采用 fast forward 的策略呢,有

在 experiment 分支上执行 `git rebase master`,首先会计算出分叉点与 experiment 分支头部的两个 commit 的差异,然后以 C3 为新的基础,整合之前计算出的差异,得到一个新的 commit

var patch = C4 - C2

var C4` = C3 + patch

C4`.parent = C3

rebase 就是改变基础的意思。这下回到 master 分支执行 merge 操作,就可以实现 fast forward 了

理解 Git的更多相关文章

  1. 全面理解Git

    前言 人生贵知心,定交无暮早. 原文博客地址:Git命令总结 知乎专栏&&简书专题:前端进击者(知乎)  前端进击者(简书) 正文 1.Git简介 Git的诞生确实是一个有趣的故事,我 ...

  2. 深入理解git,从研究git目录开始

    转发学习的啦. 似乎很少有人在读某个git快速教程的时候会说:“这个关于git的快速教程太酷了!读完了用起git来超级舒服,并且我一点也不怕自己会破坏什么东西.” 对git的初学者来说,刚接触git时 ...

  3. 真正理解 git fetch, git pull 以及 FETCH_HEAD【转】

    转自:http://www.cnblogs.com/ToDoToTry/p/4095626.html 真正理解 git fetch, git pull 要讲清楚git fetch,git pull,必 ...

  4. 理解Git的工作流程(转)

    英文原文:Understanding the Git Workflow 如果你不理解Git的设计动机,那你就会处处碰壁.知道足够多的命令和参数后,你就会强行让Git按你想的来工作,而不是按Git自己的 ...

  5. 理解git经常使用命令原理

    git不同于类似SVN这样的版本号管理系统,尽管熟悉经常使用的操作就能够满足大部分需求,但为了在遇到麻烦时不至于靠蛮力去尝试,了解git的原理还是非常有必要. 文件 通过git管理的文件版本号信息所有 ...

  6. [转载]理解 Git 分支管理最佳实践

    原文 理解 Git 分支管理最佳实践 Git 分支有哪些 在进行分支管理讲解之前,我们先来对分支进行一个简单的分类,并明确每一类分支的用途. 分支分类 根据生命周期区分 主分支:master,deve ...

  7. 理解git的分支原理,更好地使用git

    文章内容转载于git-scm. 部分内容涉嫌枯燥 一.git分支概念 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作.在很多版本控 ...

  8. 深入理解Git - 一切皆commit

    在对 git 有了基本理解和知道常规操作之后,如何对 git 的使用有进一步的理解? 一切皆 commit 或许是个不错的理解思路. 本文将从『一切皆 commit 』的角度,通过 git 中常见的名 ...

  9. 真正理解 git fetch, git pull 以及 FETCH_HEAD

    真正理解 git fetch, git pull 要讲清楚git fetch,git pull,必须要附加讲清楚git remote,git merge .远程repo, branch . commi ...

  10. 深入理解Git - Git底层对象

    前篇: 深入理解Git - 一切皆commit 如何从稍微底层一点的角度,从底层实现理解一切皆commit ? 配合希沃白板课件食用,效果更佳: [希沃白板5]课件分享 : <Git 进阶 - ...

随机推荐

  1. Linux指令--mkdir

    本篇博客参照http://www.cnblogs.com/peida/archive/2012/10/25/2738271.html. linux mkdir 命令用来创建指定的名称的目录,要求创建目 ...

  2. C语言学习之交换(冒泡)排序

    在学习c语言的过程中,在数组内容中我们总是能学习到对一组数据进行排序,对于排序有许多的方法,像 (交换)冒泡排序.选择排序.(基数)桶排序.(插入)二分法排序等等. 我主要以我个人的理解去分析常见的交 ...

  3. html页面不显示中文

    问题:HTML编辑中文后无法在网页上显示中文 原因:是适用的 Cufon字体包不支持中文造成的. 简单粗暴的解决方法:如果你本来打算是去掉Cufon,可以看一看<如何禁用Cufon功能>. ...

  4. 二级缓存:EHCache的使用

    EHCache的使用 在开发高并发量,高性能的网站应用系统时,缓存Cache起到了非常重要的作用.本文主要介绍EHCache的使用,以及使用EHCache的实践经验. 笔者使用过多种基于Java的开源 ...

  5. R语言的神奇之基于向量

    对于大多数需要来说,当我们需要计算两个向量相加时,我们需要分别对这两个向量的元素进行遍历,所以写起来非常的麻烦.下面看看R语言是如何实现的. 首先,将1:5赋予一个名为x的向量 > X<- ...

  6. 图说:为什么Java中的字符串被定义为不可变的

    8张图,看懂Java字符串的不变性 字符串,想必大家最熟悉不过了,通常我们在代码中有几种方式可以创建字符串,比如:String s = "Hollis";这时,其实会在堆内存中创建 ...

  7. Hands-On Machine Learning with Scikit-Learn and TensorFlow---读书笔记

    去年在北京参加了一次由O'Reilly和Cloudera联合举办的大数据会议Strata Data Conference,并有幸获得了O'Reilly出版的Hands-On Machine Learn ...

  8. php 把驼峰样式的字符串转换成下划线样式的字符串

    1.如何在php中把驼峰样式的字符串转换成下划线样式的字符串.例:输入是FooBar的话,输出则是foo_bar 以下是用正则的方式去完成,既然用到正则,方法肯定就不只一种,我们看下下面的方式 ech ...

  9. srs2录制flv文件metadata不准确

    测试环境:server:srs2client:librestreaming / yasea srs 配置 dvr录制24分钟flv文件. e:\flv $ ll total drwxr-xr-x Ad ...

  10. System.nanoTime

    System.currentTimeMillis()返回的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数. System.nanoTime()返回的是纳秒,nanoTime而返回的可能是任意 ...