Git的工作原理
我们使用Git来记录每一次文件内容的变更,版本的更新,清晰地比较出不同版本的内容差异;可以使用Git在项目的历史版本自如地进行切换;还可以使用Git从当前项目的更改中撤销一些操作,可以新建分支,合并分支甚至关联远程服务器仓库等,这一切的背后都是怎么实现的?了解Git的思想以及基本原理这些操作也就略知一二了。
Git的分区
- workspace: 工作区 直接编辑的地方,开发者可见具体的项目,可以直接操作项目文件
- index(stage): 暂存区 数据暂存的地方
- repository: 本地仓库 存放已提交的数据
- remote: 远程仓库 在远程服务器上存放本地仓库的数据
.git文件夹
-
.git下的文件夹
- hooks 文件夹则存放项目的客户端或服务端钩子脚本
- info 文件夹下的exclude文件包含项目全局忽略匹配模式,与.gitignore文件互补
- exculd 文件
- logs 保存所有更新的引用记录
- refs
- HEAD # 最后一次的提交信息
- objects 文件夹存储着Git数据库的所有内容,存储所有Git的对象
- info 记录对象存储的附加信息
- pack 以压缩形式(.pack)存储许多对象的文件,附带索引文件(.idx)以允许它们被随机访问
- refs 文件夹存储着所有分支指向各自提交对象的指针;本地分支,远端分支,标签等
- heads 记录commit分支的树根
- master 标识了本地项目中的master分支指向的当前commit的哈希值
- remotes 记录从远程仓库copy来的commit分支的树根(只读)
- origin
- HEAD
- master 标识了远端项目中的master分支指向的当前commit的哈希值。
- origin
- tags 记录任何对象名称(不一定是提交对象或指向提交对象的标签对象)
- heads 记录commit分支的树根
.git下的文件
-
HEAD 文件指向当前分支, 包含了一个分支的引用,通过这个文件Git可以得到下一次commit的parent,可以理解为指针
-
index 文件存储着暂存区的内容信息
-
config 文件包含项目的配置信息
-
description 存储着仓库的描述信息,主要给gitweb等git托管系统使用
-
packed-refs 打包标头和标签以便高效的存储库访问
-
FETCH_HEAD 是一个版本链接,指向着目前已经从远程仓库取下来的分支的末端版本
-
ORIG_HEAD 记录的是在进行危险(drastic)操作(如合并merge,回退reset等)时,此操作之前HEAD所指向的位置,便于我们在发生毁灭性失误时进行回退
-
COMMIT_EDITMSG 保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
Git数据库
-
Git本质上是一个内容寻址文件系统 (就是根据文件内容的hash码来定位文件。 这就意味着同样内容的文件,在这个文件系统中会指向同一个位置,不会重复存储。)Git 的核心部分是一个简单的键值对数据库,可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索(retrieve)该内容
-
Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。在进行提交操作时,Git 会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。
-
git通过一种安全散列算法1(SHA-1)可以得到任意文件的SHA-1哈希值(40位的字符),也就是commit ID,然后通过文件哈希值存取数据,存取的数据都位于objects目录,SHA-1哈希值的前两个字符作为子目录名称,后 38 个字符则作为子目录内文件的名称。
Git的数据存储原理
Git对象
-
数据对象(blob object) 存储的是一个文件的具体内容
-
树对象(tree object) 存储的文件的目录,一大坨指针,指向子级tree,或者blob
-
提交对象(commit object) 存储作者信息,提交者信息,注释,指向一个 big tree 的指针
-
标签对象(tag object)它包含一个标签创建者信息、一个日期、一段注释信息,以及一个指针。
Git的底层命令、高层命令
Git常用命令共有30多个,可运行git help
查看;但Git总共有130多个命令,可以通过git help -a
查看,这些命令可以分为高层命令和底层命令,底层命令被设计成unix风格,不常用
-
-
-
往Git数据库存入数据
-
往Git数据库取出数据
-
首次提交 testA.txt文件
-
第二次提交,修改了test.txt文件内容
-
第三次提交,增加一个新文件 testB.txt ,一个新目录 lib ,lib 里增加一个文件 testC.txt
-
第四次提交,新建一个分支 branchB,并且在新分支中做了一次 commit
-
第五次提交, 合并一分支
-
-
Git的引用(reference或refs)
-
浏览完整的提交历史,但为了能遍历那段历史从而找到所有相关对象,需要记住最后提交的SHA-1值 。我们需要一个文件来保存 SHA-1 值,并给文件起一个简单的名字,然后用这个名字指针来替代原始的 SHA-1 值。文件被称为"引用(references,或缩写为 refs)",使用
git branch (branchname)
这样的命令时,Git 实际上会运行update-ref
命令,取得当前所在分支最新提交对应的 SHA-1 值,并将其加入你想要创建的任何新引用中。 -
标签引用
-
远程引用
- 如果你添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在
refs/remotes
目录下
- 如果你添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在
-
HEAD引用
- 分支和标签都是指向提交对象的指针,所有的本地分支都存储在
git/refs/heads
目录下,每一个分支对应一个文件 - Git 分支的本质:一个指向某一系列提交之首的指针或引用
- 分支和标签都是指向提交对象的指针,所有的本地分支都存储在
5. Gitflow工作流
Gitflow工作流约定使用的分支简介
-
master 分支为项目的核心分支,也是最终对外发布的分支,唯一且稳定。仅提供可读,不可在该分支上直接修改代
-
develop 分支是项目的开发主干分支,唯一。仅提供可读,不可在该分支上直接修改代码。新功能的开发需从该分支拉取新的分支展开。develop分支应该包含项目完整的全部历史记录。
-
featrue分支项是目的需求开发分支,可多个,从develop分支或其他featrue分支拉取。程序员的多人分工协作即通过featrue来实现,是代码具体实现的一线程序员接触最多的分支。需求开发完成后,要合并回develop分支。
-
release 分支为预发布分支,通常被叫做测试分支 ,主要用于开发阶段的测试及bug修复。当feature分支开发完毕后会合并回develop分支,然后再从develop分支拉取release分支提测。测试并修复后的release分支要合并回develop分支以及master分支,并打上合适的tag标记(包含必要的releaseNote)。
-
hotfix 分支为紧急线上修复分支,即当对外发布的master分支出现重大bug,影响线上使用时,从master分支拉取hotfix分支进行紧急修复。修复后的hotfix分支要合并回master分支和develop分支。
GitFlow工作流程
先,完成中央仓库的初始化,将新项目搭建起框架后的工程代码或要转gitflow的项目代码上传至git中央仓库。项目负责人克隆中央仓库到本地形成master分支,并拉取develop分支(步骤①)推送至服务器。一般的实际场景,开发团队中只有项目负责人有权限操作master分支,拉取develop分支,并将develop分支的代码合并到master分支中。
然后,开发团队中的其他人克隆中央仓库的develop分支到本地,形成全体成员统一的唯一的develop分支轨迹。之后,按照需求及成员各自的分工,各个成员可以从develop分支拉取出各自的featrue分支(步骤②)进行独立的开发;若涉及到多人合作开发同一分支,拉取的分支要及时推送至服务器,便于各成员共享。
当各成员完成各自的功能开发后,需将完成后的代码提交到featrue分支,然后合并到develop分支(步骤③)。代码合并后,featrue分支可以不再保留。
功能累积足够且稳定或到达约定的提测周期时,项目负责人应当从develop分支拉取出release分支(步骤④),打包提交相应的版本给测试人员进行部署测试,测试中提交的bug全部在该release分支完成修改。
测试结束并完成bug修复后,release分支应该合并回develop分支和master分支(步骤⑤),代码合并后,release分支可以不再保留。合并后的master分支,应由项目负责人及时推送到中央仓库(步骤⑥)。同时全体成员要及时同步自己develop分支。
有上线需求时,直接从master分支打包提交应用版本进行部署。当线上版本出现重大bug,项目负责人需从master分支拉取hotfix分支(步骤⑦),进行线上的紧急修复。
最后,修复后的hotfix分支要合并回develop分支和master分支(步骤⑧)。并推送到中央仓库(步骤⑨)。