Git 完全指南 —— 第3章:理解工作区、暂存区、版本库三个核心

理论铺垫了这么多,终于要动手了。这一章我们将创建自己的第一个 Git 仓库,完成从零到第一次提交的全过程。在这个过程中,你会接触到 Git 最核心的概念------工作区、暂存区和版本库。理解这三个区域的关系,是掌握 Git 一切操作的基础。别急,我们一步一步来。

一、 Git仓库的创建

创建一个 Git 仓库有两种方式:从零开始初始化,或者从远程仓库克隆一份。分别对应git initgit clone命令,它们适用于不同的场景。

1. git init:从零开始

当你有一个已有的项目目录(或者一个空目录),想把它纳入 Git 管理时,可以使用 git init对项目进行初始化为一个git仓库。

bash 复制代码
# 创建一个新目录并进入
mkdir my-project
cd my-project

# 初始化 Git 仓库
git init
# 输出:Initialized empty Git repository in /path/to/my-project/.git/

执行 git init 后,Git 会在当前目录下创建一个隐藏的 .git 目录。这个目录就是 Git 仓库的全部。以后所有的版本历史、配置信息、元数据都存储在这里。外部的文件和目录就是你的"工作区"。

适用场景:本地新建项目、将已有项目纳入版本控制。

2. git clone:从远程复制

当你需要参与一个已有的远程项目时,则使用 git clone命令克隆远程仓库到你的本地。克隆一个远程仓库,最常见的方式有HTTPS和SSH两种方式。

bash 复制代码
# 使用 HTTPS 克隆
git clone https://github.com/user/repo.git

# 使用 SSH 克隆
git clone git@github.com:user/repo.git

# 克隆到指定目录名
git clone https://github.com/user/repo.git my-folder

