文章目录
- [Git 文件状态管理:add、commit、status 和 diff](#Git 文件状态管理:add、commit、status 和 diff)
-
- [一、Git 管理的是修改,而不只是文件](#一、Git 管理的是修改,而不只是文件)
- [二、文件在 Git 中的常见状态](#二、文件在 Git 中的常见状态)
-
- [1. 未跟踪状态:Untracked](#1. 未跟踪状态:Untracked)
- [2. 已暂存状态:Staged](#2. 已暂存状态:Staged)
- [3. 已提交状态:Committed](#3. 已提交状态:Committed)
- [4. 已修改但未暂存:Modified](#4. 已修改但未暂存:Modified)
- [三、git add 和 git commit:暂存与提交](#三、git add 和 git commit:暂存与提交)
-
- [1. git add 的作用](#1. git add 的作用)
- [2. git commit 的作用](#2. git commit 的作用)
- [3. 多次 git add 可以对应一次 git commit](#3. 多次 git add 可以对应一次 git commit)
- [四、一个容易踩坑的例子:只提交了 add 过的文件](#四、一个容易踩坑的例子:只提交了 add 过的文件)
- [五、git status:看懂当前仓库状态](#五、git status:看懂当前仓库状态)
- [六、git diff:查看文件具体改了什么](#六、git diff:查看文件具体改了什么)
-
- [git status 和 git diff 的区别](#git status 和 git diff 的区别)
- [git diff HEAD -- file](#git diff HEAD -- file)
- 七、修改文件后的推荐提交流程
-
- [1. 查看当前状态](#1. 查看当前状态)
- [2. 查看具体差异](#2. 查看具体差异)
- [3. 加入暂存区](#3. 加入暂存区)
- [4. 再次查看状态](#4. 再次查看状态)
- [5. 提交到本地仓库](#5. 提交到本地仓库)
- [6. 确认工作区干净](#6. 确认工作区干净)
- [八、git log 和 commit id:查看提交历史](#八、git log 和 commit id:查看提交历史)
-
- [commit id 为什么不是简单数字](#commit id 为什么不是简单数字)
- [九、从 .git 目录理解 add、commit 和对象存储](#九、从 .git 目录理解 add、commit 和对象存储)
-
- [1. index:暂存区](#1. index:暂存区)
- [2. HEAD:当前分支指针](#2. HEAD:当前分支指针)
- [3. refs/heads/master:当前分支最新提交](#3. refs/heads/master:当前分支最新提交)
- [4. objects:Git 对象库](#4. objects:Git 对象库)
- [5. commit、tree、blob 对象](#5. commit、tree、blob 对象)
- 总结
Git 文件状态管理:add、commit、status 和 diff
前面已经整理了如何创建 Git 本地仓库,也知道了 Git 中几个非常重要的区域:
- 工作区
- 暂存区
- 版本库
这一篇继续往下走,重点整理 Git 中最常用的几个命令:
git addgit commitgit statusgit diffgit log
这些命令看起来简单,但它们是 Git 日常使用的核心。
尤其是 git add 和 git commit,很多刚开始学习 Git 的人会把它们混在一起。其实这两个命令分别对应不同阶段:
text
git add :把修改放入暂存区
git commit :把暂存区内容提交到本地仓库
理解清楚这一点,后面学习版本回退、撤销修改、分支合并时会轻松很多。
一、Git 管理的是修改,而不只是文件
刚开始学习 Git 时,很容易以为 Git 管理的是文件。
比如创建了一个 ReadMe 文件,然后执行:
bash
git add ReadMe
git commit -m "commit my first file"
看起来好像 Git 管理的是这个文件本身。
但更准确地说,Git 跟踪并管理的是文件的变化,也就是"修改"。
什么是修改?
下面这些都算修改:
- 新增一个文件;
- 删除一个文件;
- 修改文件中的一行内容;
- 删除文件中的一行内容;
- 修改某几个字符;
- 新增一个空文件;
- 文件改名;
- 文件权限变化。
所以 Git 关注的不是"你有没有这个文件",而是:
和上一次提交相比,当前工作区发生了哪些变化。
这也是为什么 git status 和 git diff 很重要。
git status用来查看当前有哪些变化;git diff用来查看具体变化内容。

二、文件在 Git 中的常见状态
一个文件在 Git 仓库中,通常会经历几个状态。
最常见的有下面几类。
1. 未跟踪状态:Untracked
新建一个文件,但还没有执行 git add,这个文件就是未跟踪状态。
比如:
bash
touch file1
git status
可能会看到类似提示:

这表示 Git 发现了 file1,但它还没有被纳入 Git 管理。
简单来说:
文件出现在工作区,不代表它已经进入 Git 管理。
2. 已暂存状态:Staged
当执行:
bash
git add file1
文件就会进入暂存区。
这时再查看状态:
bash
git status
可能会看到:

这说明 file1 已经准备好进入下一次提交。
也就是说:
暂存区保存的是下一次 git commit 要提交的内容。
3. 已提交状态:Committed
执行:
bash
git commit -m "add file1"
暂存区中的内容就会被提交到本地仓库。
提交之后,这次修改就真正进入了版本库。
这时再执行:
bash
git status
如果没有新的修改,通常会看到:
bash
nothing to commit, working tree clean

这表示当前工作区是干净的,没有需要提交的新变化。
4. 已修改但未暂存:Modified
如果一个已经被 Git 管理的文件发生了修改,但还没有执行 git add,它就是已修改但未暂存状态。
比如修改 ReadMe 文件后执行:
bash
git status
可能会看到:
bash
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: ReadMe

这表示 Git 知道 ReadMe 被修改了,但这个修改还没有加入暂存区。

三、git add 和 git commit:暂存与提交
git add 和 git commit 是 Git 本地操作中最核心的一组命令。
1. git add 的作用
git add 的作用是:
把工作区中的修改添加到暂存区。
常见用法有几种。
添加单个文件:
bash
git add ReadMe
添加多个文件:
bash
git add file1 file2 file3
添加某个目录:
bash
git add src
添加当前目录下所有修改:
bash
git add .
git add . 很方便,但也容易把不想提交的文件一起加进去。
所以在执行 git add . 前,最好先看一下:
bash
git status
确认当前有哪些文件发生了变化。
2. git commit 的作用
git commit 的作用是:
把暂存区中的内容提交到本地仓库。
最常见用法是:
bash
git commit -m "提交说明"
比如:
bash
git commit -m "add ReadMe file"
这里的 -m 后面是提交说明。
提交说明不是给 Git 看的,而是给人看的。
以后查看历史记录时,我们需要通过提交说明快速判断某次提交做了什么。
不推荐这样写:
bash
git commit -m "update"
因为 update 太笼统了,过几天再看根本不知道更新了什么。
更推荐写成:
bash
git commit -m "add user login page"
git commit -m "fix login error message"
git commit -m "add order query api"
好的提交说明应该尽量做到:
- 简洁;
- 明确;
- 能说明本次提交的目的。
3. 多次 git add 可以对应一次 git commit
暂存区的一个重要作用是:
可以先收集多次修改,然后一次性提交。
比如创建三个文件:
bash
touch file1 file2 file3
分别加入暂存区:
bash
git add file1
git add file2
git add file3
最后一次性提交:
bash
git commit -m "add three files"
提交成功后,可能会看到类似输出:
bash
[master 23807c5] add three files
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file1
create mode 100644 file2
create mode 100644 file3
这说明三个文件被作为同一次提交保存到了版本库中。
这里要重点理解:
git commit 提交的是暂存区里的内容,而不是工作区里的全部内容。

四、一个容易踩坑的例子:只提交了 add 过的文件
来看一个很容易踩坑的例子。
先创建 file4:
bash
touch file4
把 file4 加入暂存区:
bash
git add file4
然后再创建一个 file5:
bash
touch file5
此时直接提交:
bash
git commit -m "add file"
提交成功后可能会看到:
bash
[master 3d406c0] add file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file4
这里明明创建了两个文件:file4 和 file5,为什么只提交了一个文件?
原因很简单:
只有 file4 执行过 git add,进入了暂存区;file5 只是存在于工作区,并没有进入暂存区。
所以这次 git commit 只提交了 file4。
如果要提交 file5,还需要继续执行:
bash
git add file5
git commit -m "add file5"
这个例子非常重要,因为它说明:
Git 不会因为文件存在于工作区,就自动把它提交到版本库。
五、git status:看懂当前仓库状态
git status 是日常使用 Git 时非常重要的命令。
它的作用是:
查看当前工作区和暂存区的状态。
比如修改 ReadMe 文件后执行:
bash
git status
可能会看到:
bash
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: ReadMe
no changes added to commit (use "git add" and/or "git commit -a")
这段信息可以这样理解:
text
On branch master
表示当前位于 master 分支。
text
Changes not staged for commit
表示有修改还没有加入暂存区。
text
modified: ReadMe
表示 ReadMe 文件被修改了。
text
no changes added to commit
表示当前暂存区没有可提交内容。
所以这时如果想提交 ReadMe 的修改,需要先执行:
bash
git add ReadMe
然后再提交:
bash
git commit -m "modify ReadMe"
常见状态总结
如果看到:
bash
nothing to commit, working tree clean
表示当前没有需要提交的修改。
如果看到:
bash
Untracked files:
file1
表示 file1 是新文件,但还没有被 Git 跟踪。
如果看到:
bash
Changes not staged for commit:
modified: ReadMe
表示文件已修改,但还没有加入暂存区。
如果看到:
bash
Changes to be committed:
modified: ReadMe
表示修改已经进入暂存区,可以提交。

六、git diff:查看文件具体改了什么
git status 只能告诉我们哪些文件发生了变化,但不能告诉我们具体改了哪里。
如果想查看具体修改内容,就需要使用:
bash
git diff
或者查看某个文件:
bash
git diff ReadMe
注意:git diff 只能对比已经被 git
跟踪的文件的修改。对于全新的文件,git
没有旧版本可以对比,所以 git diff 什么都不显示。
解决方法: 先把文件添加到 git 跟踪
之后你再修改 ReadMe,git diff 就能看到变化了。
假设 ReadMe 原来是:
text
hello bit
hello bit
现在修改成:
text
hello bit
hello git
hello world
执行:
bash
git diff ReadMe
可能会看到类似输出:
diff
diff --git a/ReadMe b/ReadMe
index 9c9e1f0..4a97140 100644
--- a/ReadMe
+++ b/ReadMe
@@ -1,2 +1,3 @@
hello bit
-hello bit
+hello git
+hello world
这段 diff 信息可以这样理解:
diff
-hello bit
前面带 -,表示这一行被删除了。
diff
+hello git
+hello world
前面带 +,表示这些行是新增的。
所以这次修改可以总结为:
- 删除了一行
hello bit; - 新增了一行
hello git; - 新增了一行
hello world。
git status 和 git diff 的区别
它们的区别可以简单理解为:
text
git status:告诉你哪些文件变了
git diff :告诉你文件具体怎么变了
真实开发中,一般建议提交前至少执行一次:
bash
git status
如果发现有修改,再执行:
bash
git diff
确认修改内容没问题后,再提交:
bash
git add .
git commit -m "xxx"
提交前检查差异,可以避免把调试代码、临时文件、错误修改一起提交进去。
git diff HEAD -- file
除了普通的 git diff,还有一种常见写法:
bash
git diff HEAD -- ReadMe
它表示:
查看当前工作区文件和版本库中最新版本之间的差异。
这里先简单理解:
HEAD可以先理解为当前版本;ReadMe是要比较的文件;--用来分隔版本和文件路径,避免歧义。
刚开始不需要把所有细节都记死,可以先记住:
如果想看某个文件相对于当前版本到底改了什么,可以用 git diff HEAD -- 文件名。
七、修改文件后的推荐提交流程
修改文件后,比较推荐的完整提交流程是:
bash
git status
git diff ReadMe
git add ReadMe
git status
git commit -m "modify ReadMe file"
git status
逐步来看:
1. 查看当前状态
bash
git status
确认有哪些文件发生变化。
2. 查看具体差异
bash
git diff ReadMe
确认这次修改是否符合预期。
3. 加入暂存区
bash
git add ReadMe
把修改加入暂存区。
4. 再次查看状态
bash
git status
此时可能会看到:
bash
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: ReadMe
表示修改已经进入暂存区,等待提交。
5. 提交到本地仓库
bash
git commit -m "modify ReadMe file"
提交成功后可能会看到:
bash
[master 94da695] modify ReadMe file
1 file changed, 2 insertions(+), 1 deletion(-)
这表示:
- 1 个文件发生变化;
- 新增了 2 行;
- 删除了 1 行。
6. 确认工作区干净
bash
git status
如果看到:
bash
nothing to commit, working tree clean
说明当前没有待提交修改。

八、git log 和 commit id:查看提交历史

提交完成后,可以使用:
bash
git log
查看历史提交。
示例:
bash
commit 23807c536969cd886c4fb624b997ca575756eed6 (HEAD -> master)
Author: zhangsan <zhangsan@example.com>
Date: Sat May 6 11:27:32 2023 +0800
add three files
commit c61428926f3853d4ec6dde904415b0e6c1dabcc6
Author: zhangsan <zhangsan@example.com>
Date: Sat May 6 11:25:50 2023 +0800
commit my first file
git log 默认会按时间从近到远显示提交历史。
每条提交通常包含:
commit id- 作者
- 提交时间
- 提交说明
其中 commit id 是 Git 为每次提交生成的唯一标识。
它不是简单的 1、2、3 递增数字,而是一个很长的哈希值。
比如:
text
23807c536969cd886c4fb624b997ca575756eed6
后面做版本回退时,经常会用到 commit id。
如果觉得 git log 输出太长,可以使用:
bash
git log --pretty=oneline
输出会更加简洁:

commit id 为什么不是简单数字
Git 的 commit id 是通过哈希算法计算出来的一个非常大的十六进制数字。
这么设计有几个好处:
- 几乎可以保证每次提交的 ID 唯一;
- 适合分布式协作场景;
- 不依赖一个中心服务器来分配版本号;
- 可以通过内容生成标识。
现在不需要深入哈希算法,只要先知道:
每次提交都会生成一个唯一的 commit id,它是后面定位历史版本的重要依据。
九、从 .git 目录理解 add、commit 和对象存储
如果只停留在命令层面,Git 可能会显得有点抽象。
其实可以通过 .git 目录理解这些命令背后的变化。
本地仓库中有几个比较关键的文件或目录:
text
.git/
├── HEAD
├── index
├── objects
└── refs/
└── heads/
└── master
1. index:暂存区
.git/index 可以简单理解为暂存区。
当执行:
bash
git add ReadMe
暂存区会被更新。
也就是说:
git add 会影响 .git/index。
2. HEAD:当前分支指针
查看 .git/HEAD:
bash
cat .git/HEAD
可能会看到:
bash
ref: refs/heads/master
这表示当前 HEAD 指向 master 分支。
现在不需要深入分支,只要先理解:
HEAD 和当前所在分支、当前版本有关。
3. refs/heads/master:当前分支最新提交
查看:
bash
cat .git/refs/heads/master
可能会看到:
bash
23807c536969cd886c4fb624b997ca575756eed6
这表示 master 分支当前指向这个提交 ID。
4. objects:Git 对象库
.git/objects 保存 Git 的对象数据。
提交、目录树、文件内容,最终都会以对象形式保存在这里。
可以简单理解为:
objects 目录保存了 Git 维护版本历史所需的底层数据。

5. commit、tree、blob 对象
Git 对象一般不能直接打开看,因为它们是经过 Git 特殊方式存储的。
不过可以使用:
bash
git cat-file -p commit_id
查看某个对象内容。
比如:
bash
git cat-file -p 23807c536969cd886c4fb624b997ca575756eed6

可能会看到:
bash
tree 830a8c9feefbdc098bbae2cdc25e5034ce1920d7
parent c61428926f3853d4ec6dde904415b0e6c1dabcc6
author zhangsan <zhangsan@example.com> 1683343652 +0800
committer zhangsan <zhangsan@example.com> 1683343652 +0800
add three files
这里可以看到几个信息:
tree:这次提交对应的目录树对象;parent:上一次提交;author:作者;committer:提交者;- 最后一行是提交说明。
继续查看 tree 对象:
bash
git cat-file -p 830a8c9feefbdc098bbae2cdc25e5034ce1920d7
可能会看到:
bash
100644 blob 9c9e1f0f6bff3015df71a0963004476f5e6cfd54 ReadMe
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file1
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file2
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file3
这里的 blob 可以理解为文件内容对象。
继续查看 ReadMe 对应的 blob:
bash
git cat-file -p 9c9e1f0f6bff3015df71a0963004476f5e6cfd54
可能会看到:
text
hello bit
hello bit
这说明 Git 确实把文件内容记录到了对象库中。
入门阶段不需要把 Git 对象模型完全吃透,但这个例子能帮助我们建立一个重要认识:
Git 的提交并不是简单复制文件,而是通过 commit、tree、blob 等对象组织项目快照。

总结
这一篇主要整理了 Git 文件状态管理相关内容。
重点包括:
- Git 管理的是修改,而不仅仅是文件;
- 文件在 Git 中常见的几种状态;
git add的作用;git commit的作用;- 多次
git add可以对应一次git commit; - 没有进入暂存区的文件不会被提交;
git status如何查看仓库状态;git diff如何查看具体差异;- 修改文件后的推荐提交流程;
git log如何查看历史提交;commit id的作用;.git/index、.git/HEAD、.git/refs、.git/objects的基本作用;commit、tree、blob对象之间的关系。
总结:
text
先用 git status 看状态,再用 git diff 看具体修改,确认无误后 git add,最后 git commit。
这个流程是以后每天都会用到的基础操作。
下一篇会继续整理 Git 中非常重要的能力:版本回退与撤销修改。
重点会包括:
git reset是什么;--soft、--mixed、--hard有什么区别;HEAD、HEAD^、HEAD~n怎么理解;- 如何回退到历史版本;
- 如何撤销工作区和暂存区的修改;
- 删除文件后如何恢复。