三、Git 文件状态管理:add、commit、status 和 diff

文章目录

  • [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 add
  • git commit
  • git status
  • git diff
  • git log

这些命令看起来简单,但它们是 Git 日常使用的核心。

尤其是 git addgit 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 statusgit 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 addgit 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

这里明明创建了两个文件:file4file5,为什么只提交了一个文件?

原因很简单:

只有 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 的基本作用;
  • committreeblob 对象之间的关系。

总结:

text 复制代码
先用 git status 看状态,再用 git diff 看具体修改,确认无误后 git add,最后 git commit。

这个流程是以后每天都会用到的基础操作。

下一篇会继续整理 Git 中非常重要的能力:版本回退与撤销修改

重点会包括:

  • git reset 是什么;
  • --soft--mixed--hard 有什么区别;
  • HEADHEAD^HEAD~n 怎么理解;
  • 如何回退到历史版本;
  • 如何撤销工作区和暂存区的修改;
  • 删除文件后如何恢复。
相关推荐
Ws_13 小时前
Git + Gerrit 第三课:分支、切换与合并
git·elasticsearch
xlq2232214 小时前
6.git
git
Drache_long14 小时前
Git命令概述
git
console.log('npc')14 小时前
修改git中commit内容
git
love8888_cnsd15 小时前
Git & Linux 速查表
java·linux·git·后端·elasticsearch
恋喵大鲤鱼1 天前
git checkout
git·git checkout
知识汲取者1 天前
Git撤销操作全解析:revert、undo与drop commit的区别与应用
git
cxxx171 天前
【同步Overleaf, Github】
git·overleaf
染翰1 天前
Linux root用户安装配置Git
linux·git·后端