git原理-文件是如何存储的

本文深入探讨Git如何存储文件,解析blob、tree、commit等对象作用,并揭示文件版本控制及分支切换背后的机制。

原文链接 https://www.treeroot.tech/archives/git%E5%8E%9F%E7%90%86-%E6%96%87%E4%BB%B6%E6%98%AF%E5%A6%82%E4%BD%95%E5%AD%98%E5%82%A8%E7%9A%84

git原理-文件是如何存储的

一直都对git的存储原理都很好奇,所以今天就专门研究了一下,记录一下研究的过程。

我们以这样的目录结构为例来研究,
这里写图片描述
首先cd gitTest目录,初始化使用 git init 命令,执行了这个命令之后gitTest目录下回生成一个.git文件,我们查看一下这个文件下都有什么内容,如下:
这里写图片描述
然后执行:
git add --all
git commit -am “commit 1 。。。。。。”
这我们优先看objects目录,git会将所有改动的文件存到.git/objects/的文件夹下,查看一下内容:
这里写图片描述

#1.git文件对象
git的文件对象分为四类分别为blob、tree、commit、tags。

git管理的文件都以blob类型存在,而目录文件则以tree类型存在,tree类型内部可以引用若干个blob和tree文件。

也就是说当我们执行了commit 之后git 会依照我们工程的目录结构在objects里相应的建立一套blob和tree文件,可是我们的示例工程只有两个文件和一个文件夹,为什么objects里确有五个文件那,这里就要说说commit类型了,commit类型内部可以引用一个tree类型文件和一个commit类型文件。

关于文件的名字,objects内部的文件全部是以sha-1命名,如果是blob类型则以文件内容做sha-1计算得出40字符的校验和,然后为了不让这个objects内部文件过多,所以使用40字符的前两位来建立一个文件夹,在以后38位为文件名,包括commit 和tree 全部使用sha-1校验和来命名。
接下来说一下这五个文件是以什么结构关联的:
每个commit 引用一个tree,根据工程目录结构这个tree 在引用一个blob(a.txt)和一个tree(bb) ,这个tree(bb)则引用一个blob(b.txt)如图:
这里写图片描述
通过这个图就可以看出来commit,tree,blob的关系了,也基本能明白为什么objects下有五个文件了,
现在我改动一下a.txt然后在提交一个版本,在a.txt添加22222222222内容执行add和commit,添加一个版本,接下来再看objects内部的情况:
这里写图片描述
比刚刚多了三个文件,究竟是哪三个文件?因为我们改动了a.txt文件,所以这里在保留上一个版本的a.txt的基础上新增加一个a.txt文件,git只对有改动的文件进行备份保留。另外两个文件分别是commit 2和一个根tree,现在这8个文件的关系如图:
这里写图片描述
commit文件内部可以引用一个commit,这样commit之间就可以建立关系了,因为只有a.txt文件做了改动所以只有a.txt文件新建了一个,然后被commit2引用,bb和b.txt文件未做改动则commit2依然引用之前的文件。同理如果我们再改动b.txt文件,我们可以设想一下这些文件的关联关系。如图:
这里写图片描述

#2.branch分支
git branch 可以获取当前的分支列表,这个分支列表会保存在./git/refs/heads/这个路径下,这里包含master和一些其他分支文件,以master文件为例查看master文件内容如下:
这里写图片描述
其实这是一个commit类型 的文件名,这样每个分支都可以拥有一个自己的commit引用,从上上面的图可以看出只要拿到commit的文件名就可以找到所有跟他关联的文件,还有个问题是./git/refs/heads/这个文件下是所有分支信息,总要有一个当前分支,其实这个当前分支是被记录在./git/HEAD文件内部的查看一下:这里写图片描述
因为当前是master分支所以HEAD文件里记录的是master,每次我们git checkout 某某来切换分支的时候就是在修改HEAD这个件内容。那么我们基本就可以理解我们首先选择一个分支为当前分支,每个分支里记录着当前分支最顶端的commit对象,这个commit对象又可以找到所有跟它关联tree和blob,同时commit对象又和它的历史commit关联。我们可以任意切换当前分支,同时又可以修改当前分支指向的commit对象,比如我们执行reset可以选择回退到任意一个commit。这些就可以顺理成章切换任意分支并且找到任意版本的文件了。

