Git 工作原理浅析
1 How Git works?
Staging Area
在Git中,staging area(暂存区)是一个工作区间,用于准备下一次提交。当你在工作目录中修改了文件后,这些修改并不会立即记录到Git仓库中。相反,你需要先将这些修改添加到暂存区。
这个机制的好处是,它允许开发者在提交前精确地控制哪些修改(新增、修改、删除的文件)将被包含在提交中。这使得提交记录更清晰、更有目的。
暂存区的使用流程通常如下:
- 修改文件:在工作目录中对文件进行修改。
- 暂存修改:使用命令
git add <文件名>
将修改后的文件添加到暂存区。可以多次添加不同的文件,以便一次性提交多个文件的修改。 - 提交更改:使用命令
git commit
将暂存区中的更改提交到Git仓库。这会生成一个新的提交(commit)记录,表示项目的一个新版本。
暂存区提供了一个灵活的中间层,可以帮助开发者组织和审核即将进行的提交,确保每次提交都是有意义的。
2 TestGit[1] 实际仓库分析
2.1 提交记录
- 提交记录图示
2.2 TestGit项目本地目录
- 项目本地目录图示
2.3 Git 中的 origin
origin
在 Git 中通常是远程仓库的默认名称。当你克隆一个仓库时,Git 自动将这个远程仓库命名为 origin
。这是一个约定俗成的名字,表示原始或源仓库。虽然 origin
不是强制的命名,但它是最常用的。在添加远程仓库时可以使用其他名字,但 origin
是默认和最常见的选择。
例如,命令 git push origin main
表示将本地的 main
分支的改动推送到名为 origin
的远程仓库的 main
分支上。在本项目中,origin
代表 GitHub 上的 TestGit 项目。
2.4 .git/objects 目录详细介绍
2.4.1 松散对象 (loose objects)
在 .git/objects
目录中,除了 pack 文件(pack
和 idx
文件)之外,其他文件通常被称为松散对象。这些是 Git 仓库中存储的最基本形式的对象,每个对象对应一个文件。
特点:
- 单一对象存储:每个松散对象文件包含一个 Git 对象,如提交(commit)、树(tree)或数据块(blob)。
- 命名方式:松散对象的文件名是基于对象内容的 SHA-1 哈希。
例如,如果一个对象的哈希是 de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3,它会被存储在 .git/objects/de/9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3。
- 压缩存储:这些对象文件是压缩存储的,使用 zlib 压缩算法。
创建和转换:
松散对象通常在对象初次创建时生成,随着时间的推移和操作的积累,Git 可能会自动将它们打包到 pack 文件中以优化存储和提升性能。
优点和缺点:
- 优点:简单易于管理,每个对象独立存储,便于直接访问和修改。
- 缺点:随着对象数量增加,松散对象的管理和存储效率降低。
对象类型:
Commit 对象:保存特定提交的信息,包括作者、提交者、提交信息和指向树对象的指针。
Tree 对象:代表一次提交时的快照,包含指向其他树对象或 blob 对象的指针。
Blob 对象:存储文件的实际内容。
在计算机科学中,"blob" 是 "Binary Large Object" 的缩写,指的是存储在数据库或文件系统中的一段大的二进制数据。BLOB 类型的文件通常用于存储多媒体数据,如图像、视频、音频文件,以及其他需要存储为二进制形式的复杂数据。
BLOB 并不是特定的文件格式,而是一种数据类型或数据存储方式。
相关命令:
1 |
|
2.4.2 Pack 文件
Pack 文件是一种存储机制,用于优化大仓库的存储空间。它包含许多对象的集合,通常在执行 Git 的垃圾回收命令 git gc
时生成。
- 每个 pack 文件都有一个
.pack
文件和一个索引文件.idx
。
相关命令:
1 |
|
2.4.3 完整目录及解析
1 |
|
松散对象解析
哈希值 | 对象类型 | 对象内容 |
---|---|---|
23e1 | commit | tree 68d2491744e247bd77ac0bc349dce38529b30593 parent 2f40ce79af9c0f4d5156bf10c683f8c20e2b41a9 author hwollin 2457935544@qq.com 1715333026 +0800 committer hwollin 2457935544@qq.com 1715333026 +0800 add c |
2f40 | commit | tree fb8842d6a9421ffef675728bd1ef4845e7a2b977 parent 1f8d4ae8f80dee3cb51555b8f4c0cdd61ec62765 author hwollin 2457935544@qq.com 1715332991 +0800 committer hwollin 2457935544@qq.com 1715332991 +0800 modify a |
68d2 | tree | 100644 blob c6127b38c1aa25968a88db3940604d41529e4cf5 .gitignore 100644 blob 8741d0c6a3b5c9d8bb35352e176c29f76264c533 README.md 100644 blob f85b16aa4ca04a8aa9a9a7869ff06e460f172ee9 a 100644 blob 91d32d52966303a0b7a5864b37d27f1f201cea68 c ——————————————————————————————————– 解释: 100644:这是文件模式,表示文件的权限。对于 blob(文件)来说, 100644 表示这是一个普通文件,可读可写。 |
91d3 | blob | I am ccc |
f85b | blob | I am aaa, modified from branch hwoll |
fb88 | tree | 100644 blob c6127b38c1aa25968a88db3940604d41529e4cf5 .gitignore 100644 blob 8741d0c6a3b5c9d8bb35352e176c29f76264c533 README.md 100644 blob f85b16aa4ca04a8aa9a9a7869ff06e460f172ee9 a |
pack 文件解析
1 |
|
non delta: 8 objects
这行表明在 pack 文件中,有 8 个对象是以非增量(non-delta)形式存储的。在 Git 中,对象可以存储为完整对象或增量(delta)对象。此处的“non delta”指的是那些存储为完整数据的对象。
chain length = 1: 2 objects
这行指的是存在两个对象,它们形成了长度为 1 的增量链。在 Git 的 pack 文件中,增量对象通过指向另一个对象来存储数据的差异。这里的“链长度”是指在恢复(reconstruct)对象时需要遍历的增量对象的数量。长度为 1 的链意味着每个增量对象直接引用了一个完整对象。
完整目录分析
2.5 git merge
在这个项目中,main
是主分支,hwoll
是工作分支。以下是将 hwoll
分支合并到 main
分支的步骤:
步骤 1: 切换到主分支 main
1 |
|
步骤 2: 更新主分支
1 |
|
步骤 3: 合并 hwoll
分支到 main
1 |
|
步骤 4: 解决可能的合并冲突
如果出现冲突,解决冲突后添加解决后的文件到暂存区:
1 |
|
然后完成合并:
1 |
|
步骤 5: 推送更改到远程仓库
最后,将更新后的 main
分支推送到远程仓库:
1 |
|
这些步骤将确保 hwoll
分支成功合并到 main
分支,并且所有更改都被正确同步到远程仓库。
- 合并后的提交和快照图示
1 |
|
2.6 回退到上次提交
假设最近一次提交是 7eb3
,再往前一次是 b4d1
,现在远程仓库和本地仓库 main
分支均处于 7eb3
,我如何将远程和本地同时回退到 b4d1
?
以下是具体步骤:
步骤 1: 切换到 main
分支
首先,确保你在 main
分支上。使用以下命令切换到 main
分支:
1 |
|
步骤 2: 回退本地仓库到 b4d1
使用 git reset
命令将本地仓库回退到指定的提交 b4d1
:
1 |
|
这个命令会将本地的 main
分支回退到提交 b4d1
,并且丢弃所有在此之后的提交和更改。
步骤 3: 强制推送回退后的提交到远程仓库
使用 git push
命令将本地的变更强制推送到远程仓库,使远程仓库的 main
分支同步回退到 b4d1
:
1 |
|
步骤 4: 检查回退结果
确保回退操作成功,可以使用以下命令查看当前分支的提交历史,确认 main
分支已经回退到 b4d1
:
1 |
|
执行上述步骤后,远程和本地仓库的 main
分支将会回退到提交 b4d1
,并且所有在 b4d1
之后的提交将被移除。
注意:使用 --force
选项会覆盖远程仓库的历史,因此在执行这一步之前,确保你已经告知团队成员并获得他们的同意,以避免不必要的数据丢失或混乱。