文章目录
1. 写在最前面
认真思考过能量守恒定律以后,笔者突然觉得这个定理很值得深入思考。因为这个定理不应该仅仅局限于物理学中,在计算机系统设计、社会学中均可使用。
什么?
你不信,那阔以先看看我的解释,反正学到就是赚到,又不吃亏是吧。
注:「能量守恒原理」能量既不会凭空产生,也不会凭空消失,它只会从一种形式转化为另一种形式,或者从一个物体转移到其它物体,而能量的总量保持不变。
2. 关于版本管理的思考
几乎所有给 Git 的定义都有写,Git 是分布式版本控制软件,那就先从思考「版本控制」是什么开始吧?
2.1 最原始的版本管理
犹记起大学写论文,然后反复修改的场景。真真的跟下图「一模一样」。
这种版本管理的方式,应对于论文修改的场景已经最够,甚至于每年毕业季你在大部分的毕业生的电脑里都能够找到如上图所示的「版本控制」方式,所以根本无需优化。但「无需优化」的最根本的原因是「规模」和「频率」:
- 规模:大学论文的字数最多几十万字,多次做拷贝备份对于现在动辄几百G 的电脑磁盘来说根本无所谓
- 频率:为几年才做几次的事情,来学习并使用完整的版本控制,这件事本身 ROI 就很低,比如就笔者而言,大学四年做如上图所示的多次修改和备份的次数真「屈指可数」,而且基本文章每次写完就丢掉了……
所以,结论是针对于特定场景下确实存在「版本控制」的需求,但不是强需求
2.2 版本管理的发展历史
提升「规模」、「频率」并且转换「使用场景」后,你会深深的意识到版本管理是有多么重要!再比如,还是笔者,现在已经成为一名还算合格的小小后端软件开发工程师,日常最多的工作就是「制造 bug」以及「修复 bug」,这个时候你说不会对代码库做版本管理,会看到以下结果(ps 嗯,传说中的程序媛
- 你老板大概率要砍你
- 你公司大概率要 fire 你
- 你大概率得天天加班,然后工作做不完(ps 想象一下,你每次改完都要用最原始的方式备份代码,不寒而栗
下图是一张版本控制的发展图:
这三种版本管理方式,笔者个人认为是逐级递进的关系,每一种出现都是为了弥补上一个设计使用上不足:
- 本地版本管理:针对与原始版本管理而言,版本与内容的关系映射更加清晰、简明,但是却无法多人协作
- 集中式版本管理:能够多人协作,但是因为是数据存储、操作全部依靠中心节点,风险不可控,中心节点挂掉以后,服务不可用,而用户对此束手无策
- 分布式版本管理:解决了中心结点唯一,风险不可控问题,每个本地节点会暂存一份对远端中心节点数据的备份,所有操作均可在本地进行。以本地节点和中心节点数据对等的方式解决单点风险问题。但是设计和实现并非常人能够做到,毕竟林纳斯·托瓦兹这个世界上只有一个。
注:林纳斯·托瓦兹决定自行开发版本控制系统替代BitKeeper,以十天的时间编写出git第一个版本。
3. Git 是如何做分布式版本控制的
3.1 分布式版本控制需要做什么
版本管理的核心要点是,「能够针对版本名称,找到版本数据」,分析一下原始和 Git 分布式版本的设计要点
- 原始版本管理:每个版本的数据均有做全量存储,能够根据版本名称找到对应版本数据
- Git 分布式版本管理:仅对改动文件做全量存储,能够回溯完整的版本间操作记录,支持不同版本数据合并,同时也支持了根据版本名称找对应版本数据的方法
二者的基础能力对比总结如下:
原始版本管理 | 分布式版本管理 | 分布式版本管理的优点 | |
---|---|---|---|
不同版本数据存储方式 | 存储每个版本的全部数据。 | 仅存储每个版本的更改文件的全部数据 | 存储粒度更细,重复存储的文件更少,所占用的存储空间更少 |
多文件版本管理 | 文件夹的方式,每次备份整个文件 | 库的方式,一个库下可存储多个文件 | 平手,没啥显著优点 |
版本间操作记录关联 | 不支持 | 支持,每次操作对应一个 commit | 能够快速查询某个版本的操作内容,快速回滚版本 |
原始版本管理遇到的问题,分布式版本管理也同样会遇到,只是分布式版本管理在设计、实现上更好,支持了更多的操作。
注:来让我们应用下开头的能量守恒原理:
你需要做的人,别人也同样需要做,事件不会减少。
付出更多的能量,便可到更多的结果。种多少因,结多少果哦
注:别人在做了同等事情的基础上,还作了优化,所以它解决的问题也就更多。比如 Git 对比 SVN
3.2 Git 基础功能实现
基础功能包含三个要解决的问题:
- 每次变更操作的内容如何记录
- 库下的文件是如何组织
- 文件是如何记录的
「学会使用一样家用电器最好的方式,不是去仔细看说明书,而是先去用,哪里有疑问以后再返回来看,这可以加深印象」
好了,新建一个 git-demo 的仓库,提交一个文件,然后就阔以开始动手分析了:
是时候回答下上面的问题了:
-
每次变更操作的内容如何记录
用 commit 做记录,其中每一个 commit 对象都精确地记录了它被创建时的仓库目录结构和文件内容,Git 的 history 就是这些 hash 对象构成的有向图。
-
库下的文件是如何组织
用 tree 存储,它会记录下当前目录下文件的组织情况。
-
文件是如何记录的
用 blob 存储,会记录文件的全部内容。
而 commit、blob、tree 的文件都会以 hash 的方式命名存储于当前仓库的 .git/objects 目录下。
3.3 Git 基础功能的延伸问题
「举一反三」这个词的重点不是反三,是要先明白一是什么后,再做深入思考🤔
思考如下两个延伸问题:
- 对原有文件的变更, Git 是如何存储的
- 库文件下嵌套子目录如何表示
老规矩,动手试试呗。
-
对原文的变更,Git 是如何存储的
是全量存储变更后的问题,分析如下图所示
注:一个单文件变更的提交会产生三个 hash object,一个操作 commit hash,一个文件内容 hash,还有一个是 tree 的 hash (ps 文件变更导致目录变更,导致 tree 变更
-
库文件下嵌套子目录如何表示
库下如果插入子目录,会在存储时嵌套存储一个 tree 类型的 hash 值。如下图所示
整个分析过程如下:
- 首先根据 commit history 找到最近一次的操作 6e20804dd69e8ac42955d27e7e054aa85061b3d1,
- 然后查看该次提交的内容,找到代码库最新的目录 tree 697855ecb8b2258d8bf9d317cd3f9a4765aab91c
- 最后查询该目录的内容
git cat-file -p 697855ecb8b2258d8bf9d317cd3f9a4765aab91c
注:相同文件名和内容的文件,git 不会做二次记录,参照上图的 a.txt
3.4 Git 高阶功能简介
实现上述的基础功能后,Git 可以在上面的基础功的基础上实现更负责的能力,比如:
-
本地或者远端合并不同版本的数据(git merge、git rebase)
-
根据指定版本切换新版(git checkout)
-
拉取远程仓库的全部版本数据(git clone)
-
推送指定版本数据到远端(git push)
-
回滚指定版本到某次操作(git reset)
-
撤销某个版本指定提交的操作(git revert)
-
摘取某几次提交的动作到指定分支(git cherry pick)
-
……
等等很多功能,但是篇幅有限,今天就先写到这里吧,后面的阔以先欠着。
4. 碎碎念
大概是很容易被治愈,又是因为喜欢的句子开心的一天,继续完结撒花。
- 我们DNA里的氮元素,我们牙齿里的钙元素,我们血液里的铁元素,还有我们吃掉东西里的碳元素,都是曾经大爆炸时千万星辰散落后组成的,所以我们每个人都是星辰。
- 并不可否认,生活磨掉了我们一部分的勇气和温柔,但我也相信,因为我们还很年轻,所以失去的还会长出来,而新的部分将闪闪发亮
好像有那么一丢丢跑题,但是管他呢,反正是学到了呀。