%% # 纲要 > 主干纲要、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/)