<think>我们正在讨论的是 `git branch -a` 命令的含义。根据引用内容,特别是引用[4]中明确提到了 `git branch -a` 命令的输出示例,我们可以得知该命令的作用是列出所有本地和远程分支。 引用[4]的原文: ``` 4. 查看本地和远程分支 $ git branch -a * dev2 master wchar_support remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/wchar_support ``` 另外,引用[3]中介绍了git branch命令用于创建、列出、删除、切换分支,而引用[2]和引用[1]也提到了branch命令的相关操作。 因此,我们可以总结出: `git branch -a` 命令用于显示本地仓库中所有的分支(包括本地分支和远程跟踪分支)。其中: - 本地分支位于 `refs/heads/` 命名空间下 - 远程跟踪分支位于 `refs/remotes/` 命名空间下 在输出中,远程跟踪分支会以远程仓库的名称(如origin)为前缀,例如 `remotes/origin/master`。 同时,输出中会用一个星号(*)标记当前所在的分支。 所以,回答应该包括: 1. 命令的作用:列出所有本地分支和远程跟踪分支。 2. 输出格式:星号标记当前分支,本地分支直接显示,远程分支以 `remotes/` 开头。 3. 与相关命令的对比:比如 `git branch`(仅本地分支)、`git branch -r`(仅远程跟踪分支)等。 此外,我们还可以给出一个示例输出,并解释其含义。 注意:根据引用[4]的例子,输出中会有 `remotes/origin/HEAD -> origin/master`,这表示远程仓库的HEAD指向origin/master分支。 最后,按照要求生成相关问题。 </think>### `git branch -a` 命令详解 `git branch -a` 是 Git 中用于**列出所有本地和远程分支**的核心命令。以下是其含义与工作原理: #### 📌 核心定义 **公式表达**: $$ \text{git branch -a} = \{\text{本地分支} \} \cup \{\text{远程跟踪分支}\} $$ 其中: - `本地分支`:存储在 `.git/refs/heads/` 下的分支 - `远程跟踪分支`:存储在 `.git/refs/remotes/` 下的分支引用 #### 🔍 输出结构解析 ```bash $ git branch -a * main # 当前本地分支(带*号) feature/login # 其他本地分支 remotes/origin/main # 远程跟踪分支 remotes/origin/dev # 远程跟踪分支 ``` 1. **本地分支**(无前缀) - 直接显示分支名(如 `main`, `feature/login`) - `*` 标记当前检出分支[^3] 2. **远程跟踪分支**(带 `remotes/` 前缀) - 格式:`remotes/<远程仓库名>/<分支名>`(如 `remotes/origin/main`) - 表示远程仓库分支在本地缓存的引用副本[^4] #### ⚖️ 与其他分支命令对比 | 命令 | 作用范围 | 输出示例 | |---------------------|-----------------------|--------------------------| | `git branch` | **仅本地分支** | `main`, `feature/login` | | `git branch -r` | **仅远程跟踪分支** | `origin/main`, `origin/dev` | | `git branch -a` | **本地+远程跟踪分支** | 如上完整输出 | #### 💻 典型应用场景 1. **查看所有可用分支** ```bash git branch -a # 列出本地和远程所有分支 ``` 2. **创建基于远程分支的新分支** ```bash git switch -c new-feature remotes/origin/dev # 基于远程dev分支创建本地分支 ``` 3. **清理过时远程引用** ```bash git fetch --prune # 删除本地已失效的远程跟踪分支 git branch -a # 验证清理结果 ``` #### 🌐 工作原理 1. **本地分支** - 存储在 `.git/refs/heads/` 目录 - 每个分支对应一个文件(如 `main` 文件存储 commit ID) 2. **远程跟踪分支** - 存储在 `.git/refs/remotes/<远程名>/`(如 `.git/refs/remotes/origin/main`) - 通过 `git fetch` 或 `git pull` 更新[^1] ```mermaid graph LR A[远程仓库] -- git fetch --> B[.git/refs/remotes/] C[本地操作] -- 创建/修改 --> D[.git/refs/heads/] B & D -- git branch -a --> E[终端输出] ``` #### 🚨 注意事项 1. **远程分支非实时**:需运行 `git fetch` 更新远程跟踪分支状态 2. **删除限制**:不可直接删除远程跟踪分支(需用 `git push origin --delete branch`) 3. **颜色标记**:多数 Git 配置会用不同颜色区分本地/远程分支 > 通过 `git branch -a`,开发者可全面掌握分支拓扑结构,为代码集成和协作奠定基础[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值