%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# Git 与 Github
- Git:一款开源的**分布式版本控制系统**
- Github:利用 Git 进行**版本控制**的**代码托管平台**(用作**远程代码仓库**)
# 什么是 Git?
Git 是一个**分布式==版本控制系统==**,广泛用于**项目源代码管理**。
#### Git 进行版本控制与追踪管理的原理
Git 本质上是一个 "快照文件系统",直接**记录 "快照"**,而非记录 "**差异**"(delta-based)。
其实现版本控制的思想为:
在**每一个版本**都为文件系统中**所有已追踪文件**创建一个**版本对应**的 **==快照==**(snapshot)
- 若文件发生变更(与上一版本相比),则创建一个**当前版本的==文件快照==**,**永久性存储到 `.git`目录**;
- 若文件未发生变更,则**创建一个==链接==,指向上一个版本的文件快照**。
同时,保存**对快照的索引**,从而实现**追踪管理**。
Git 的**快照流**如下图所示:
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-C3F08E5D70F67DA8743883DE669202CB.png|588]]
# Git 的基本概念
Git 进行版本管理时,涉及三个概念:**工作区**、**暂存区**、**仓库**
- **工作区**(Working Directory)
- **暂存区**(Staging Area)
- **仓库**(Repository)
> [!NOTE]
>
> - **`.git` 文件夹** 即为 **==Git 仓库==**。
> - **`.git` 所在目录** 即为该 **Git 仓库的==工作区==**。
>
<br>
## Git 工作流的主要步骤
1. 在「**==工作区==**」进行文件修改。
2. 使用 `git add` 将修改后的文件添加到「==**暂存区**==」。(创建**文件快照**及**索引**)
3. 使用 `git commit` 将暂存区的内容提交到「**==仓库(本地)==**」。(工作区和暂存区的 "**状态**" 被清空(相对于最新的提交),而**快照被永久性存储在仓库中**)
4. 使用 `git push` 将本地仓库的内容推送到 「==**远程仓库**==」
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-A26D09090B21B82F32212FA6D1222C4D.png|454]]
![[_attachment/05-工具/git 使用/git 使用总结.assets/IMG-git 使用总结-0C9290F13DC08459332CA34644243F8F.png|523]]
### 工作区 Working Directory
本机文件系统上 **==`.git` 文件夹所在的目录==** 即为 "**Git ==工作区==**"。
工作区中的文件包括 "**已纳入 Git 版本控制的文件**" 和 "**未追踪的文件**" 以及 "**忽略的文件**"。
### 仓库 Repository
**`.git` 目录**即是一个 **==Git 仓库==** 的实体,该目录中包含了**版本控制相关的所有==元数据==及==对象数据库==**,包括配置文件、**存储对象**(Blobs、Trees、Commits)、**引用**(HEAD、Branches、Tags)、**索引**以及日志文件。
仓库分为两种类型:
- **本地仓库**:位于开发者的计算机上,**包含所有的历史记录和版本跟踪信息**;
- **远程仓库**:通常托管在服务器上(如 GitHub, GitLab, Bitbucket 等),便于团队成员可以共享代码并协作。
- 开发者通过推送(push)、拉取(pull)或克隆(clone)操作**与远程仓库交互**。
%% ![[Excalidraw/Excalidraw-Solution/git 说明_0.excalidraw.svg]] %%
%% [[Excalidraw/Excalidraw-Solution/git 说明_0.excalidraw.md|🖋 Edit in Excalidraw]] %%
### 暂存区 Index
暂存区(也称为索引 `Index`),用于存储**即将被提交的文件快照**,充当工作区与仓库之间的缓冲区。
当执行 `git add` 命令时,将为文件**创建快照** 并**保存对该文件快照的索引**。
- 「**文件快照**」存储在 `.git/objects` 目录下,作为 blob 数据对象存储;
- 「对文件快照的 **索引**」存储为 **`.git/index` 文件**,索引了 "**将被提交到仓库的文件快照**"。
"**暂存区**" 数据对应的实体文件为 `.git/index`(二进制文件),包含下列信息:
- **文件元数据**
1. **路径名**:索引中每个文件的相对路径。
2. **哈希值**:每个文件的内容通过 SHA-1 哈希算法生成的哈希值,指向实际数据存储在对象数据库中的 blob 对象。
3. **大小**:文件的大小。
4. **时间戳**:文件最后修改的时间戳。
5. **文件模式**:文件的权限和类型(例如,普通文件、可执行文件等)。
6. **阶段**:在合并操作中,不同阶段的文件有不同的标记。这有助于处理合并冲突。
- **文件状态信息**:哪些文件是新添加的、修改过的、已删除的等
- 其他信息:例如缓存树
- **缓存树**:为了优化命令的性能,`.git/index` 可以包括一个缓存树(cache-tree),它存储了文件目录结构的层次信息,加快了某些操作,如 `git status` 的执行。
# `.git` 仓库目录说明
`.git` 目录中包含以下文件&文件夹:
![[Excalidraw/Excalidraw-Solution/git 说明.excalidraw.svg|851]]
%%[[Excalidraw/Excalidraw-Solution/git 说明.excalidraw.md|🖋 Edit in Excalidraw]]%%
| | 说明 |
| ---------------- | ------------------------------------------------------------------------------------------- |
| **`refs` 目录** | 存储着**分支引用、远程跟踪引用、标签引用** |
| **`objects` 目录** | 存储着 [[#Git 数据对象]],包括三种类型:Blob、Tree、Commit。该目录即为 "**==对象数据库==**" 实体。 |
| `info` 目录 | 包含一个全局性排除(global exclude)文件, <br>用以放置那些**不希望被记录在 `.gitignore` 文件中的忽略模式**(ignored patterns) |
| `hooks` 目录 | 包含客户端或服务端的钩子脚本(hook scripts) |
| | |
| `config` 文件 | 项目特有的 Git 配置选项 |
| **`HEAD` 文件** | 存储着 **==HEAD 引用==**——指向某一个"**分支引用**"的引用,表示该分支为 "**当前检出的分支**" |
| **`index` 文件** | 保存着 **==暂存区==信息**——一个简单的文件列表,记录了 "**将被提交到仓库的文件快照**" |
| `description` 文件 | 仅供 GitWeb 程序使用; |
<br>
## 工作区中文件状态
Git **工作区**中的文件,可能为以下状态:
- **==未跟踪==**(Untracked)
- **已跟踪**(Tracked)
- **==未修改==**(Unmodified)
- **==已修改==**(Modified)
- **==已暂存==**(Staged)
- **忽略**(Ignored)
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-58645B076C0C944D5CCD7D08199DA46C.png|625]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-D2C540FCDC4E5685ADED0DBF1EA8138E.png|325]]
#### 未跟踪 Untracked
文件在当前 Git 工作区中存在,但**未被 Git 跟踪**。即文件在仓库的历史记录中不存在,也没有被记录在即将提交的快照中。
当添加一个新文件到工作目录但**还没有使用 `git add` 将其添加到暂存区**时,该文件就处于未跟踪状态。
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-234826FF401174A8268C3DCD42922245.png|500]]
#### 已跟踪 Tracked
文件已被 Git 纳入跟踪。
对于已跟踪的文件,暂存区或仓库中会存有其文件快照,因此**同一文件**存在三个版本:
1. **工作区**中的**当前文件**
2. **暂存区**中的**文件快照**
3. **仓库中上一版提交**的**文件快照**
根据三个版本之间的匹配/一致关系,已跟踪会处于以下三个子状态之一:
- **未修改(<font color="#4f81bd">Unmodified</font>)**:文件**自上次提交后没有被修改**——与**最近一次提交的快照相匹配**。
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-E57A9D3EC02D1AC1BA4A3F20E3D891E2.png]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-3775B1D4756A8F158C991054ADE83980.png|414]]
- **已修改(<font color="#4f81bd">Modified</font>)**:文件**自上次提交后==已经被修改==** 且未添加到暂存区,与 **==暂存区==** 中的不同。
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-102756407DBE79D9AFF0242C89A7BD9E.png]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-DF1A3AA9C8DD36995093F89C6B5E12A6.png|450]]
- **已暂存(<font color="#4f81bd">Staged</font>)**:文件**自上次提交后已修改** 且 **已通过 `git add` 添加到暂存区**,与 **==暂存区==** 中的相同。
暂存区中的文件快照**将会被包含在下一次提交中**。
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-B3E5CDAEBE79730976E6731C27106860.png]]
完成提交后,最终变为:
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-BF24DDE58F50152BF206F28A2CFFB738.png]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-B263FA067A718394491A6BEAF2B1B5E7.png|384]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-500692C6F3EAA4BF8B3224C7948AE67F.png|341]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-988F456AD43EA5B06B7D564DE1A88B56.png|500]]
#### 忽略 Ignored
文件被 Git 忽略,**不会被跟踪或显示为未跟踪**。
"**忽略指定文件/目录**" 通过在 `.gitignore` 文件中指定**文件或目录的匹配模式/规则**来实现(参见 [[#.gitignore 忽略提交]]),
所有被匹配上的文件/目录都将被 Git 忽略,而不会纳入版本控制管理中。
需要忽略的文件通常是一些编译产物或者运行时生成的文件,如日志、编译输出(. o、. class 文件)等,这些文件通常不应该包含在版本控制中。
<br><br><br>
# Git 内部原理 ⭐
两方面:objects 与 refs
## Git 数据对象
在数据层面上,Git 仓库 (`.git`目录) 中存储有**四种类型**的**数据对象**:
- **Blob**:用于存储 "**==文件数据==**" 的数据对象,即 "**文件快照**" 所对应的对象
- Git 对 "**文件内容**" 计算得到 SHA-1 哈希值,**基于该哈希值创建一个 Blob 对象**,作为该文件的 "**快照**"。
- **Blob 含义:Binary Large Object**,**二进制大对象**
- **Tree**:用于存储 "**==目录结构==**" 的数据对象,作为 "**对 Blob 或其他 Tree的「索引」**"
- 可以链接到其他 tree(子目录)和 blobs(文件)
- **Commit**:用于存储 "**==提交信息==**" 的数据对象
- 代表 **整个项目的一个快照**,包含一个**指向顶层目录树的指针**、作者/提交者信息、时间戳以及**指向上一个提交的指针**等元数据。
- **Tag**:用于存储 **标签** 的数据对象
- 包含标签创建者信息、日期、注释信息以及一个**指向 commit 对象的指针**。
#### Git 数据对象示意
> 参见[^1] [^2]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-5F3B4DAB29B4ED669B421330ECACC447.png|421]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-1AEACB5543ABC53F046B699E92460D06.png|410]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-2CA4F4B453AE5D2998974ADEEEC1BA6C.png|686]]
<br>
#### Git 内部数据存储模型示例
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-E36AE9EA7D64C6CE103FC312E2D72585.png|554]]
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-B1131784853427457747F8653A7EFF50.png|273]]
<br><br>
## Git 引用
Git 中具有四种引用:
- **==分支引用==**:指向一个 **commit 对象**,表示**该分支的最新提交**;
- **==远程跟踪分支引用==**(**==只读==**):指向一个 **commit 对象**,表示 **==本地已知==的** "**远程仓库上该分支的最后提交**"。
- **==标签引用==**:分为 "**轻量标签**" 和 "**注释标签**"
- **轻量标签**:直接指向一个 **commit 对象**;
- **注释标签**:指向一个**tag 对象**,而标签对象本身则指向一个提交对象;
- **==HEAD 引用==**:**指向 "分支引用" 的引用**,表示**该分支为 "==当前检出分支=="** <br> ![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-F56C2FAEE895F40E0F9EBC9FD4D17A75.png|366]]
四种引用对应的**存储文件**如下:
| 引用 | 存储路径 | 文件实体示例 |
| ------------ | ----------------------- | ------------------------------------------------------------------------- |
| **分支引用** | `.git/refs/heads/` 目录 | `master` 分支对应的引用文件为 `.git/refs/heads/master` |
| **远程跟踪分支引用** | `.git/refs/remotes/` 目录 | 远程名为 `origin`、分支名为 `main` 的远程跟踪分支,对应引用文件为 `.git/refs/remotes/origin/main` |
| **标签引用** | `.git/refs/tags/` 目录 | 标签 `v1.0` 的引用文件就是 `.git/refs/tags/v1.0` |
| **HEAD 引用** | `.git/HEAD` 文件 | |
<br>
## Git 中 "抽象概念" 与 "数据实体" 的对应关系
- **文件快照** —— **Blob 对象** ——> `.git/objects` 目录下的文件
- **提交** —— **Commit 对象** ——> `.git/objects` 目录下的文件
- **分支** —— **分支引用** ——> `.git/refs/heads/` 目录下的文件
- **当前分支** —— **HEAD 引用** ——> `HEAD` 文件
- **远程跟踪分支** —— **远程跟踪分支引用** -> `.git/refs/remotes/` 目录下的文件
- **标签** —— **标签引用** ——> `.git/refs/tags/` 目录下的文件
### 提交——commit 对象
一个提交代表 "**工作区中==所有已纳入跟踪的文件==在某一时间点的==状态快照**==" 。
每个提交都包含**对文件的更改详情**、**一个唯一的 ID(通常称为“提交哈希”或“提交 ID”)**、提交者的信息、时间戳以及一个**指向上一个提交的指针**,这形成了一个提交历史链。
Git 仓库中保存的 commit 虽然是**对所有已纳入跟踪的文件建立快照**,但**并非简单粗暴的复制整个目录**,而是只对那些 "自上一版本来已发生变更的文件" 创建了新的文件快照,并且会将这些所有新建立的文件快照统一打包后作为一个 commit 记录。
### 分支——指向 commit 的引用
"**分支**" 本质上是 "**指向 commit 的引用**","**当前分支**" 本质上即是 **"HEAD" 引用所指向的 "分支引用"**,
而**分支切换**的本质即是**令 HEAD 引用指向不同的 "分支" 引用**。
在 Git 仓库中,**每个 "分支" 都有一个对应的引用文件**,存储在 `.git/refs/heads/` 目录下。
- 创建分支时,Git 通常是**在 `.git/refs/heads/` 目录中创建一个新的文件**,并写入**当前 HEAD 指向的提交的哈希值**。
- 切换分支时,**Git 更新 `.git/HEAD` 文件,修改了 HEAD 指针的指向,使其指向新分支的引用**,然后**变更工作区中的文件为==新分支最后一次提交的版本==**
- 合并分支时,Git 将另一分支的内容合并到当前分支,更新**当前分支引用所指的提交**:如果合并成功且自动提交,则分支引用指向新生成的合并提交。
### 远程跟踪分支——remote-tracking branches
**"远程跟踪分支" 引用以 `<remote>/<branch>` 命名,例如`origin/main`** ,指向 **==本地已知==的** "**远程仓库上在该分支的最后提交**"。
%%
~~**远程跟踪分支引用**保存于本地仓库中的 `.git/FETCH_HEAD` 文件中,“远程跟踪分支”也即指对远程分支的 `FETCH_HEAD`。~~
- ~~`.git/FETCH_HEAD` 文件: 该文件中保存有一个`FETCH_HEAD`列表,**记录着本地当前已拉取的远程仓库分支的末端版本**,文件中每一行对应着远程仓库的一个分支,文件第一行为当前分支对应的`FETCH_HEAD`。~~
- ~~`FETCH_HEAD`: 一个版本链接,记录在本地文件中,指向目前已经从远程仓库拉取下来的分支的末端版本(**即本地所记录的远程仓库上某个 branch 的末端版本/最新状态**)~~
%%
----
<br><br><br>
# Git 工具
安装 Git 后,包含下列工具:
![[_attachment/05-工具/git 使用/git 使用.assets/IMG-git 使用-AC222F9890283BB41A4DB6DFE3BD3964.png|400]]
- **Git Bash**:在 Windows 系统上提供了一个**基于 bash 的命令行界面**,**模拟 Unix/Linux 中的 ==shell 环境==**,**支持常见的 shell 命令**以及 **Git 相关命令**。
- **Git CMD**:提供了一个类似 **Windows CMD 的环境**,但**加入了对 Git 相关命令的支持**。
- **Git GUI**:图形界面版本,提供图形界面操作;
> [!NOTE] **git 的相关命令都需要 Git Bash 或 Git CMD 环境中使用**,直接在 cmd 中键入命令是无法被识别的。
#### Git Bash
在 Git Bash 中,Windows 上的文件系统路径表现为 `/磁盘分区`,例如 Windows 上路径 `E:\test_git` 在其中对应表示为 `/E/test_git`。
![[_attachment/05-工具/git 使用/git 使用.assets/IMG-git 使用-3DFF94DD13B03889C8FB0FB59B7E7767.png|301]]
<br><br><br>
# Git GUI
除 Git 内置 GUI 外,还有许多第三方的 Git GUI 客户端:
- Github Desktop
- GitKraken (Gitlens 插件的团队,收费)
- Source Tree
- Tower
#### GITHub Desktop 入门
官方文档:
[GitHub Desktop 文档 - GitHub 文档](https://docs.github.com/zh/desktop)
- [GitHub | \[新手超入門\]無痛使用GitHub桌面板](https://hugheschung.blogspot.com/2018/08/github.html)
- [好工具推荐系列:Github客户端GitHub Desktop使用方法 - 掘金](https://juejin.cn/post/6963589971945111588)
#### GitKraken
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-62E465A4365E5C553469A6055FBF2924.png|705]]
#### Tower
![[_attachment/05-工具/git 使用/git 说明.assets/IMG-git 说明-35B11775EDD4451FD179646837165F33.png|632]]
# 参考资料
- [How to explain git in simple words? | XOSH.ORG](https://xosh.org/explain-git-in-simple-words/) ⭐
# Footnotes
[^1]: [版本控制(Git) · the missing semester of your cs education](https://missing-semester-cn.github.io/2020/version-control/)
[^2]: [How to explain git in simple words? | XOSH.ORG](https://xosh.org/explain-git-in-simple-words/)