从表面上看,git clone命令就是把一个远程的仓库下载到本地,但是实际上其做的事情远比"下载代码"多得多,它会执行:

  1. 创建一个新目录
  2. 初始化 .git 仓库
  3. 拉取所有历史版本数据
  4. 检出默认分支的最新版本到工作区
  5. 自动设置远程仓库的引用(origin

也就是说,git clone 相当于 git init + git remote add + git fetch + git checkout 的组合。

适用场景:参与开源项目、加入团队协作、获取公开代码库。

两者的关键区别

特性 git init git clone
起始状态 空仓库,无历史记录 完整仓库,包含全部历史
远程关联 自动关联 origin
使用场景 新建项目 加入已有项目
工作区 空的或有已有文件 包含最新版本的代码

当你本地有个项目,并在远程创建另一个项目,在本地执行git init后,可以通过git add remote命令将本地项目与远程仓库进行关联。

二、 工作区、暂存区、版本库

Git 的操作模型可以用"三棵树"来理解。这里的"树"不是数据结构中的树,而是对三个不同区域的形象比喻。

1. 工作区(Working Directory)

工作区就是你在文件管理器中看到的那个项目目录。你在这里编辑代码、创建文件、删除文件。工作区中的文件直接存在磁盘上,Git 不会自动追踪它们的变化。

2. 暂存区(Staging Area / Index)

暂存区是 Git 独有的概念,也是初学者最容易困惑的地方。你可以把它理解为一个"提交候选清单"。在工作区修改完内容后,你告诉 Git "我打算在下一次提交中包含这些修改",但还没有真正提交。

暂存区的存在让你拥有了对提交内容的精确控制权。你可以修改了 10 个文件,但只选择其中 3 个文件放入暂存区,形成一个逻辑完整的提交。

暂存去在物理上通常对.git/index下的一个二进制文件,里面记录了文件的权限、哈希值、阶段编号和文件路径。

3. 版本库(Repository / .git

版本库就是 .git 目录所存储的一切。它包含了所有的提交历史、所有的分支信息、所有的标签。你在工作区和暂存区的操作,最终都会被"固化"到版本库中。

三棵树之间的数据流动

flowchart LR WD[&#34;工作区<br/>Working Directory<br/>(你编辑文件的地方)&#34;] SA[&#34;暂存区<br/>Staging Area/Index<br/>(提交候选清单)&#34;] REPO[&#34;版本库<br/>Repository<br/>(.git 目录)&#34;] WD -- git add --> SA SA -- git commit--> REPO REPO -- git checkout --> WD REPO -- git reset --> SA SA -- git restore --staged --> WD style WD fill:#e1f5fe style SA fill:#fff9c4 style REPO fill:#c8e6c9

理解这个流程图,就理解了 Git 日常操作的核心。所有命令的本质都是在三棵树之间移动数据:

  • git add:把工作区的修改放入暂存区
  • git commit:把暂存区的内容固化到版本库
  • git checkout:把版本库中的文件恢复到工作区
  • git reset:把版本库中的提交回退到暂存区
  • git restore --staged:把暂存区的内容退回工作区

三、 记录第一次提交

让我们用一个完整的例子走一遍从创建到提交的全流程。

bash 复制代码
# 创建项目目录
mkdir hello-git && cd hello-git

# 初始化仓库
git init

# 创建第一个文件
echo "# Hello Git" > README.md
echo 'console.log("Hello, Git!")' > index.js

# 查看当前状态
git status

此时 git status 的输出会显示:

vbnet 复制代码
On branch main
No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md
        index.js

nothing added to commit but untracked files present (use "git add" to track)

Git 告诉你有两个"未追踪"的文件。它们存在于工作区中,但 Git 还没有开始管理它们,并提示你可以通过git add命令对它们进行跟踪。

1. git add 的本质

git add 做的事情远不止"把文件加入跟踪列表"这么简单。在底层,Git 会计算每个文件内容的 SHA-1 哈希值,创建对应的 blob 对象,然后更新暂存区的树结构。我们会在第4章深入探讨这些底层细节。

接下来我们可以使用git add命令添加一个或多个文件到暂存区。

bash 复制代码
# 添加单个文件
git add README.md

# 添加多个文件
git add README.md index.js

# 添加所有文件(注意:. 代表当前目录下所有变更)
git add .

# 交互式添加(可以选择文件的部分修改)
git add -p

此时再次使用git status查看状态,输出变为:

vbnet 复制代码
    On branch main
    No commits yet

    Changes to be committed:
      (use "git rm --cached <file>..." to unstage)
            new file:   README.md
            new file:   index.js

文件从 "未追踪" 变成了 "待提交"(staged) ,说明它们已经进入暂存区了。在这个过程中,git会更新索引文件.git/index,记录文件的元信息,并将文件的压缩后存储到.git/objects下。

使用git ls-files --stage可以查看暂存区的索引信息。

2. git commit 的本质

git commit 会把暂存区中的快照永久保存到版本库中,生成一个唯一的提交对象。-m 参数指定提交信息。如果没有 -m,Git 会打开编辑器让你输入更详细的提交信息。

bash 复制代码
git commit -m "feat: 初始化项目,添加 README 和入口文件"


输出:

    [main (root-commit) a1b2c3d] feat: 初始化项目,添加 README 和入口文件
     2 files changed, 2 insertions(+)
     create mode 100644 README.md
     create mode 100644 index.js

在这个过程中,git会先根据暂存区存储的内容,打包成 Tree对象,并生成一个Commit 对象,Commit对象里面包含了本次提交的元数据(作者、时间、提交信息)以及指向刚才生成的 Tree 对象的指针;接着会更新当前所在分支的引用文件(存放在 .git/refs/heads/,例如 .git/refs/heads/main),这就相当于把当前分支的"指针"向前推进了一步;提交完成后,Git 会清空或重置 .git/index 文件的状态,使其与刚刚提交的 Commit 保持一致。这意味着暂存区被清空了,准备迎接下一次新的修改。

另外,git commit的信息是非常有讲究的,这在团队协作、项目复盘等场景非常重要。如果你有参加过一些开源项目或者你的团队对git管理得当的话,通常都会对git提交信息做一些规范的约束。一个良好的提交信息通常应该遵循以下规范:

xml 复制代码
<type>(<scope>): <subject>

<body>
  • type :提交类型,如 feat(新功能)、fix(修复)、docs(文档)、refactor(重构)、test(测试)、chore(杂项,比如删除冗余代码)
  • scope:影响范围,可选
  • subject:简短描述,不超过 50 个字符
  • body:详细说明,可选

3. 查看提交历史

使用git log命令可以查看所有的提交记录信息。

bash 复制代码
# 简洁的提交日志
git log --oneline

# 更详细的日志
git log --stat

# 图形化分支历史
git log --oneline --graph --all

四、 .gitignore 编写与模板

在实际开发中,并不是所有文件都应该被 Git 管理。编译产物、依赖包、临时文件、敏感配置------这些文件不应该进入版本库。

.gitignore 文件就是用来告诉 Git "哪些文件应该被忽略"的规则文件。

1. 常见 .gitignore 规则

gitignore 复制代码
# ========== 编译产物 ==========
*.o
*.class
*.pyc
build/
dist/
*.js.map

# ========== 依赖目录 ==========
node_modules/
vendor/
venv/

# ========== IDE 配置 ==========
.idea/
.vscode/
*.swp
*.swo

# ========== 操作系统文件 ==========
.DS_Store
Thumbs.db

# ========== 环境配置(可能包含密钥)==========
.env
.env.local
*.pem

# ========== 日志文件 ==========
*.log
logs/

# ========== 大文件 ==========
*.zip
*.tar.gz

2. .gitignore 的匹配语法

模式 含义
*.log 忽略所有 .log 结尾的文件
build/ 忽略 build 目录及其所有内容
/config.json 只忽略根目录的 config.json
!important.log 不忽略 important.log(即使前面有 *.log 规则)
docs/*.pdf 忽略 docs 目录下的 PDF 文件(不包含子目录)
docs/**/temp 忽略 docs 下任意层级的 temp 目录

3. 一个容易踩的坑

如果你不小心把一个不该提交的文件(比如 .env)提交了,之后再添加到 .gitignore 中是无效的,因为 Git 已经在追踪这个文件了。解决方法是先从 Git 的追踪中移除它:

bash 复制代码
# 从 Git 追踪中移除,但保留本地文件
git rm --cached .env
git commit -m "chore: 从版本控制中移除 .env 文件"

4. 获取社区维护的 .gitignore 模板

GitHub 维护了一个丰富的 .gitignore 模板仓库,覆盖了几乎所有主流语言和框架:

bash 复制代码
# 查看可用的模板
git ls-remote https://github.com/github/gitignore

直接访问 github.com/github/giti... 即可浏览和下载。

五、 文件的生命周期四种状态

Git 中的文件有四种状态,理解这四种状态及其转换关系,是掌握 Git 工作流的关键。

stateDiagram-v2 [*] --> Untracked : 新建文件 Untracked --> Staged : git add Staged --> Committed : git commit Committed --> Modified : 编辑文件 Modified --> Staged : git add Staged --> Modified : 编辑已暂存的文件 Committed --> Untracked : git rm Staged --> Untracked : git rm --cached note right of Untracked : Git 不追踪此文件 note right of Staged : 已标记为待提交 note right of Committed : 已保存到版本库 note right of Modified : 工作区有未暂存的修改

1. 四种状态详解

(1)Untracked(未追踪) :文件存在于工作区中,但 Git 从未管理过它。.gitignore 就是针对这类文件的,被 .gitignore 匹配的未追踪文件不会出现在 git status 的输出中。

(2)Staged(已暂存):文件被标记为"准备提交"。此时文件的一个快照已经被保存到暂存区中,但还没有成为正式的提交。

(3)Committed(已提交) :文件已经通过 git commit 被永久保存到版本库中。此时工作区中的文件内容和版本库中完全一致。

(4)Modified(已修改) :文件已经被 Git 追踪,但工作区中的内容和版本库中最后一次提交的内容不一致。你需要再次 git add 把修改放入暂存区,然后 git commit 提交。

2. 用 git status 读懂文件状态

git status 会是你使用频率最高的命令之一。它的输出清晰地告诉你每个文件处于什么状态:

bash 复制代码
git status

On branch main
Changes to be committed:        ← 已暂存(Staged)
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

Changes not staged for commit:  ← 已修改但未暂存(Modified)
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   index.js

Untracked files:                ← 未追踪(Untracked)
  (use "git add <file>..." to include in what will be committed)
        utils.js

3. 一个完整的操作示例

下面这个例子展示了文件在四种状态之间的完整流转:

bash 复制代码
# 创建新文件 → Untracked
echo "工具函数" > utils.js

# 添加到暂存区 → Staged
git add utils.js

# 提交 → Committed
git commit -m "feat: 添加工具函数模块"

# 修改文件 → Modified
echo "// 新增一个函数" >> utils.js

# 再次添加到暂存区 → Staged
git add utils.js

# 再次提交 → Committed
git commit -m "feat: utils 添加辅助函数"

# 从版本库中删除文件 → Untracked(文件从磁盘上也删除了)
git rm utils.js
git commit -m "chore: 移除 utils 模块"

4. 使用git diff查看差异

在文件处于 Modified 状态时,你可能想看看具体改了什么。git diff 就是干这个的:

bash 复制代码
# 查看工作区与暂存区的差异(未暂存的修改)
git diff

# 查看暂存区与版本库的差异(已暂存但未提交的修改)
git diff --staged

# 查看工作区与版本库的差异(所有未提交的修改)
git diff HEAD

# 查看某个文件的差异
git diff index.js

git diff 的输出默认使用 unified diff 格式,用颜色标注了增加的行(绿色)和删除的行(红色)。

六、小结

这一章我们完成了 Git 使用的"第一步":创建仓库、理解三棵树模型、完成第一次提交。核心内容如下:

  • 创建仓库git init 从零开始,git clone 从远程复制。两者适用的场景完全不同。
  • 三棵树模型:工作区(编辑文件的地方)、暂存区(提交候选清单)、版本库(永久存储)。Git 的日常操作本质上就是在这三棵树之间移动数据。
  • git add 和 git commitgit add 把工作区的修改放入暂存区,git commit 把暂存区的内容固化到版本库。暂存区的存在让你可以精确控制每次提交包含哪些修改。
  • .gitignore :告诉 Git 哪些文件不需要管理。注意,已经被追踪的文件不会自动被忽略,需要先用 git rm --cached 移除追踪。
  • 文件四种状态 :Untracked → Staged → Committed → Modified → Staged,形成一个循环。git status 是查看文件状态的最佳工具。

下一章,我们将打开 .git 目录这个"黑盒",探索 Git 的内部世界,包括对象模型、SHA-1 哈希、以及 Git 是如何在底层存储每一次提交的。

相关推荐
江华森3 小时前
Git 基础筑基:从原理到团队协作的全栈实战
git
JakeJiang7 小时前
Git 必备命令指南:从日常高频到项目开发实战
git
叫我少年1 天前
Windows 中安装 git
git
深海鱼在掘金6 天前
Git 完全指南 —— 第1章:Git 概览与版本控制演进
git
noravinsc7 天前
关于Git Flow
git
蜜獾云7 天前
在Git中配置用户名和密码
git
scx_link7 天前
通过git bash在本地创建分支,并推送到远程仓库中
开发语言·git·bash
南大白7 天前
IntelliJ IDEA 运行时的 JVM 本地内存溢出崩溃
git
码农小旋风7 天前
Claude Code 基础用法大全:对话、分析、修改、测试、Git 和工作流
人工智能·git·chatgpt·claude