0%

Git原理和基础

Git原理和基础

在过去几个学期的期末大作业合作中,我们常常花费了大量的时间和精力去将各人的工作整合在一起成为一个完整的项目。通常我们分配完成任务,完成各自的编码任务,最后再汇总在一个人那里,完成最后的调试。

在这样的前提下,我开始系统地学习和了解版本管理工具,其中Git是使用最广泛的。

Git工作原理

Git 将顶级目录中的文件和文件夹作为集合,并通过一系列快照来管理其历史记录。在Git的术语里,文件被称作Blob对象(数据对象),也就是一组数据。目录则被称之为“树”,它将名字与 Blob 对象或树对象进行映射(使得目录中可以包含其他目录)。快照则是被追踪的最顶层的树。一个最简单的文件结构树例子:

image-20240108104233560

这个顶层的树包含了两个元素,一个名为 “foo” 的树(它本身包含了一个blob对象 “bar.txt”),以及一个 blob 对象 “baz.txt”


Git的历史记录是一个由快照的有向无环图,图中的每一个节点都是一个快照,每个快照都包括一个文件结构状态。所以每一个快照都有一系列父辈,也就是其之前的一系列快照。

在 Git 中,这些快照被称为“提交”。通过可视化的方式来表示这些历史提交记录时,看起来差不多是这样的:

image-20240108110306552

图中的每个o表示一次commit,即快照。箭头指向了当前提交的父辈在第三次提交之后,历史记录分岔成了两条独立的分支。这可能因为此时需要同时开发两个不同的特性,它们之间是相互独立的。开发完成后,这些分支可能会被合并并创建一个新的提交,这个新的提交会同时包含这些特性。新的提交会创建一个新的历史记录,看上去像这样(最新的合并提交用粗体标记):

image-20240108110746951

Git 中的提交是不可改变的。但这并不代表错误不能被修改,只不过这种“修改”实际上是创建了一个全新的提交记录。


数据模型和伪代码表示

使用伪代码表示Git的数据模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 文件就是一组数据
type blob = array<byte>

// 一个包含文件和目录的目录
type tree = map<string, tree | blob>

// 每个提交都包含一个父辈,元数据和顶层树
type commit = struct {
parent: array<commit>
author: string
message: string
snapshot: tree
}

对象和内存寻址

Git 中的对象可以是 blob、树或提交:

type object = blob | tree | commit

Git 在储存数据时,所有的对象都会基于它们的 SHA-1 哈希 进行寻址,Blobs、树和提交都一样,它们都是对象。当它们引用其他对象时,它们并没有真正的在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为引用。

引用

但是对于用户而言,使用哈希作为快照的名称是不可行的,针对这一问题,Git 的解决方法是给这些哈希值赋予人类可读的名字,也就是引用(references)。引用是指向提交的指针。与对象不同的是,它是可变的(引用可以被更新,指向新的提交)。例如,master 引用通常会指向主分支的最新一次提交。使用HEAD来表示当前所在的位置。

基础操作

Git的命令行接口有许多功能,这里简单记录一套最基本的使用方法:

创建git仓库,创建一个新的 git 仓库,其数据会存放在一个名为 .git 的目录下

1
git init

显示当前仓库状态,下面的状态是没有提交到暂存区的结果

1
git status

image-20240108113553832

添加文件到暂存区,将修改后的文件添加到暂存区,后查看status,显示出等待commit的记录。

1
git add

image-20240108113758913

提交记录,输入后会提示用户添加提交信息记录,成功后显示出简化版本的hash值

1
git commit

image-20240108114129515

查看日志,显示出当前的位置和已经存在的分支,加上参数后会以图的形式表示,更加直观

1
git log --all --graph --decorete

image-20240108114915744


有关Git的多人协作功能还需要进一步学习,在此贴就不进行形象记录,主要体现Git的底层逻辑。

后续学习资料