Git 的创建
可能大家都知道 Linus Torvalds 创建了 Linux 内核, 但是很巧的是,被大量的开发者使用的 Git, 也是由他创建的。
从 Linux 诞生以来,就有许多的社区开发者需要参与开发,而 Linus Torvalds 一开始主要依赖手工处理补丁和电子邮件来管理 Linux 内核代码。
直到2002年,整个项目组开始使用 BitKeeper 这个分布式版本控制系统。
但是在2005年的时候, 因为一位社区开发者反编译 BitKeeper,导致 Linux 内核社区关于 BitKeeper 的使用许可被 BitKeeper 回收,失去了免费使用权。这促使 Linus Torvalds 着手开发一个新的版本管理系统,也就是 Git,既然要开发一个新系统,就得从痛点出发,所以从一开始,Git 的设计目标就包括:
高效处理大型项目
支持非线形开发
完全分布式
保护数据的完整性
而 Git 最终的实现也确实达到了这几个目标。
Git 的设计
Git 采用了分布式架构有三个本地工作区域,和一个远程工作区域:
Working Directory:本地工作目录,其中包含所有的项目文件。这也是我们进行日常工作,编辑,添加和删除文件的地方。
Index / Stage:本地的暂存区,用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息。
Local Repository:本地仓库,它包含了项目的所有版本历史。这些历史记录包括所有的提交(commits),分支(branches),标签(tags)等。当你将更改从暂存区提交时,这些更改会被记录到本地仓库中。
Remote Repository:远程仓库,托管代码的服务器,例如 GitHub,GitLab,Bitbucket等。
Git 的分布式架构意味着每个开发者在本地都有项目的完整副本。这样,开发者可以在本地进行提交、分支和合并操作,而不需要与中央服务器通信, 减少了网络延迟和中央服务器的负载。
另外,在 Git 中,有四种主要的对象,分别是:
Blob(用于存储文件内容): 每个文件都会被换成一个斑点对象,该对象包含文件的原始数据内容,但不包含文件名或其他元数据。
Tree(用于存储目录结构): Tree 对象代表一个目录。它包含了指向 blob 对象的指针(代表目录中的文件)以及指向其他 tree 对象的指针(代表子目录)。每个指针都带有与之关联的文件或目录的名称和权限信息。
Commits:Git 中的核心是 commit,Commit 对象表示项目历史中的一个快照。每次提交更改,Git 都会创建一个新的提交对象。这个对象包含以下信息:
一个指向 tree 对象的指针(代表该提交时项目的根目录)
一个或多个父提交(parent commits)的引用,这表示当前提交是在哪些提交的基础上进行的更改
一个提交信息(commit message),描述了进行更改的原因
提交者(committer)的名字和邮箱地址,以及提交的时间戳
引用(References) :Git 通过引用来跟踪commit。引用是指向 commit 对象的指针。最常见的有以下几种:
Branch 分支是对提交历史的引用,它指向链中的最新提交。当你在分支上创建新提交时,分支引用会自动移动到最新的提交上。
Tag 是指向某一特定提交(commit)的静态引用。标签通常用于标记项目中的重要点,比如版本的发布点。
HEAD 是一个特殊的引用,它指向当前检出的分支的最新提交。当你切换分支时,HEAD 会随之移动。
所以,其实我们创建分支,删除分支,只是创建或删除了一个指针。
那存储的 blob,tree,commit 是怎么被删除的呢?
Git 有一个垃圾回收机制(Git GC),它可以清理不再需要的对象,也就是未被任何分支、标签、stash 或其他引用指向的孤立对象。通常垃圾回收是自动进行的,Git 会在执行某些命令(如 git commit, git merge)时自动触发 GC。此外,用户也可以通过 git gc 命令手动触发垃圾回收。
Git 的解析
接下来我们简单看看几个具体命令背后的逻辑。
1
git add
计算文件内容的 SHA-1 哈希值。
Git 对象数据库中创建一个新的 blob 对象(当这个内容的 blob 对象不存在时)。
将这个 blob 对象的哈希值记录在暂存区中,与相应的文件路径关联。
2
git commit
记录用户信息:Git 使用配置的用户信息来记录谁创建了提交
创建 tree 对象
创建 commit 对象
更新 HEAD 和分支引用:Git 将当前分支的引用更新为指向这个新的提交对象,同时 HEAD 引用也会更新。
完成提交:将更改记录到Git的历史中。
3
git merge
确定合并基础,即两个不同分支的共同起点.
比较差异,Git 会比较两个分支与合并基础的差异,来确定每个分支上所做的更改。
合并变更,若两个分支对同一个文件的不同部分进行了更改,Git 可以自动合并这些修改,并将结果应用到当前分支的工作目录中。若两个分支对同一部分进行了修改,则需要人工介入解决冲突。
创建新的提交,Git 会自动创建一个新的提交来完成合并,这个提交称为合并提交,它会有两个父提交。
更新 HEAD,Git 将当前分支的 HEAD 更新为新的合并提交。
在我们的日常工作中,通常使用github 的pull request来 merge 代码,其默认使用的就是git merge 显式合并这种方式。
除此之外我们也能看到“Rebase and Merge” 和“Squash and Merge” 这两个选项。
Rebase and Merge: 将特性分支上的每个提交 rebase 到主分支上,这样不会产生合并提交。
Squash and Merge:将特性分支上的所有提交压缩成一个提交然后 rebase 到主分支上。
Rebase 的基本工作原理是将一系列提交从一个分支移动到另一个分支的顶部,这样做通常会改变这些提交的哈希值,因为它们现在有了一个新的父提交,但是它也可以使得目标分支拥有一个干净、线性的提交历史。
本文简要介绍了 Git 的相关基础知识,希望大家看完后能对 Git 有更多的了解,而不再是最熟悉的陌生人,也希望本文能鼓励到大家继续探索 Git 的高级功能,以便更加自信和高效地使用这款强大的工具!
参考链接:
https://www.worldhello.net/gotgit/index.html
https://www.linuxfoundation.org/blog/blog/10-years-of-git-an-interview-with-git-creator-linus-torvalds