开篇介绍:
hello 大家,那么在我们之前,写了一堆又一堆的代码,不管是进程、线程等等,还是什么其他的一大坨代码,我们总能神奇的发现,诶,好像我上一次写的代码会更好诶,嗯,那我要上一次写的代码哈哈,然后就没了,为什么,因为我们没有上一次写的代码呀,我们一没保存二没保存的,去哪里找?????
那么这显然就是很痛苦,不过,Git听到了你的召唤,所以,Git------版本控制器,它就来了,它将帮助我们完美的解决这一问题,夸夸夸的拿下,哈哈,那么接下来,我们就先来学习一个Git的基础操作,看看它是怎么帮我们解决问题的。
你是否也在经历 "版本地狱"?
你有没有过这样的经历:写毕业论文时,文件夹里躺着「论文 v1.docx」「论文 v2 最终版.docx」「论文 v3 究极版.docx」「论文 v4 改到吐版.docx」...... 最后答辩前,你翻遍所有版本,却想不起来哪个是最终确定的版本;
写代码时,改了几行逻辑后发现功能崩了,想回到半小时前的版本,却只能对着屏幕干瞪眼 ------ 因为你只靠 "复制粘贴" 保存版本,而半小时前的副本早就被覆盖了;甚至帮同事改个文档,改完后对方说 "还是原来的好",你却找不到最初的版本,只能重新做一遍。
这些场景的核心问题只有一个:你没有一套可靠的「版本管理」方法。而 Git,就是解决这个问题的 "终极武器"。
前置知识:先搞懂 3 个基础问题
在开始敲命令之前,我们先回答三个 "灵魂拷问",避免你稀里糊涂学操作:
1. 什么是版本控制?
版本控制(Version Control),说白了就是 "给你的文件做「快照」+「日记本」":
- 「快照」:每次你对文件做了重要修改,都可以拍一张 "照片" 保存下来,哪怕之后改崩了,也能随时回到这张快照的状态;
- 「日记本」:每一张快照都要写一句 "备注",比如 "修改了论文的引言部分""修复了代码里的计算错误",方便你之后找到想要的版本。
没有版本控制,你就只能靠手动复制文件来做 "快照",不仅文件越积越多,还记不清每个版本改了啥;有了版本控制,所有修改都被有序记录,想回退、想查看,一键就能搞定。
2. Git 是什么?
Git 是目前世界上最流行的「分布式版本控制系统」(不用记这个名词,知道核心就行),简单说:
- 它是免费的、开源的;
- 它能在你的电脑本地运行(不用联网也能用);
- 它专门用来跟踪「文本文件」的修改(比如代码、Markdown 文档、TXT)------ 注意:图片、视频等二进制文件,Git 只能记录 "文件大小变了",但没法知道具体改了啥,不过这对新手来说暂时不用关心。
3. 学习 Git 的核心原则:先本地,再远程
很多人一上来就被 "GitHub""推送拉取" 搞懵,但其实 Git 的核心能力是「本地版本管理」------ 你完全可以不用联网、不用远程仓库,只在自己的电脑上用 Git 管理文件。
准备工作:安装 Git 并完成基础配置
在开始操作前,你需要先安装 Git,并做最简单的配置(这一步是前提,跳过会导致后续命令报错)。
第一步:安装 Git
Git 支持 Windows、Mac、Linux 三大系统,安装方法超简单:
- Windows :去 Git 官网(https://git-scm.com/download/win)下载安装包,一路点击 "下一步" 即可(记得勾选 "Git Bash Here",方便右键打开命令行);
- Mac :打开 "终端",输入
xcode-select --install安装 Xcode 命令行工具,或者用 Homebrew 安装:brew install git; - Linux(CentOS) :终端输入
sudo yum -y install git; - Linux(Ubuntu) :终端输入
sudo apt-get install git -y。
安装完成后,打开命令行(Windows 是 Git Bash,Mac/Linux 是终端),输入git --version,如果能看到类似git version 2.42.0的输出,说明安装成功了。
第二步:配置用户名和邮箱
Git 需要知道 "谁在提交修改",所以必须配置用户名和邮箱(不用真实邮箱,格式对就行)。命令如下:
bash
# 配置全局用户名(所有仓库都用这个)
git config --global user.name "你的昵称"
# 配置全局邮箱
git config --global user.email "你的邮箱@示例.com"
比如你可以输入:
bash
git config --global user.name "小明学Git"
git config --global user.email "xiaoming@git.com"
解释一下:
-
--global:表示这是 "全局配置",你电脑上所有的 Git 仓库都会用这个用户名和邮箱;如果想给某个仓库单独配置,去掉--global即可,但是要这么做的话,我们就必须是要在已经进入了创建好了的仓库中,再去使用,不然就会报错哦,这一点需要注意。bashgit config user.name "你的昵称" git config user.email "你的邮箱@示例.com" -
用户名和邮箱只是标识,不用和任何平台绑定,只要格式正确就行。
验证配置是否成功:输入
bash
git config -l
能看到你刚才配置的user.name和user.email,就说明配置好了。
取消配置:
那么我们要是想取消掉我们所配置的信息呢,诶嘿,那就需要使用到git unset指令了
bash
#删除所配置的信息
git config --global --unset user.name
git config --global --unset user.email
一样的,如果你创建的时候是带着--global的话,那么你unset的时候也得带上,不然无法奏效,而要是你创建的时候没带的话,那么unset的时候也不必带上。

第一大核心操作:git init(初始化本地仓库)
在开始管理文件之前,你得先告诉 Git:"我要把这个文件夹当成「版本管理仓库」,你要盯着里面的所有修改!"------ 这个 "告诉" 的动作,就是
bash
git init
1. 什么是 Git 仓库?
先给 Git 仓库一个通俗的定义:Git 仓库就是一个「被 Git 监控的文件夹」------ 这个文件夹里的所有文件(新增、修改、删除),只要你通过 Git 命令 "报备",Git 就会记录每一次变化;如果没报备,Git 就当没看见。
你可以把 Git 仓库类比成 "带监控的文件柜":
- 普通文件夹:你往里面放文件、改文件,没人记录;
- Git 仓库:你往里面放文件,监控会记录 "谁、什么时候、改了什么",还能随时调阅历史记录。
2. git init 到底做了什么?
git init的核心作用是:在你指定的文件夹里,创建一个隐藏的.git目录 ------ 这个目录就是 Git 的 "控制中心",里面存着 Git 跟踪版本所需的所有数据(比如快照、提交记录、配置等)。
重要提醒 :千万不要手动修改或删除
.git目录里的任何文件!一旦改乱,整个仓库的版本记录就会失效,相当于 "监控系统被拆了"。
3. 实操:用 git init 创建仓库
我们用一个具体的场景演示:你想创建一个叫my-first-git-repo的文件夹,把它变成 Git 仓库,用来管理你的学习笔记。
步骤 1:打开命令行,切换到目标目录
首先,你需要在命令行里 "走到" 你想创建仓库的位置。比如你想在桌面创建仓库:
-
Windows(Git Bash) :
# 切换到桌面(cd = change directory,切换目录) cd ~/Desktop # 查看当前目录(pwd = print working directory,打印当前路径) pwd # 输出应该是:/c/Users/你的用户名/Desktop -
Mac/Linux :
# 切换到桌面 cd ~/Desktop # 查看当前目录 pwd # 输出应该是:/Users/你的用户名/Desktop
步骤 2:创建文件夹(可选)
如果你还没有my-first-git-repo文件夹,先创建:
# mkdir = make directory,创建目录
mkdir my-first-git-repo
步骤 3:进入文件夹,执行 git init
# 进入新建的文件夹
cd my-first-git-repo
# 初始化Git仓库
git init
执行git init后,命令行会输出类似这样的内容:
Initialized empty Git repository in /c/Users/小明/Desktop/my-first-git-repo/.git/
这句话的意思是:"在指定路径下,初始化了一个空的 Git 仓库"------ 翻译成人话就是:"Git 已经准备好监控这个文件夹了!"

步骤 4:验证仓库是否创建成功
输入ls -a(-a表示显示隐藏文件),能看到.git目录,就说明仓库创建成功了:
ls -a
# 输出:. .. .git

tree -a的意思是把隐藏的目录的结构也显示出来。
4. git init 的常见误区与问题
误区 1:重复执行 git init 会覆盖仓库?
不会!哪怕你在已经初始化的仓库里再执行git init,Git 也只会提示:
Reinitialized existing Git repository in /xxx/my-first-git-repo/.git/
意思是 "重新初始化了已存在的仓库",不会删除任何版本记录,放心执行。
误区 2:我能把任意文件夹变成仓库吗?
可以!不管文件夹里有没有文件,都能执行git init。比如你有一个已经写了一半的笔记文件夹,直接进入该文件夹执行git init,就能让 Git 开始监控它。
误区 3:创建仓库后,文件夹名能改吗?
可以!Git 是通过.git目录识别仓库的,只要.git目录还在,哪怕你把文件夹改名为我的笔记,它依然是 Git 仓库。
问题:如果我想放弃这个仓库,该怎么做?
很简单:删除仓库文件夹里的.git目录即可。比如在my-first-git-repo里执行:
# Windows/Mac/Linux通用
rm -rf .git
删除后,这个文件夹就变回普通文件夹,所有版本记录都会消失(谨慎操作!)。
5. 为什么必须先执行 git init?不执行会怎样?
如果不执行git init,Git 就不知道要监控哪个文件夹 ------ 你后续执行git add、git commit等命令时,Git 会直接报错:
fatal: not a git repository (or any of the parent directories): .git
翻译过来就是:"这不是一个 Git 仓库,我没法干活!"
简单说:
git init是所有 Git 操作的 "起点",没有它,后面的一切都免谈。
第二大核心操作:git add & git commit(添加与提交)
初始化仓库后,你终于可以让 Git 帮你管理文件了 ------ 而git add和git commit,就是 Git 管理文件的 "两大核心步骤"。
一、先理清核心逻辑:作家写小说 = Git 管理文件
Git 的 "工作区 - 暂存区 - 版本库" 三层架构,本质是把 "修改文件" 拆成 "临时创作→整理待存→永久存档" 三个步骤,就像作家写小说不会直接把草稿塞存档柜,而是先在书桌写、再整理到稿夹、最后归档 ------ 这样既灵活(可调整待存内容),又安全(存档后不丢)。
二、逐区域详细解读
1. 工作区:作家的 "创作书桌"
通俗类比(作家视角)
你的书桌是创作的 "主战场":
- 你在空白草稿纸上写《第 1 章 初遇》的初稿(对应:在仓库文件夹里新建文件);
- 写完后觉得某段剧情不好,划掉重写(对应:修改仓库里已有的文件);
- 写废了一页草稿,揉成团扔垃圾桶(对应:删除仓库里的文件);
- 这些草稿都摊在书桌上,随时能改、随时能丢,没有 "定稿" 的属性,只是临时的创作内容。
技术定义(Git 视角)
工作区(Working Directory)是你电脑里能直接看到、直接操作的 Git 仓库文件夹(就是执行git init后生成的那个文件夹),是所有文件编辑、新增、删除的 "第一线"。
核心特征(双视角对比)
| 对比维度 | 作家视角 | Git 视角 |
|---|---|---|
| 可见性 | 草稿纸摊在书桌上,肉眼能直接看、直接改 | 文件夹里的文件能双击打开、编辑,在电脑里清晰可见 |
| 稳定性 | 草稿随时改,没有 "不能动" 的约束 | 文件修改后仅保存在本地磁盘,Git 不会自动记录这些修改 |
| 状态标识 | 草稿属于 "未整理" 状态,没被纳入存档计划 | 新文件默认是「未跟踪(Untracked)」;已跟踪的文件修改后是「已修改(Modified)」 |
常见操作(作家 ↔ Git 一一对应)
| 作家的操作 | Git 对应的操作 | 操作结果 |
|---|---|---|
| 在书桌写新草稿《第 1 章.txt》 | touch 第1章.txt + 用编辑器写内容 |
工作区新增「未跟踪」文件,Git 知道这个文件存在,但没开始管理 |
| 修改《第 1 章.txt》的剧情细节 | 打开文件删掉某段、新增对话 | 工作区文件变为「已修改(未暂存)」,Git 能检测到文件和上次存档的差异 |
| 扔掉写废的《废稿.txt》 | rm 废稿.txt |
工作区文件被删除,Git 会标记为「已删除」状态,提示你处理 |
易错点(作家视角 + Git 视角)
- 作家:别把重要草稿随便堆在书桌角落,容易被清理;
- Git:工作区的修改如果没执行
git add,一旦误删文件 / 覆盖内容,无法通过 Git 恢复(因为 Git 还没记录这些修改)。
2. 暂存区:作家的 "待打印稿夹"
通俗类比(作家视角)
你在书桌上写完《第 1 章》的前半部分,觉得这部分内容没问题、可以定稿了,就把这几张草稿纸整理好,放进标着 "待打印 - 第 1 章" 的文件夹里 ------ 这个文件夹就是 "暂存区":
- 稿夹只放 "确认要定稿" 的草稿,不是所有草稿都往里塞(比如写废的片段仍留在书桌);
- 你可以分批次放:先放《第 1 章》前半部分,写完后半部分再补进去;
- 如果你发现稿夹里某张草稿有笔误,还能抽出来改,改完再放回去;
- 稿夹里的内容只是 "待存档",还没真正变成 "终稿",随时能调整。
技术定义(Git 视角)

暂存区(Stage/Index)是 Git 的 "中间缓冲区",存储在 Git 仓库的.git隐藏目录里的index文件中(你看不到这个文件,但 Git 能识别),作用是临时保存 "准备提交到版本库" 的变更------ 相当于给 "要存档的内容" 做一道 "筛选门"。
核心特征(双视角对比)
| 对比维度 | 作家视角 | Git 视角 |
|---|---|---|
| 可见性 | 稿夹是闭合的,不打开看不到里面的草稿 | 无法直接双击打开查看暂存区内容,需用git status/git diff --staged命令查看 |
| 稳定性 | 稿夹里的草稿是 "待定稿",可调整但不轻易改 | 暂存区的变更不会自动同步工作区的新修改 ------ 修改文件后需重新git add才会更新 |
| 状态标识 | 稿夹里的草稿属于 "待存档" 范围 | 文件处于「已暂存(Staged)」状态,等待git commit指令 |
常见操作(作家 ↔ Git 一一对应)
| 作家的操作 | Git 对应的操作 | 操作结果 |
|---|---|---|
| 把《第 1 章》前半部分草稿放进待打印稿夹 | git add 第1章.txt |
该文件从「未跟踪 / 已修改」变为「已暂存」,进入 "待存档" 状态 |
| 写完《第 1 章》后半部分,补进稿夹 | 修改文件后再次执行git add 第1章.txt |
暂存区同步最新修改,覆盖之前的暂存内容(确保存档的是最新版) |
| 发现稿夹里某张草稿写错,抽出来(保留草稿) | git restore --staged 第1章.txt(旧版 Git 用git reset HEAD 第1章.txt) |
该文件从「已暂存」变回「已修改」,工作区的草稿(修改)仍保留,可重新调整 |
| 把稿夹里所有草稿都抽出来 | git reset HEAD . |
暂存区清空,所有文件回到工作区状态,相当于 "放弃本次整理" |
易错点(作家视角 + Git 视角)
- 作家:别把写错的草稿直接放进稿夹,否则存档后会带着错误;
- Git:
- 修改已暂存的文件后,没重新执行
git add------ 此时暂存区还是旧版本,git commit只会存档旧内容; - 用
git add .时误把临时文件(如临时笔记.txt)放进暂存区,导致没必要的内容被纳入存档计划。
- 修改已暂存的文件后,没重新执行
3. 版本库:作家的 "带锁存档柜"
通俗类比(作家视角)
你把待打印稿夹里的《第 1 章》所有草稿整理好,在封面写上 "第 1 章终稿 - 2026 年 1 月 15 日",然后把这份终稿放进带锁的存档柜里:
- 存档柜里的终稿是 "永久保存" 的,不会随便修改;
- 每一份终稿都有唯一标记(日期 + 章节),能清楚查到 "第 1 章终稿" 是哪天存的;
- 后续想改第 1 章,会写新的草稿(工作区)、整理到稿夹(暂存区),再存一份 "第 1 章修订版 - 2026 年 1 月 16 日" 进存档柜,不会覆盖原来的终稿;
- 存档柜里的所有终稿,构成了你小说的完整版本历史。
技术定义(Git 视角)

版本库(Repository)是 Git 的核心存储区域,位于.git隐藏目录的objects等子目录中,存储所有已提交的 "版本快照"(提交对象)------ 每一次git commit都会生成一个唯一的 SHA-1 哈希 ID(commit ID),相当于终稿的 "日期 + 章节" 标记,永久保存变更。
核心特征(双视角对比)
| 对比维度 | 作家视角 | Git 视角 |
|---|---|---|
| 可见性 | 存档柜带锁,需打开才能看到里面的终稿 | 无法直接查看版本库内容,需用git log(查历史)/git checkout(恢复旧版本)查看 |
| 稳定性 | 终稿永久保存,不轻易修改,可追溯 | 提交后的版本快照不可修改(修改会生成新快照),所有历史记录可通过git log完整查看 |
| 状态标识 | 终稿属于 "已存档",有唯一标识 | 文件处于「已提交(Committed)」状态,每个提交有唯一的 commit ID |
常见操作(作家 ↔ Git 一一对应)
| 作家的操作 | Git 对应的操作 | 操作结果 |
|---|---|---|
| 把稿夹里的《第 1 章》终稿存入存档柜,标注说明 | git commit -m "第1章终稿:完成初遇剧情" |
生成新的版本快照,存入版本库,工作区和暂存区变 "干净"(无未提交修改) |
| 发现终稿标注写错,修改标注 | git commit --amend -m "第1章终稿:完成初遇+伏笔剧情" |
修改最近一次提交的备注,生成新的 commit ID(相当于重写终稿封面) |
| 想找回存档柜里上周的《第 1 章》旧版终稿 | 1. git log --oneline找到旧版 commit ID;2. git checkout <ID> -- 第1章.txt |
从版本库恢复旧版本文件到工作区,可重新编辑 |
| 撤回刚存入存档柜的终稿(保留草稿) | git reset --soft HEAD^ |
提交记录被撤销,变更回到暂存区(相当于把终稿抽回稿夹) |
易错点(作家视角 + Git 视角)
- 作家:存档柜的终稿标注要清晰(比如 "第 1 章终稿 - 20260115"),否则后期找不到对应版本;
- Git:
- 提交备注写 "改了点东西""123"------ 后期用
git log查历史时,完全不知道这次存档改了啥; - 滥用
git reset --hard HEAD^------ 会彻底删除版本库的最新提交,且丢弃工作区 / 暂存区的所有修改,新手慎用。
- 提交备注写 "改了点东西""123"------ 后期用
三、超详细汇总表格
| 区域 | 通俗类比(作家场景) | 技术定义 | 对应物理位置 | 核心状态 | 核心操作 | 关键特征 | 新手必避坑点 |
|---|---|---|---|---|---|---|---|
| 工作区 | 书桌(草稿纸) | 可直接编辑的 Git 仓库文件夹 | 电脑里可见的仓库文件夹 | 未跟踪 / 已修改 / 已删除 | 新建 / 修改 / 删除文件 | 可见、易修改、无自动版本记录 | 未git add的修改丢失无法恢复;误删文件无备份 |
| 暂存区 | 待打印稿夹 | Git 的中间缓冲区,存储待提交的变更 | .git/index文件 |
已暂存 | git add / git restore --staged | 不可见、可调整、需手动同步修改 | 修改文件后未重新git add;误加临时文件 |
| 版本库 | 带锁存档柜 | 存储所有已提交的版本快照,Git 的核心存储区 | .git/objects等子目录 |
已提交 | git commit / git log | 不可见、永久保存、有唯一 commit ID | 提交备注不清晰;滥用git reset --hard |
总结
- 工作区是 "临时创作区":看得见、改得快,但没被 Git 保护,修改前一定要确认是否需要保留;
- 暂存区是 "筛选整理区" :承上启下,能灵活调整要存档的内容,修改文件后务必重新
git add同步; - 版本库是 "永久存档区" :一旦
git commit就生成快照,有唯一 ID 可追溯,备注一定要清晰,别乱删版本。
这三层架构的核心价值是 "灵活 + 安全"------ 既可以在工作区自由修改,又能通过暂存区筛选内容,最后用版本库永久保存,这也是 Git 比 "手动复制文件改版本号" 更靠谱的根本原因。

- 图中左侧为工作区,右侧为版本库。Git 的版本库里存了很多东西,其中最重要的就是暂存区。
- 在创建 Git 版本库时,Git 会为我们自动创建一个唯一的 master 分支,以及指向 master 的一个指针叫 HEAD。(分支和HEAD的概念后面再说)
- 当对工作区修改(或新增)的文件执行 git add 命令时,暂存区目录树的文件索引会被更新。
- 当执行提交操作git commit 时,master 分支会做相应的更新,可以简单理解为暂存区的目录树才会被真正写到版本库中。
由上述描述我们便能得知:通过新建或粘贴进目录的文件,并不能称之为向仓库中新增文件,而只是在工作区新增了文件。必须要通过使用 git add 和 git commit 命令才能将文件添加到仓库中
进行管理!!!
核心逻辑:只有进入版本库的修改,才会被 Git 永久保存为 "快照";暂存区是工作区和版本库之间的 "中转站"。
为什么需要暂存区?举个例子:你在工作区改了 3 个文件(笔记 1.md、笔记 2.md、笔记 3.md),其中笔记 1 和笔记 2 是 "完成的修改",笔记 3 还没改完。如果没有暂存区,你要么把 3 个文件一起提交(包含未完成的笔记 3),要么都不提交(丢失笔记 1 和 2 的修改);有了暂存区,你可以先把笔记 1 和 2add到暂存区,提交到版本库,笔记 3 留在工作区继续改 ------ 这就是暂存区的价值:灵活选择要提交的修改。
1. git add:把修改 "送进" 暂存区
git add的核心作用是:将工作区中「有变化的文件」(新增、修改、删除)添加到暂存区,告诉 Git:"我要把这些修改纳入版本管理,准备存档了!"
1.1 git add 的基本用法
先在你的仓库里新建一个文件,比如notes.md,用来写学习笔记:
# 在my-first-git-repo里新建notes.md
touch notes.md
# 用编辑器打开文件(Windows可以用notepad,Mac用open,Linux用vim)
# Windows
notepad notes.md
# Mac
open -a TextEdit notes.md
# Linux
vim notes.md
在notes.md里写点内容,比如:
# Git学习笔记
## 第一天:认识git init
1. git init是初始化仓库的命令
2. 初始化后会生成.git隐藏目录
保存并关闭文件,现在工作区里有了notes.md这个新文件 ------ 接下来用git add把它加入暂存区。
用法 1:添加单个文件
# 格式:git add 文件名
git add notes.md
执行后,没有任何输出 ------ 这是 Git 的特点:"没消息就是好消息",只要不报错,就说明操作成功。
用法 2:添加多个文件
如果你又新建了todo.md文件,想同时添加notes.md和todo.md:
# 格式:git add 文件1 文件2
git add notes.md todo.md
用法 3:添加当前目录下所有修改的文件
如果你在工作区同时修改了多个文件、新建了多个文件,一个个输入文件名太麻烦,就可以用这个 "懒人命令":
# 格式:git add .
git add .
这里的.代表当前目录下的所有文件和子目录,执行这个命令后,Git 会自动扫描工作区,把所有 "有变化" 的文件(新增、修改、删除)都添加到暂存区。
注意事项:
- 这个命令很方便,但新手要慎用!比如你在工作区新建了一个
temp.txt临时文件,本来不想纳入版本管理,结果用git add .会把它一起加入暂存区。 - 解决办法:后续如果遇到这种情况,可以用
git reset HEAD temp.txt撤回这个文件的暂存状态(后面会讲)。
1.2 git add 的核心本质:让文件从 "未跟踪" 变成 "已暂存"
新建的文件在 Git 里有一个专属状态叫未跟踪(Untracked) ------ 意思是 Git 知道这个文件存在,但还没开始管理它。执行git add 文件名后,文件的状态就会变成已暂存(Staged) ------ 意思是文件已经进入暂存区,等待被提交到版本库。
你可以用git status(第三部分会详细讲)查看文件状态,比如新建notes.md后,未执行git add时,git status会输出:
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
notes.md
nothing added to commit but untracked files present (use "git add" to track)
这段输出的意思很明确:notes.md是未跟踪文件,用git add就能让它被 Git 跟踪。
1.3 git add 的常见误区与高频问题
误区 1:执行 git add 后,文件就被永久保存了?
错!git add只是把文件 "放进待打印稿夹",并没有存入 "存档柜"。如果此时你修改了文件,新的修改不会自动进入暂存区 ------ 你需要再次执行 git add,才能把新的修改加入暂存区。
举个例子:
- 你新建
notes.md,执行git add notes.md,此时暂存区里是notes.md的第一版内容; - 你又修改了
notes.md,添加了一行新内容,此时工作区的notes.md是第二版内容,但暂存区里还是第一版; - 如果你直接执行
git commit,提交的是第一版内容,第二版的修改会留在工作区; - 想要提交第二版,必须再次执行
git add notes.md,更新暂存区的内容。
问题 1:不小心 add 了不想提交的文件,怎么撤回?
比如你误把temp.txt加入了暂存区,想把它从暂存区撤回到工作区(变回未跟踪状态),用这个命令:
# 格式:git reset HEAD 文件名
git reset HEAD temp.txt
执行后,再用git status查看,会发现temp.txt又变回了 "Untracked files"。
原理 :
git reset HEAD <file>的作用是 "把暂存区里的该文件,恢复成版本库里的最新状态"------ 如果版本库里还没有这个文件(比如新建的temp.txt),就会直接从暂存区移除,变回未跟踪状态。
问题 2:修改了文件,但 git add 后不想保留修改,怎么回到原来的状态?
分两种情况:
- 只执行了 git add,没执行 commit :先执行
git reset HEAD 文件名撤回暂存,再执行git checkout -- 文件名,就能把工作区的文件恢复到暂存前的状态; - 已经执行了 commit:这个属于版本回退的范畴,后面在常见问题里会讲。
2. git commit:把暂存区的修改 "存入" 版本库
如果说git add是 "把草稿放进待打印稿夹",那git commit就是 **"把待打印稿夹里的内容整理好,写上备注,存入存档柜"** ------ 这是 Git 里唯一能生成版本快照的操作,也是让修改被永久保存的关键步骤。
2.1 git commit 的基本用法(必须掌握)
git commit的核心格式是:
# 格式:git commit -m "本次提交的备注信息"
git commit -m "初始化Git学习笔记"
-m后面就是要跟上你对这次提交上去的版本的改动,注意,一定要有!!!
执行成功后,Git 会输出类似这样的内容:
[master (root-commit) a1b2c3d] 初始化Git学习笔记
1 file changed, 3 insertions(+)
create mode 100644 notes.md
我们逐行解释这个输出的含义:
[master (root-commit) a1b2c3d]:master是当前分支名(新手不用管分支,默认就是 master);root-commit表示这是仓库的第一次提交 ;a1b2c3d是本次提交的唯一 ID(commit id),后续回退版本、查看历史都要用到它;1 file changed, 3 insertions(+):本次提交修改了 1 个文件,新增了 3 行内容;create mode 100644 notes.md:创建了notes.md文件,100644是文件的权限模式(不用关心)。
**最关键的规则:-m 参数后面的备注信息,一定要写清楚!**很多新手图省事,会写git commit -m "update"或者git commit -m "123"------ 这种备注信息等于没写。正确的备注信息要满足两个条件:
- 简洁:尽量控制在 50 个字符以内;
- 明确:让人一眼就知道这次提交改了什么。
比如:
- 好的备注:
git commit -m "添加git add三种用法的笔记"、git commit -m "修复notes.md里的错别字"; - 坏的备注:
git commit -m "改了点东西"、git commit -m "完成了"。
2.2 git commit 的常见参数
参数 1:-m (必选)------ 指定提交信息
这个参数我们已经讲过,它的作用是在命令行直接写提交信息 。如果省略-m参数,Git 会自动打开默认的文本编辑器(比如 Linux 的 vim、Windows 的记事本),让你输入提交信息 ------ 新手如果不小心进入这个界面,很容易不知道怎么退出。
如果不小心进入了编辑器界面(比如 vim),怎么退出?
- 按
Esc键,输入:wq,然后按回车 ------ 意思是 "保存(w)并退出(q)"; - 如果不想输入提交信息,按
Esc键,输入:q!,然后按回车 ------ 意思是 "强制退出(q!),不保存",这次提交会被取消。
参数 2:-am (常用)------ 跳过 git add,直接提交已跟踪文件
这个参数是-a和-m的组合,作用是把所有 "已跟踪" 的文件的修改,直接提交到版本库,跳过 git add 步骤。举个例子:你修改了已经提交过的notes.md文件(属于 "已跟踪文件"),想直接提交,不用先执行git add notes.md,可以直接用:
git commit -am "添加git commit的参数说明"
注意:-am 参数对 "未跟踪文件" 无效! 如果你新建了一个todo.md文件(未跟踪),执行git commit -am "新增todo.md"会报错 ------Git 会提示你todo.md是未跟踪文件,需要先执行git add todo.md。
总结一下-am的适用场景:只修改了已跟踪的文件,没有新建文件 ------ 这种场景下用-am可以节省一次git add的操作。
2.3 git commit 的常见误区与高频问题
误区 1:commit 之后,修改就不能撤回了?
错!Git 是一个 "后悔药管够" 的工具,哪怕 commit 了,也能撤回 ------ 但有一个前提:你还没有把修改推送到远程仓库(新手不用管远程仓库,本地随便撤回),至于怎么撤回,我们后面会说。
问题 1:commit 信息写错了,能不能修改?
能!用git commit -m --amend -m命令:
# 格式:git commit --amend -m "新的提交信息"
git commit --amend -m "添加git add四种用法的笔记"
这个命令的作用是修改最近一次提交的备注信息,执行后,原来的 commit id 会被新的 id 取代 ------ 相当于 "覆盖" 了上一次的提交记录。
注意 :这个命令只能修改最近一次的提交信息,如果已经提交了多次,想修改更早的提交信息,就需要更复杂的操作(新手暂时不用学)。
问题 2:commit 后发现漏改了内容,怎么办?
有两种简单的解决办法:
- 方法一:修改后再次 commit (推荐新手用)继续修改文件,然后执行
git add 文件名和git commit -m "补充修改xxx"------ 这是最安全、最简单的方式,不会破坏历史记录; - 方法二:修改后用 --amend 合并到上一次提交 修改文件后,执行
git add 文件名,然后执行git commit --amend -m "原来的备注信息+补充的内容"------ 这个方法会把新的修改合并到最近一次提交里,适合 "漏改的内容很少,不想多一条提交记录" 的场景。
问题 3:commit 后发现改错题了,想撤回这次提交,怎么办?
用git reset命令,这个命令我们后面在 "常见问题汇总" 里会详细讲,可以先记这个简单的用法:
# 回到上一次提交的状态,保留工作区的修改
git reset --soft HEAD^
执行后,你会发现这次提交的修改又回到了暂存区,你可以修改后重新提交。
3. 工作区、暂存区、版本库的状态转换
学到这里,你已经掌握了git add和git commit的基本用法,现在我们用一个表格 + 流程图,把 "未跟踪→已暂存→已提交" 的状态转换讲透 ------ 这是 Git 本地操作的核心逻辑,一定要理解!
3.1 三种核心状态的定义
先定核心逻辑:三种状态 = 文件在 Git 中的 "流转阶段"
Git 的三种核心状态,本质是文件从「刚创建」到「永久存档」的三个阶段,就像作家写小说的草稿:
- 刚拿到空白草稿纸(未跟踪)→ 写完后放进待打印稿夹(已暂存)→ 整理好存入存档柜(已提交)每个阶段都有明确的 "身份标识""操作规则" 和 "安全等级",下面逐个拆解。
状态 1:未跟踪(Untracked)------ 作家的 "空白 / 零散草稿纸"
1. 通俗类比
你是作家,刚买了一叠空白草稿纸,摊在书桌上还没写一个字;或者你写了几行零散的剧情,但只是随手放在书桌角落,既没打算整理,也没放进 "待打印稿夹"------ 这张草稿纸对 "存档流程" 来说是「陌生的」:存档柜里没有它的任何记录,你甚至可以随手把它揉掉,完全不影响已存档的内容。
2. 技术定义
Git 能检测到这个文件存在于「工作区」(你的仓库文件夹),但从未对它执行过git add操作,Git 没有任何关于这个文件的版本记录,也不会主动跟踪它的修改、删除等变化 ------ 简单说:Git "认识" 这个文件,但 "不管" 它。
3. 如何快速识别这个状态(看git status输出)
在仓库目录执行git status,如果文件是「未跟踪」状态,会看到这样的输出(重点看标注部分):
On branch master
Untracked files: # 明确标注"未跟踪文件"
(use "git add <file>..." to include in what will be committed) # 提示:用git add可纳入存档计划
第1章草稿.txt # 你的未跟踪文件
人物设定.md
nothing added to commit but untracked files present (use "git add" to track)
# 总结:没有内容待提交,但有未跟踪文件,需git add才能被Git跟踪
4. 这个状态是怎么来的?(仅 2 种情况)
- 情况 1:新建文件 ------ 你在仓库文件夹里新建了文件(比如
touch 第1章草稿.txt、新建 Excel 表格),且从未执行git add 文件名; - 情况 2:撤回暂存后变回 ------ 你对未跟踪文件执行了
git add(变成已暂存),又用git restore --staged 文件名撤回暂存,文件会回到未跟踪状态。
5. 针对这个状态的核心操作(作家 ↔ Git 一一对应)
| 作家的操作 | Git 对应的命令 | 操作结果(状态变化) |
|---|---|---|
| 觉得草稿有用,放进待打印稿夹 | git add 第1章草稿.txt |
未跟踪 → 已暂存(纳入存档计划) |
| 觉得草稿没用,揉掉扔掉 | rm 第1章草稿.txt |
文件从工作区消失,Git 不再提示 |
| 不想管这张草稿,但又不想被提示 | 新建.gitignore文件,写入第1章草稿.txt |
Git 会忽略这个文件,执行git status不再显示它 |
| 不小心删了草稿,想找回来 | 无任何 Git 命令可用 | 未跟踪文件的删除无法通过 Git 恢复(Git 没记录) |
6. 坑
- 坑 1:以为 "新建文件 = Git 自动管理"------ 错!未跟踪状态下,Git 完全不保护这个文件,误删 / 覆盖后找不回来;
- 坑 2:用
git add .时,把临时文件(比如temp.log、测试数据.xlsx)也变成已暂存 ------ 后期提交时会把无关文件纳入版本库,建议用**.gitignore提前忽略**; - 坑 3:嫌
git status提示未跟踪文件烦,直接删文件 ------ 先确认文件是否有用,再操作。
状态 2:已暂存(Staged)------ 作家的 "待打印稿夹里的草稿"
1. 通俗类比
你在书桌上写完《第 1 章》的完整草稿,逐字检查后觉得没问题,把这张草稿纸折好、写上 "第 1 章 - 待存档",放进专门的「待打印稿夹」------ 这张草稿现在有了 "身份":
- 它属于 "待存档" 范围,不是随便扔在书桌的零散内容;
- 你可以往稿夹里补内容(比如写完第 2 章,也放进去);
- 如果你发现稿夹里的草稿有笔误,能抽出来改,改完再放回去;
- 稿夹里的内容还没真正 "存档",只是做好了存档准备。
2. 技术定义
文件已经通过git add操作被加入「暂存区」(Git 的中间缓冲区),Git 会为这个文件生成当前版本的 "快照"(记录文件的完整内容),并标记为「待提交」------ 此时文件的修改已经被 Git 记录,等待你执行git commit存入版本库。
3. 如何快速识别这个状态(看git status输出)
执行git status,已暂存文件会出现在「Changes to be committed」栏目下:
On branch master
Changes to be committed: # 明确标注"待提交的变更"
(use "git restore --staged <file>..." to unstage) # 提示:用这个命令可撤回暂存
new file: 第1章草稿.txt # 新建文件的已暂存
modified: 人物设定.md # 已跟踪文件修改后的已暂存
new file::表示这个文件是新建的,从未提交过;modified::表示这个文件是已提交过的(已跟踪),修改后执行git add变成已暂存。
4. 这个状态是怎么来的?(仅 2 种情况)
- 情况 1:未跟踪文件执行
git add------ 比如对第1章草稿.txt执行git add 第1章草稿.txt,从 "未跟踪"→"已暂存"; - 情况 2:已修改的已跟踪文件执行
git add------ 比如你修改了已提交过的人物设定.md,执行git add 人物设定.md,从 "已修改"→"已暂存"。
5. 针对这个状态的核心操作(作家 ↔ Git 一一对应)
| 作家的操作 | Git 对应的命令 | 操作结果(状态变化) |
|---|---|---|
| 把稿夹里的草稿整理好,存入存档柜 | git commit -m "第1章:完成初遇剧情" |
已暂存 → 已提交(永久存档) |
| 发现稿夹里的草稿有错,抽出来(保留草稿) | git restore --staged 第1章草稿.txt(旧版 Git 用git reset HEAD 第1章草稿.txt) |
已暂存 → 未跟踪 / 已修改(回到工作区) |
| 对稿夹里的草稿做了修改,重新放进去 | 1. 修改文件;2. 再次执行git add 第1章草稿.txt |
暂存区同步最新修改(覆盖旧快照) |
| 把稿夹里所有草稿都抽出来 | git restore --staged .(.代表所有文件) |
所有已暂存文件回到工作区 |
6. 关键补充:已暂存≠"最新版"
这是新手最容易踩的坑!比如:
- 你写完《第 1 章》,执行
git add 第1章草稿.txt(已暂存); - 你又觉得剧情不好,修改了
第1章草稿.txt(加了一段对话); - 此时工作区的文件是 "新版",但暂存区的文件还是 "旧版";
- 如果你直接
git commit,只会提交 "旧版",新版修改仍留在工作区; - 正确操作:修改后再次执行
git add 第1章草稿.txt,让暂存区同步最新版本。
7. 坑
- 坑 1:修改已暂存文件后,直接
git commit------ 只会提交旧版本,白改了; - 坑 2:以为 "暂存 = 存档"------ 暂存区只是 "待存档",没执行
git commit就不算永久保存; - 坑 3:撤回暂存时用错命令 ------ 记住
git restore --staged 文件名即可,别乱试其他命令。
状态 3:已提交(Committed)------ 作家的 "存档柜里的终稿"
1. 通俗类比
你把待打印稿夹里的《第 1 章》草稿拿出来,打印成正式文档,在封面写上 "第 1 章终稿 - 2026 年 1 月 15 日",然后放进带锁的「存档柜」------ 这份文档现在有了 "正式身份":
- 它被永久保存,不会随便丢失;
- 存档柜里有它的记录,你能查到 "2026.1.15 存了第 1 章终稿";
- 后续想改第 1 章,你会写新的草稿(工作区),再走 "暂存→存档" 流程,不会直接改存档柜里的终稿;
- 哪怕你改坏了新草稿,也能从存档柜里取出原来的终稿。
2. 技术定义(精准不晦涩)
你执行git commit后,暂存区的文件快照被存入「版本库」(Git 的核心存储区),Git 会生成一个唯一的提交 ID(commit ID)(40 位哈希值),记录这次提交的作者、时间、备注和文件版本 ------ 此时文件的这个版本被永久记录,可追溯、可恢复,然后我们也要注意,即使我们把文件commit了,暂存区虽然是没有那个文件了,但是那个文件还是处于被追踪的状态哦!!!
3. 如何快速识别这个状态(看git status+git log)
(1)git status识别(工作区干净)
执行git status,会看到 "工作区干净" 的提示,说明所有修改都已提交:
On branch master
nothing to commit, working tree clean # 工作区干净,无未提交修改
(2)git log验证(查看提交记录)
执行git log --oneline(精简输出),能看到刚提交的记录:
a87b9c6 第1章:完成初遇剧情 # commit ID(前7位)+ 提交备注
4. 这个状态是怎么来的?(仅 1 种情况)
对「已暂存」的文件执行git commit -m "备注信息"------ 这是唯一能让文件进入 "已提交" 状态的操作,也是 Git 生成版本快照的核心步骤。
5. 针对这个状态的核心操作(作家 ↔ Git 一一对应)
| 作家的操作 | Git 对应的命令 | 操作结果(状态变化) |
|---|---|---|
| 想修改存档柜里的终稿,写新草稿 | 编辑第1章草稿.txt(加新剧情) |
已提交 → 已修改(工作区文件变更) |
| 查看存档柜里的所有终稿记录 | git log --oneline |
列出所有提交记录(从新到旧) |
| 发现终稿的封面备注写错,改备注 | git commit --amend -m "第1章:完成初遇+伏笔剧情" |
修改最近一次提交的备注,生成新 commit ID |
| 想撤回刚存入存档柜的终稿(保留草稿) | git reset --soft HEAD^ |
已提交 → 已暂存(变更回到暂存区) |
| 想彻底删除刚存入的终稿(慎用) | git reset --hard HEAD^ |
已提交的版本被删除,工作区 / 暂存区也恢复到上一版本(修改丢失) |
| 想找回存档柜里上周的旧版终稿 | 1. git log --oneline找旧版 commit ID;2. git checkout <ID> -- 第1章草稿.txt |
旧版本文件恢复到工作区(变为已修改状态) |
6. 坑
- 坑 1:提交备注写 "改了点东西""123"------ 后期查
git log时,完全不知道这次提交改了啥,备注要写清楚(比如 "第 1 章:新增男主出场剧情"); - 坑 2:滥用
git reset --hard HEAD^------ 这个命令会彻底删除最新提交,且丢弃所有未提交修改,新手非必要别用; - 坑 3:以为 "已提交就不能改"------ 可以改,但要走规范流程(比如新增提交,而非删旧版本)。
总结(核心要点回顾)
- 未跟踪:Git "认识但不管" ------ 新建文件默认状态,无版本保护,删了找不回,需
git add纳入跟踪; - 已暂存:Git "管了但没存档" ------ 进了暂存区,有快照记录,需
git commit才会永久保存,修改后要重新git add同步; - 已提交:Git "永久保护"------ 生成唯一 commit ID,可追溯、可恢复,备注一定要清晰,别乱删版本。
| 状态名称 | 通俗解释 | 对应的文件状态 |
|---|---|---|
| 未跟踪(Untracked) | Git 知道这个文件存在,但没开始管理它 | 新建的文件,从未执行过 git add |
| 已暂存(Staged) | 文件已经进入暂存区,等待被 commit 到版本库 | 执行过 git add,但没执行 git commit |
| 已提交(Committed) | 文件的修改已经被存入版本库,生成了快照 | 执行过 git commit,修改被永久保存 |
3.2 状态转换的触发操作)
我们用 "箭头 + 命令" 的形式,把转换关系列出来:
- 未跟踪 → 已暂存 :执行
git add 文件名(新建的文件,只有执行 add,才会被 Git 跟踪) - 已暂存 → 已提交 :执行
git commit -m "备注"(暂存区的内容,只有执行 commit,才会变成版本快照) - 已提交 → 已暂存 :修改已提交的文件后,执行
git add 文件名(对已提交的文件做修改,修改后的内容会回到 "未暂存" 状态,执行 add 后变回已暂存) - 已暂存 → 未跟踪 :执行
git reset HEAD 文件名(把文件从暂存区撤回到工作区,变回未跟踪状态) - 已提交 → 工作区(回退) :执行
git reset --soft HEAD^(把最近一次提交的修改撤回到暂存区,保留工作区内容)
3.3 典型操作流程演示:三次提交的完整闭环
前置条件 :已经执行git init创建了my-first-git-repo仓库,当前在仓库目录下。
| 步骤 | 操作内容 | 执行命令 | 状态变化 |
|---|---|---|---|
| 1 | 新建notes.md,写入第一版内容 |
touch notes.md 用编辑器写入:# Git学习笔记 |
notes.md:未跟踪 |
| 2 | 将notes.md加入暂存区 |
git add notes.md |
notes.md:未跟踪 → 已暂存 |
| 3 | 第一次提交,备注 "初始化笔记" | git commit -m "初始化Git学习笔记" |
notes.md:已暂存 → 已提交 |
| 4 | 修改notes.md,添加git add的用法 |
用编辑器写入:## git add的四种用法 |
notes.md:已提交 → 未暂存(修改后) |
| 5 | 将修改后的notes.md加入暂存区 |
git add notes.md |
notes.md:未暂存 → 已暂存 |
| 6 | 第二次提交,备注 "添加 git add 用法" | git commit -m "添加git add的四种用法说明" |
notes.md:已暂存 → 已提交 |
| 7 | 新建todo.md,写入待办事项 |
touch todo.md 写入:1. 学习git status命令 |
todo.md:未跟踪;notes.md:已提交 |
| 8 | 将notes.md和todo.md加入暂存区 |
git add . |
todo.md:未跟踪 → 已暂存;notes.md:已提交 → 已暂存(无修改,状态不变) |
| 9 | 第三次提交,备注 "新增 todo.md 待办" | git commit -m "新增todo.md,记录Git学习待办" |
todo.md:已暂存 → 已提交 |
执行完这 9 步,你的仓库里就有了 3 条提交记录,notes.md和todo.md都被 Git 妥善管理起来了 ------ 这就是最标准的 Git 本地操作流程!
Git 的 "追踪(Tracked)" 到底追踪什么?
很多人误以为 "追踪" 是 "追踪文件本身",但其实 Git 的核心是追踪 "变更",而非文件------ 这是理解 Git 的关键认知。
1. 先定义:什么是 "追踪(Tracked)"?
Git 中的 "追踪" 是指:Git 开始为某个文件建立 "变更历史记录",持续监控该文件的所有内容变化和状态变化。
- 「已追踪文件(Tracked)」:执行过
git add的文件(哪怕还没 commit),Git 会记录它的每一次修改、删除、移动; - 「未追踪文件(Untracked)」:从未执行过
git add的文件,Git 只检测到它存在,但不记录任何变化(删了也找不回)。
只要我们把一个文件git add了,那么这个文件就是会被git跟踪,你想要终止对它的跟踪的话就只能去使用git rm --cached指令!!!
2. 追踪的核心对象:不是 "文件",而是 "文件的变更"
通俗类比(作家视角)
你不会盯着 "草稿纸" 这个本子看,而是盯着 "草稿纸上的内容变化":
- 新增了一行字、删了一段剧情、改了一个名字 ------ 这些才是你要记录的;
- 哪怕换了一本新草稿纸写《第 1 章》(文件重命名),你追踪的还是 "《第 1 章》的内容变化",而非 "草稿纸本身"。
技术本质
Git 追踪的是文件的 "内容快照"(Blob 对象):
- 第一次
git add文件时,Git 会为文件当前的内容生成一个唯一的 Blob 对象(用 SHA-1 哈希标识),并记录这个快照; - 后续修改文件后,Git 会对比 "当前内容" 和 "上一次快照" 的差异,标记出 "哪些行加了、哪些行删了、哪些行改了"------ 这就是 "追踪变更" 的过程;
- 执行
git commit时,Git 会把暂存区的所有快照打包成 "提交对象",永久保存这些变更。
3. Git 具体追踪什么内容?(粒度 + 范围)
| 追踪的内容 | 通俗解释(作家视角) | 技术说明 |
|---|---|---|
| 文件内容的行级差异 | 草稿纸上加了一行、删了一行、改了一个词 | Git 默认最小追踪粒度是 "行",而非 "字符" |
| 文件的状态变化 | 草稿纸被删了、换了个本子写(重命名) | 追踪文件的 "已修改 / 已删除 / 已移动" 状态 |
| 文件的版本历史 | 存档柜里《第 1 章》的 v1、v2、v3 版本 | 记录每一次提交的快照,形成版本链 |
4. Git 不追踪什么?
- 不追踪文件的元数据:比如文件的创建时间、修改时间、文件所有者;
- 不追踪文件的权限变化 :比如文件从 "只读" 改成 "可写"(需加
git diff --stat才能看到); - 不追踪 "空文件夹":Git 只追踪文件,空文件夹会被忽略(需新建
.gitkeep文件才能纳入管理); - 不追踪 "未跟踪文件" 的变化:未执行
git add的文件,Git 不记录任何修改 / 删除。
5. 追踪的生命周期(从 "未追踪" 到 "停止追踪")

实操例子:追踪的完整流程
# 1. 新建文件:未追踪
touch 第1章.txt
git status # 显示"Untracked files: 第1章.txt"
# 2. git add:开始追踪(进入暂存区,已追踪)
git add 第1章.txt
git status # 显示"Changes to be committed: 第1章.txt"
# 3. git commit:追踪的变更存入版本库
git commit -m "第1章:初始化"
# 4. 修改文件:Git追踪到"已修改"
echo "男主走在街头" >> 第1章.txt
git status # 显示"Changes not staged for commit: modified: 第1章.txt"
# 5. 停止追踪(保留文件)
git rm --cached 第1章.txt
git status # 显示"Untracked files: 第1章.txt"(停止追踪,但文件还在)
查看工作区、缓存区、版本库中的不同的部分(git diff 文件):
git diff = 作家的 "草稿对比工具"
git diff的本质是对比 Git 不同区域(工作区 / 暂存区 / 版本库)中文件的内容差异,就像作家用 "对比工具" 看两份草稿的区别:
- 对比 "书桌上的草稿"(工作区)和 "待打印稿夹里的草稿"(暂存区);
- 对比 "待打印稿夹里的草稿"(暂存区)和 "存档柜里的终稿"(版本库);
- 对比 "存档柜里的 v1 终稿" 和 "v2 终稿";所有对比结果都会清晰标出 "哪里加了内容、哪里删了内容、哪几行改了"------ 这是排查修改、确认变更的核心指令,也是 Git "追踪变更" 的直观体现。
一、前置知识:先明确 "对比的对象"
git diff的所有用法,本质是对比以下三个区域中文件的 "内容快照",先复习这三个核心区域:
| 区域 | 作家类比 | 核心特征 |
|---|---|---|
| 工作区 | 书桌(草稿纸) | 正在编辑的内容,未暂存 |
| 暂存区 | 待打印稿夹 | 已执行git add,待提交 |
| 版本库 | 存档柜(终稿) | 已执行git commit,永久保存 |
关键提醒:
git diff默认只对比文件内容的行级差异(比如加了一行、删了一行、改了一个词),不会对比文件名、文件权限的变化(需加参数,后面会讲)。
二、git diff的核心用法
下面按 "对比对象" 拆分git diff的用法
用法 1:基础款(无参数)------ 对比「工作区 vs 暂存区」
这是git diff最常用的默认用法,也是新手最先接触的场景。
1. 通俗类比(作家视角)
你在书桌上修改了《第 1 章.txt》(比如加了一段男主的心理描写),但还没把修改后的草稿放进 "待打印稿夹"(没执行git add)------ 用git diff就是对比 "书桌上的新版草稿" 和 "稿夹里的旧草稿",看具体改了哪几行。
2. 技术定义
对比工作区中已跟踪文件 的当前内容,和暂存区中该文件的快照 ------ 只显示 "工作区改了但没暂存" 的差异,未跟踪文件(从没git add过的)不会显示。
3. 实操步骤 + 示例
步骤 1:准备测试文件(模拟作家写小说)
# 1. 新建文件并提交到版本库(存档柜)
echo "第1章:初遇
男主走在街头,看到了女主。" > 第1章.txt
git add 第1章.txt
git commit -m "第1章v1:完成初遇剧情"
# 2. 修改工作区文件(书桌加内容):新增男主心理描写
echo "第1章:初遇
男主走在街头,心里想着:今天要见她吗?
男主走在街头,看到了女主。" > 第1章.txt
# 此时:工作区改了,暂存区还是v1的快照(没执行git add)
步骤 2:执行git diff(无参数)
git diff
步骤 3:输出示例(带颜色,这里用文字标注)
diff --git a/第1章.txt b/第1章.txt
index 8a7b9c..d4e5f6 100644
--- a/第1章.txt
+++ b/第1章.txt
@@ -1,3 +1,4 @@
第1章:初遇
+男主走在街头,心里想着:今天要见她吗?
男主走在街头,看到了女主。
4. 输出内容逐行解读
这是git diff输出的核心,拆解后再也不会看不懂:
| 输出行 | 含义解读(通俗版) |
|---|---|
diff --git a/第1章.txt b/第1章.txt |
声明对比的两个文件:a 代表 "原始版本(暂存区)",b 代表 "新版本(工作区)" |
index 8a7b9c..d4e5f6 100644 |
两个版本的文件快照哈希值(不用管),100644 是文件权限(普通文件默认值) |
--- a/第1章.txt |
--- 代表 "原始文件(暂存区的旧版本)" |
+++ b/第1章.txt |
+++ 代表 "目标文件(工作区的新版本)" |
@@ -1,3 +1,4 @@ |
差异的行号范围:- -1,3:原始文件从第 1 行开始,共 3 行;- +1,4:目标文件从第 1 行开始,共 4 行 |
第1章:初遇 |
无 +/-:这一行在两个版本中完全一样,只是作为上下文展示 |
+男主走在街头... |
+ 号:这一行是新版本新增的(工作区有,暂存区没有) |
男主走在街头... |
无 +/-:这一行无变化 |
5. 核心特征
- 只显示 "工作区改了但没暂存" 的差异;
- 如果工作区和暂存区完全一致,执行
git diff会无任何输出(代表没差异); - 未跟踪文件(从没
git add过的)不会出现在输出里。
用法 2:git diff --staged(或--cached)------ 对比「暂存区 vs 版本库(最新提交)」
这是查看 "即将提交的变更" 的核心用法,容易和无参数版混淆,必须分清。
1. 通俗类比(作家视角)
你把书桌上修改后的《第 1 章.txt》放进了 "待打印稿夹"(执行git add),现在想对比 "稿夹里的新版草稿" 和 "存档柜里的 v1 终稿",看这次要存档的内容具体改了啥。
2. 技术定义
对比暂存区中文件的快照 ,和版本库中该文件的最新提交版本------ 只显示 "已暂存但未提交" 的差异,工作区的未暂存修改不会显示。
3. 实操步骤 + 示例
步骤 1:延续上面的例子,先把修改暂存
# 把工作区的修改放进暂存区(稿夹)
git add 第1章.txt
# 此时:暂存区是新版,版本库是v1,工作区和暂存区一致
步骤 2:执行git diff --staged(--cached 和 --staged 等价)
git diff --staged
# 或:git diff --cached(旧版本Git常用)
步骤 3:输出示例
diff --git a/第1章.txt b/第1章.txt
index 8a7b9c..d4e5f6 100644
--- a/第1章.txt
+++ b/第1章.txt
@@ -1,3 +1,4 @@
第1章:初遇
+男主走在街头,心里想着:今天要见她吗?
男主走在街头,看到了女主。
4. 核心特征
- 只显示 "已暂存、等待提交" 的差异;
- 如果暂存区和版本库一致(比如刚提交完),执行该命令会无输出;
- 这是提交前必做的操作:确认要提交的内容是否正确,避免提交错漏。
用法 3:git diff <commit ID> ------ 对比「工作区 vs 版本库指定版本」
想对比当前工作区的文件,和存档柜里某一版旧终稿的差异,比如看 "现在写的草稿" 和 "v1 终稿" 差了啥。
1. 通俗类比(作家视角)
你想看看书桌上的《第 1 章.txt》草稿,和存档柜里上周存的 "v1 终稿" 相比,总共改了哪些内容(不管有没有暂存)。
2. 技术定义
对比工作区中文件的当前内容 ,和版本库中指定 commit ID 对应版本的文件快照------ 会显示所有差异(包括已暂存和未暂存的)。
3. 实操步骤 + 示例
步骤 1:先查版本库的 commit ID(找到 v1 的 ID)
git log --oneline
# 输出:a87b9c 第1章v1:完成初遇剧情(假设这是v1的commit ID)
步骤 2:修改工作区(新增内容,不暂存)
# 再给第1章加一行内容(未暂存)
echo "女主回头,对男主笑了笑。" >> 第1章.txt
步骤 3:执行git diff <v1的commit ID>
git diff a87b9c
步骤 4:输出示例(显示所有差异)
diff --git a/第1章.txt b/第1章.txt
index 8a7b9c..e8f9g0 100644
--- a/第1章.txt
+++ b/第1章.txt
@@ -1,3 +1,5 @@
第1章:初遇
+男主走在街头,心里想着:今天要见她吗?
男主走在街头,看到了女主。
+女主回头,对男主笑了笑。
4. 核心特征
- 对比的是 "工作区所有内容" 和 "版本库指定版本",不管是否暂存;
- 如果想对比 "工作区 vs 上一个提交版本",可以简写为
git diff HEAD^(HEAD^ 代表上一个版本)。
用法 4:git diff <版本1> <版本2> ------ 对比「版本库两个版本」
想查看存档柜里两个旧版本的差异,比如对比 "v1 终稿" 和 "v2 终稿" 改了啥,这是回溯历史变更的核心用法。
1. 通俗类比(作家视角)
你想看看存档柜里 "v1 终稿" 和 "v2 终稿" 的区别,确认第 2 版比第 1 版多了哪些剧情。
2. 技术定义
对比版本库中两个指定 commit ID 对应的文件快照------ 和工作区、暂存区无关,只看版本库的历史差异。
3. 实操步骤 + 示例
步骤 1:先提交 v2 版本(模拟有两个版本)
# 把之前的修改提交为v2
git commit -m "第1章v2:新增男主心理描写和女主回应"
# 查两个版本的commit ID
git log --oneline
# 输出:
# f87c9d 第1章v2:新增男主心理描写和女主回应(v2 ID)
# a87b9c 第1章v1:完成初遇剧情(v1 ID)
步骤 2:执行git diff <v1 ID> <v2 ID>
git diff a87b9c f87c9d
步骤 3:输出示例
diff --git a/第1章.txt b/第1章.txt
index 8a7b9c..e8f9g0 100644
--- a/第1章.txt
+++ b/第1章.txt
@@ -1,3 +1,5 @@
第1章:初遇
+男主走在街头,心里想着:今天要见她吗?
男主走在街头,看到了女主。
+女主回头,对男主笑了笑。
4. 简化写法
- 对比上一个版本和当前版本:
git diff HEAD^ HEAD; - 对比最近两次提交:
git diff HEAD~2 HEAD~1(HEAD~2 代表倒数第二个版本)。
三、git diff的常用简化参数
默认的git diff输出比较详细,可以用以下参数简化输出,聚焦核心信息:
| 参数 | 作用(通俗版) | 示例命令 | 示例输出 | |
|---|---|---|---|---|
--name-only |
只显示 "有差异的文件名",不显示具体改了啥 | git diff --name-only |
第1章.txt |
|
--name-status |
显示文件名 + 差异类型(A = 新增,M = 修改,D = 删除) | git diff --name-status |
M 第1章.txt(M = 修改) |
|
--oneline |
把差异内容压缩成一行(适合快速看概要) | git diff --oneline |
`8a7b9c..d4e5f6 第 1 章.txt | 1 +` |
--color-words |
按 "单词" 显示差异(不是按行),适合改了个别词的场景 | git diff --color-words |
只标红 / 绿修改的单词(比如 "她"→"女主") | |
--no-index |
对比两个非 Git 仓库的文件(比如对比桌面的两个草稿文件) | git diff --no-index 草稿1.txt 草稿2.txt |
和普通 diff 输出一致 |
四、常见场景的实操指南
场景 1:提交前确认 "要提交的内容"(必做!)
# 步骤1:查看工作区未暂存的修改(确认有没有漏改)
git diff
# 步骤2:暂存所有修改
git add .
# 步骤3:查看即将提交的修改(确认提交内容正确)
git diff --staged
# 步骤4:确认无误后提交
git commit -m "第1章v2:新增男主心理描写"
场景 2:回溯历史,看某版本改了啥
# 步骤1:查所有版本的commit ID
git log --oneline
# 步骤2:对比v1和v2的差异(只看文件名)
git diff --name-only a87b9c f87c9d
# 步骤3:看具体改了哪几行
git diff a87b9c f87c9d
场景 3:对比两个不同文件的内容(非 Git 仓库也能用)
# 对比桌面的《第1章-草稿1.txt》和《第1章-草稿2.txt》
git diff --no-index ~/Desktop/第1章-草稿1.txt ~/Desktop/第1章-草稿2.txt
五、坑
坑 1:以为git diff能显示未跟踪文件
- 后果:新建了文件(未跟踪),执行
git diff看不到,以为没差异; - 原因:
git diff默认只对比已跟踪文件 (至少git add过一次的); - 避坑:未跟踪文件想对比,用
git diff --no-index 新文件 空文件(或直接用cat看内容)。
坑 2:混淆git diff和git diff --staged
- 后果:以为
git diff能看到 "要提交的内容",结果漏看已暂存的修改; - 避坑:记住口诀 ------
git diff:工作区 vs 暂存区(没暂存的修改);git diff --staged:暂存区 vs 版本库(要提交的修改)。
坑 3:看不懂输出里的 "-" 号,以为是 "删除了文件"
- 后果:看到
--- a/第1章.txt就以为文件被删了; - 原因:
---只是代表 "原始版本文件",不是文件被删; - 避坑:只有输出里出现
deleted file mode 100644,才代表文件被删除。
坑 4:对比版本时输错 commit ID
- 后果:对比结果不对,甚至报错;
- 避坑:执行前先复制
git log --oneline里的 commit ID,别手动输。
总结(核心要点回顾)
git diff的核心是 "对比差异" :不同参数对应不同对比对象,核心口诀:- 无参数:工作区 vs 暂存区(看未暂存的修改);
--staged/--cached:暂存区 vs 版本库(看要提交的修改);git diff <ID>:工作区 vs 指定版本(看所有修改);git diff <ID1> <ID2>:版本库两个版本对比(看历史变更);
- 输出解读关键:+ 号 = 新增行,- 号 = 删除行,@@行号 @@= 差异位置;
- 提交前必做两步 :先
git diff查未暂存修改,再git diff --staged查待提交修改,避免提交错漏。
第三大核心操作:git status & git log(查看工作区状态与提交历史)
学会了git init、git add和git commit,你已经能完成基本的版本管理了 ------ 但你还需要两个命令,来 "监控" 你的仓库:
git status:查看当前工作区和暂存区的状态------ 比如哪些文件被修改了、哪些文件在暂存区、哪些文件是未跟踪的;git log:查看所有的提交历史记录------ 比如你提交了多少次、每次提交的时间和内容、commit id 是什么。
这两个命令是 "诊断工具"------ 不管你在操作中遇到什么问题,先执行git status和git log,就能快速理清当前仓库的情况。
1. git status:你的仓库 "状态诊断仪"
git status的核心作用是:告诉你当前工作区和暂存区的 "差异"------ 它会用清晰的文字,告诉你哪些文件需要git add,哪些文件需要git commit,哪些文件是未跟踪的。
一定要养成一个习惯:每次执行git add或git commit之前,先执行git status,查看当前状态------ 这样可以避免很多误操作。
1.1 git status 的基本用法与输出解读
git status的用法非常简单,直接在仓库目录下执行:
git status
它的输出会根据仓库的状态不同而变化,我们分三种常见场景来解读 ------ 这是新手最常遇到的情况。
场景 1:工作区 "干净"------ 没有任何修改(最理想的状态)
如果你刚执行完git commit,工作区没有任何修改,执行git status会输出:
On branch master
nothing to commit, working tree clean
翻译成人话就是:"当前在 master 分支,工作区很干净,没有需要提交的修改"------ 这说明你的所有修改都已经被存入版本库了。
场景 2:有 "未跟踪文件"------ 新建了文件但没执行 git add
如果你新建了todo.md,但没执行git add todo.md,执行git status会输出:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
todo.md
nothing added to commit but untracked files present (use "git add" to track)
这段输出的每一句话都是 "提示":
Untracked files::下面列出的是未跟踪文件;(use "git add <file>..." to include in what will be committed):告诉你解决方法 ------ 执行git add 文件名,就能把文件纳入提交范围;nothing added to commit but untracked files present:总结 ------ 没有内容被添加到暂存区,但存在未跟踪文件。
场景 3:有 "已修改但未暂存" 的文件 ------ 修改了已提交的文件但没执行 git add
如果你修改了已提交的notes.md,但没执行git add notes.md,执行git status会输出:
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: notes.md
no changes added to commit (use "git add" and/or "git commit -a")
解读:
Changes not staged for commit::下面列出的是 "修改了但没加入暂存区" 的文件;- 第一行提示:
use "git add <file>..."------ 执行git add可以把修改加入暂存区; - 第二行提示:
use "git restore <file>..."------ 执行git restore 文件名可以丢弃工作区的修改,回到上一次提交的状态(新手慎用!会丢失未提交的修改); no changes added to commit:总结 ------ 没有内容被添加到暂存区,无法执行 commit。
场景 4:有 "已暂存但未提交" 的文件 ------ 执行了 git add 但没执行 git commit
如果你执行了git add notes.md,但没执行git commit,执行git status会输出:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: notes.md
解读:
Changes to be committed::下面列出的是 "已经加入暂存区,等待提交" 的文件;- 提示:
use "git restore --staged <file>..." to unstage------ 执行这个命令可以把文件从暂存区撤回到工作区(和git reset HEAD 文件名效果一样)。
1.2 git status 的简化输出:-s 参数
默认的git status输出很详细,但有时候你只想快速看一下状态,就可以用-s参数(short 的缩写),输出会变得非常简洁。比如:
git status -s
输出示例:
?? todo.md
M notes.md
解读这些符号的含义(新手记两个最常用的):
??:表示文件是未跟踪状态 (比如todo.md);M:表示文件被修改了但没加入暂存区(注意前面有个空格);M:表示文件被修改了且已经加入暂存区(注意后面有个空格)。
-s参数的好处是:一眼就能看出所有文件的状态,适合熟练后快速查看。
1.3 git status 的常见误区
误区 1:git status 会显示所有的历史修改?
错!git status只关心当前状态------ 它只会告诉你 "现在工作区和暂存区的差异",不会显示过去的提交记录。想看历史记录,要用git log。
误区 2:为什么我修改了文件,但 git status 没反应?
有两种可能:
- 文件是未跟踪状态,你修改了但没执行 git add ------ 这种情况 git status 会显示
?? 文件名,不是没反应; - 你修改的是二进制文件(比如图片、视频) ------Git 只能跟踪文本文件的修改,二进制文件修改后,git status 只会显示
modified: 文件名,不会显示具体改了什么。
2. git log:你的仓库 "历史日记本"
如果说git status是 "看现在",那git log就是 **"看过去"**------ 它会列出仓库所有的提交记录,包括每次提交的commit id、作者、时间、备注信息,注意,只有在commit之后,git log才会记录下来,而你要是使用git add的话,那么是不会记录的哦。
git log是版本回退的 "关键工具"------ 想要回退到某个历史版本,必须先用git log找到对应的commit id。
2.1 git log 的基本用法与输出解读
直接执行git log,会输出详细的提交历史:
git log
输出示例(对应我们之前的三次提交):
commit d8e7f6a1234567890abcdef1234567890abcdef
Author: 小明学Git <xiaoming@git.com>
Date: Wed Oct 11 15:30:00 2023 +0800
新增todo.md,记录Git学习待办
commit b5c4d3e9876543210fedcba9876543210fedcba
Author: 小明学Git <xiaoming@git.com>
Date: Wed Oct 11 15:20:00 2023 +0800
添加git add的四种用法说明
commit a1b2c3d0123456789abcdef0123456789abcdef
Author: 小明学Git <xiaoming@git.com>
Date: Wed Oct 11 15:10:00 2023 +0800
初始化Git学习笔记
我们逐行解读一条提交记录的结构:
commit d8e7f6a...:这是本次提交的唯一 ID(commit id) ------ 这是一个 40 位的字符串,后面回退版本时,只需要写前 7 位即可(比如d8e7f6a);Author: 小明学Git <xiaoming@git.com>:提交者的用户名和邮箱(就是你之前用git config配置的);Date: Wed Oct 11 15:30:00 2023 +0800:提交的时间;- 空行下面的一行:就是你用
-m参数写的备注信息。
注意 :git log的输出顺序是从最近到最远------ 最新的提交在最上面,最早的提交在最下面。
2.2 git log 的常用参数
默认的git log输出太详细,如果提交次数多了,翻起来很麻烦 ------ 所以 Git 提供了很多参数,让你可以自定义输出格式。
参数 1:--oneline(最常用)------ 一行显示一条提交记录
这个参数会把每条提交记录压缩成一行,只显示commit id的前 7 位和备注信息,非常适合快速查看历史:
git log --oneline
输出示例:
d8e7f6a 新增todo.md,记录Git学习待办
b5c4d3e 添加git add的四种用法说明
a1b2c3d 初始化Git学习笔记
这个输出简洁明了,是新手最常用的git log用法。
参数 2:--graph(可视化分支)------ 显示分支合并图
这个参数会在提交记录前添加*号,形成一个简单的分支图 ------ 虽然我们这篇文章不讲分支,但这个参数很实用,可以先了解:
git log --oneline --graph
输出示例:
* d8e7f6a 新增todo.md,记录Git学习待办
* b5c4d3e 添加git add的四种用法说明
* a1b2c3d 初始化Git学习笔记
如果有分支合并,会显示更复杂的图形,比如|\、| *等。
参数 3:--author(筛选作者)------ 只看某个作者的提交
如果你和别人协作(本地协作,比如多人共用一个仓库),想只看自己的提交记录,可以用这个参数:
# 格式:git log --author="用户名"
git log --author="小明学Git"
输出会只显示 "小明学 Git" 这个作者的提交记录。
2.3 git log 的 "救命" 补充命令:git reflog
git log有一个 "缺点":它只能显示当前分支的提交记录------ 如果你执行了版本回退,回到了之前的版本,再执行git log,会发现 "回退之后的提交记录" 不见了。
比如你有 3 条提交记录,回退到了第 2 条,执行git log只会显示前 2 条,第 3 条好像 "消失" 了 ------ 这时候git reflog就派上用场了!
git reflog的作用是:记录本地仓库的所有操作历史------ 包括提交、回退、分支切换等,不管你怎么操作,它都会记下来。
git reflog
输出示例:
d8e7f6a HEAD@{0}: reset: moving to HEAD^
b5c4d3e HEAD@{1}: commit: 新增todo.md,记录Git学习待办
a1b2c3d HEAD@{2}: commit: 添加git add的四种用法说明
d4e5f6g HEAD@{3}: commit (initial): 初始化Git学习笔记
这个输出告诉你:
HEAD@{0}:最近一次操作是 "回退到上一个版本";HEAD@{1}:上一次操作是 "提交了 todo.md";- 以此类推。
必记 :如果不小心回退错了版本,或者想找回 "消失" 的提交记录,就用git reflog------ 它能帮你找到所有操作的commit id。
3. git status & git log 的搭配使用场景
这两个命令不是孤立的,而是经常搭配使用 ------ 比如:
- 提交前检查 :执行
git status确认所有需要提交的文件都在暂存区 → 执行git commit -m "备注"→ 执行git log --oneline确认提交成功; - 回退版本前检查 :执行
git log --oneline找到要回退的commit id→ 执行git reset --hard 目标commit id→ 执行git status确认工作区状态; - 排查问题时检查 :执行
git status确认工作区是否有未提交的修改 → 执行git log --oneline查看最近的提交记录,找到可能导致问题的提交。
版本回退:
版本回退 = 作家从存档柜找回旧版终稿
Git 的版本回退,本质是调整「HEAD 指针」(可以理解为 "当前版本的书签"),让它从 "最新版本" 指向 "历史版本"------ 就像作家发现最新存档的《第 1 章 v3》写坏了,想把存档柜、稿夹、书桌都换回《第 1 章 v2》的状态:
- 只想改存档柜的标签(保留草稿)→ 软回退;
- 改存档柜标签 + 清空稿夹(保留书桌草稿)→ 混合回退;
- 存档柜、稿夹、书桌全换回旧版(扔掉所有新修改)→ 硬回退。
所有回退操作的核心是git reset命令,不同参数对应不同的 "回退力度",这是我们要重点拆解的。
将工作区内容回退到暂存区中的内容(git checkout -- file):
先定核心逻辑:git checkout -- [file] = 作家 "用存档 / 稿夹里的草稿,覆盖书桌上写坏的草稿"
这个指令的唯一核心作用是:将指定文件的「工作区版本」恢复到「暂存区版本」(若暂存区有该文件快照)或「版本库最新提交版本(HEAD)」(若暂存区无快照),且会直接覆盖工作区的现有修改(不可逆)。
简单说:你书桌上的《第 1 章.txt》改坏了,用这个指令从 "待打印稿夹(暂存区)" 或 "存档柜(版本库)" 取出一份 "干净的草稿",直接替换书桌上的坏草稿 ------ 这是 Git 中恢复单个文件最直接的方式。
关键前置:-- 的作用(新手必懂,避免踩坑)
-- 是 "分界符",用来明确区分「分支名」和「文件名」,避免 Git 把文件名误判为分支名。比如:
- 如果你有个文件叫
main,同时有个分支也叫main,执行git checkout main会切换分支,而git checkout -- main才会恢复main这个文件; - 日常使用中,哪怕文件名和分支名不冲突,也建议加上
--,养成好习惯,避免意外。
一、git checkout -- [file] 的执行原理(从哪恢复?)
这个指令的恢复逻辑有明确的 "优先级",核心看「暂存区」是否有该文件的快照:
- 若文件已被
git add(暂存区有快照):从「暂存区」恢复文件 → 覆盖工作区; - 若文件未被
git add(暂存区无快照):从「版本库最新提交(HEAD)」恢复文件 → 覆盖工作区; - 若文件是「未跟踪文件」(从没
git add过) :该指令完全无效(因为暂存区 / 版本库都没有它的快照,Git 无法恢复)。
用表格更清晰:
| 文件状态 | 恢复来源 | 执行效果 |
|---|---|---|
| 已暂存(git add 过) | 暂存区 | 工作区文件 ← 暂存区快照(覆盖当前修改) |
| 已跟踪但未暂存 | 版本库最新提交(HEAD) | 工作区文件 ← 版本库快照(覆盖当前修改) |
| 未跟踪(从没 git add 过) | 无(Git 无记录) | 指令无效,工作区文件不变 |
二、实操示例
前置准备(先创建测试文件并提交)
# 1. 新建《第1章.txt》并写入初始内容
echo "第1章:初遇
男主走在街头,看到了女主。" > 第1章.txt
# 2. 提交到版本库(存档柜)
git add 第1章.txt
git commit -m "第1章v1:完成初遇剧情"
场景 1:文件已暂存 → 从暂存区恢复工作区文件
作家类比
你在书桌上修改了《第 1 章.txt》(加了一段错误的反派剧情),并把这份错稿放进了 "待打印稿夹"(git add);后来发现错了,想把书桌上的错稿换成 "稿夹里之前存的正确版本"(比如稿夹里是没加反派的版本)。
实操步骤
# 步骤1:修改工作区文件(加错误内容)
echo "第1章:初遇
男主走在街头,看到了女主。
反派突然出现(错误内容)。" > 第1章.txt
# 步骤2:把修改暂存(放进稿夹,此时暂存区是错版,但先模拟"暂存区有快照")
git add 第1章.txt
# 步骤3:再修改工作区(加更错的内容,比如"反派打了男主")
echo "第1章:初遇
男主走在街头,看到了女主。
反派突然出现(错误内容)。
反派打了男主(更错)。" > 第1章.txt
# 步骤4:执行恢复指令 → 从暂存区恢复(覆盖工作区的"更错内容")
git checkout -- 第1章.txt
# 步骤5:查看文件内容(验证恢复效果)
cat 第1章.txt
输出结果(恢复后)
第1章:初遇
男主走在街头,看到了女主。
反派突然出现(错误内容)。
效果:工作区回到暂存区的版本(只删了 "更错" 的那行,暂存区的错版还在)。
场景 2:文件未暂存 → 从版本库(HEAD)恢复工作区文件
作家类比
你在书桌上修改了《第 1 章.txt》(加了错误剧情),但还没放进稿夹(没 git add);现在想直接用 "存档柜里的 v1 终稿" 覆盖书桌上的错稿。
实操步骤
# 步骤1:修改工作区文件(加错误内容,不暂存)
echo "第1章:初遇
男主走在街头,看到了女主。
反派突然出现(错误内容)。" > 第1章.txt
# 步骤2:执行恢复指令 → 从版本库HEAD恢复
git checkout -- 第1章.txt
# 步骤3:查看文件内容
cat 第1章.txt
输出结果(恢复后)
第1章:初遇
男主走在街头,看到了女主。
效果:工作区回到版本库最新提交的 v1 版本,所有未暂存的错误修改都被覆盖。
场景 3:未跟踪文件 → 指令无效
作家类比
你新买了一本空白草稿纸《番外.txt》(未跟踪),写了一段错剧情,想通过 git checkout -- 番外.txt 恢复 ------ 但 Git 从没记录过这份草稿,指令无效。
实操步骤
# 步骤1:新建未跟踪文件(从没git add过)
echo "番外:错误剧情" > 番外.txt
# 步骤2:执行恢复指令
git checkout -- 番外.txt
# 步骤3:查看文件内容
cat 番外.txt
输出结果
番外:错误剧情
效果:文件内容不变,Git 提示 "未匹配到文件"(或无提示),指令无效。
三、关键注意事项(避坑核心!)
1. 执行后修改不可逆(最核心的坑)
git checkout -- [file] 会直接覆盖工作区的修改 ,且这些被覆盖的修改无法通过 Git 恢复(因为没暂存 / 提交)。
避坑:执行前务必确认工作区的修改不需要保留,或先备份文件(比如 cp 第1章.txt 第1章_备份.txt)。
2. -- 不能漏(避免歧义)
如果你的文件名和分支名重名(比如有个文件叫 dev,也有个分支叫 dev):
- 漏写
--:git checkout dev→ Git 会认为你要切换到 dev 分支,而非恢复文件; - 写对
--:git checkout -- dev→ Git 明确恢复dev这个文件。
3. 路径要写对(子文件夹文件)
如果文件在子文件夹里(比如 draft/第1章.txt),必须写全路径:
# 正确:
git checkout -- draft/第1章.txt
# 错误(Git找不到文件):
git checkout -- 第1章.txt
4. 对 "已删除的文件" 也有效
如果工作区的文件被你手动删除(rm 第1章.txt),执行 git checkout -- 第1章.txt 会从暂存区 / 版本库恢复该文件到工作区:
# 删除工作区文件
rm 第1章.txt
# 恢复文件
git checkout -- 第1章.txt
# 验证:文件重新出现
ls 第1章.txt # 输出:第1章.txt
四、和 Git 新版指令 git restore 的关系(补充)
Git 2.23 版本后,为了简化 git checkout 的混乱(既切换分支又恢复文件),把 "恢复文件" 的功能拆分到了 git restore 指令:
git checkout -- [file]↔git restore [file](完全等价,恢复工作区文件);- 如果想从版本库恢复(而非暂存区),可以显式指定:
git restore --source=HEAD [file]。
建议:新手可以优先用 git restore(语义更清晰),但必须掌握 git checkout -- [file](老项目 / 旧 Git 版本仍常用)。
总结(核心要点回顾)
- 核心作用 :
git checkout -- [file]是恢复单个文件的工作区版本,覆盖现有修改(不可逆); - 恢复来源:优先从暂存区恢复,暂存区无快照则从版本库 HEAD 恢复,未跟踪文件无效;
- 关键避坑 :
- 执行前备份重要修改,避免不可逆丢失;
- 必须加
--区分文件名和分支名; - 子文件夹文件要写全路径。
- 那么大家在日常使用的话,也大可以使用git restore指令进行恢复哦。
这个指令是 Git 中 "救急" 的核心用法 ------ 比如改坏了文件想一键回滚,只要记住它的恢复逻辑和风险,就能安全使用。
一、版本回退的前置准备:找到要回退的 "旧版终稿"(查 commit ID)
回退前必须先找到目标版本的「唯一标识(commit ID)」------ 就像作家要先在存档柜里找到《第 1 章 v2》的标签,才能取出对应的终稿。
1. 用git log查 "可达的历史版本"(最常用)
git log会列出从最新到最远的提交记录,重点看commit ID(前 7 位) 和提交备注:
# 精简输出(一行一条记录,新手首选)
git log --oneline
示例输出(作家写小说的提交记录):
f87c9d0 第1章v3:新增反派剧情 # 最新版本(HEAD指向这个)
e65b7a8 第1章v2:完善男主人设 # 想回退到这个版本
d43a5c6 第1章v1:完成初遇剧情 # 更早的版本
f87c9d0/e65b7a8:commit ID 的前 7 位(完整 ID 是 40 位哈希,用前 7 位就够);HEAD:默认指向最新提交,回退就是让 HEAD 指向旧 ID。
2. 用git reflog查 "所有操作历史"(找回 "消失" 的版本)
如果已经执行过回退,git log会 "看不到" 被回退的版本(比如回退到 v2 后,git log里没有 v3 了)------ 此时用git reflog(引用日志),能查到所有本地操作记录(包括提交、回退、分支切换),是找回误删版本的 "救命命令"。
git reflog
示例输出:
f87c9d0 HEAD@{0}: reset: moving to e65b7a8 # 刚执行了回退操作
e65b7a8 HEAD@{1}: commit: 第1章v2:完善男主人设
d43a5c6 HEAD@{2}: commit: 第1章v1:完成初遇剧情
HEAD@{0}:最近一次操作;- 哪怕 v3 的记录在
git log里消失了,git reflog里也能找到它的 commit ID,能一键恢复。
必记:
- 回退前先执行
git log --oneline,把目标版本的 commit ID 记下来; - 如果怕回退错,先执行
git reflog,确保有 "后悔药"。
二、核心回退命令:git reset的三种模式
git reset是版本回退的核心命令,通过不同参数(--soft/--mixed/--hard)控制 "回退范围"------ 三种模式的区别,本质是「是否动暂存区、是否动工作区」
先明确三个关键概念:
- 版本库:存档柜(永久保存的终稿);
- 暂存区:待打印稿夹(待存档的草稿);
- 工作区:书桌(正在写的草稿)。
以防下面大家忘记了,在这里我就直接提醒大家,版本回退除了可以回退到之前的版本,你要是说,把当前版本回退到之前的版本之后想要再回到当前版本,那么你依旧可以使用版本回退进行操作,你只需要git reflog去看之前的当前版本的commit id即可,这一点也希望大家要注意
模式 1:软回退(--soft)------ 只动版本库,保留所有草稿
1. 通俗类比(作家视角)
你发现《第 1 章 v3》写坏了,只想把存档柜的 "最新标签" 从 v3 改回 v2,但:
- 待打印稿夹里的 v3 草稿还在(暂存区不变);
- 书桌上没写完的 v3 草稿也还在(工作区不变);
- 只是存档柜显示 "v2 是最新终稿",你可以修改 v3 草稿后重新存档。
2. 技术定义
仅调整 HEAD 指针指向目标版本(版本库回退),暂存区和工作区的所有修改都保留------ 已暂存的内容仍在暂存区,未暂存的修改仍在工作区。
3. 核心命令
bash
# 格式1:回退到上一个版本(HEAD^ 代表"上一个版本",HEAD表示当前版本)
git reset --soft HEAD^
# 格式2:回退到指定版本(推荐,精准)
git reset --soft <目标版本commit ID>
# commit id通过git log进行查看
4. 实操示例(回退到第 1 章 v2)
# 步骤1:确认当前版本(v3,commit ID:f87c9d0)
git log --oneline # 输出:f87c9d0 第1章v3;e65b7a8 第1章v2
# 步骤2:执行软回退到v2(commit ID:e65b7a8)
git reset --soft e65b7a8
# 步骤3:查看状态(验证效果)
git status
示例输出(git status):
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: 第1章.txt # v3的修改仍在暂存区,待提交
效果总结:
- 版本库:HEAD 指向 v2,v3 的提交记录 "暂时隐藏";
- 暂存区:v3 的修改还在,显示 "待提交";
- 工作区:所有修改都保留,可继续编辑。
适用场景:
- 提交后发现 "备注写错了""漏加了一行内容",想修改后重新提交;
- 不想丢弃最新版本的修改,只是想撤销 "提交" 这个动作。
模式 2:混合回退(--mixed,默认模式)------ 动版本库 + 暂存区,保留工作区
1. 通俗类比(作家视角)
你想把存档柜标签改回 v2,同时把待打印稿夹里的 v3 草稿抽回书桌(清空暂存区),但:
- 书桌上的 v3 草稿还在(工作区不变);
- 只是稿夹空了,你可以重新整理草稿再放进稿夹。
2. 技术定义
调整 HEAD 指针指向目标版本(版本库回退),清空暂存区(已暂存的内容回到工作区),但工作区的修改仍保留------ 这是git reset的默认模式(不加参数就是 --mixed)。
3. 核心命令
# 格式1:默认模式,回退到上一个版本(省略--mixed)
git reset HEAD^
# 格式2:显式指定,回退到指定版本(推荐)
git reset --mixed <目标版本commit ID>
4. 实操示例(回退到第 1 章 v2)
# 步骤1:执行混合回退
git reset --mixed e65b7a8
# 步骤2:查看状态
git status
示例输出(git status):
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: 第1章.txt # v3的修改回到工作区,未暂存
效果总结:
- 版本库:HEAD 指向 v2;
- 暂存区:清空,所有已暂存的内容回到工作区;
- 工作区:所有修改保留,显示 "未暂存"。
适用场景:
- 提交后发现 "暂存区里加了不该加的文件",想清空暂存区,重新筛选要提交的内容;
- 想撤销 "git add" 和 "git commit" 两个动作,只保留工作区的修改。
模式 3:硬回退(--hard)------ 版本库 + 暂存区 + 工作区全回退(慎用!)
1. 通俗类比(作家视角)
你彻底放弃 v3,想让存档柜、稿夹、书桌全变回 v2 的状态:
- 存档柜标签改回 v2;
- 稿夹里的 v3 草稿全扔掉(清空暂存区);
- 书桌上的 v3 草稿全换成 v2 的终稿(覆盖工作区);
- v3 的所有修改都消失,彻底回到 v2。
2. 技术定义
调整 HEAD 指针指向目标版本,版本库、暂存区、工作区全恢复到目标版本的状态 ------ 所有未提交的修改(暂存区 + 工作区)都会被永久删除 ,无法恢复(除非用git reflog找回 commit ID)。
3. 核心命令
# 格式1:回退到上一个版本
git reset --hard HEAD^
# 格式2:回退到指定版本
git reset --hard <目标版本commit ID>
4. 实操示例(回退到第 1 章 v2)
# 步骤1:执行硬回退
git reset --hard e65b7a8
# 步骤2:查看状态
git status
示例输出(git status):
On branch master
nothing to commit, working tree clean # 工作区、暂存区全干净
效果总结:
- 版本库:HEAD 指向 v2;
- 暂存区:清空;
- 工作区:所有文件恢复为 v2 的内容,v3 的修改全丢失。
适用场景:
- 确定最新版本的修改完全没用(比如代码写崩了、剧情全写废了),想一键回到能正常运行 / 写作的版本;
- 新手慎用!执行前务必确认 "所有修改都不需要保留"。
三种回退模式对比表
| 模式 | 版本库(存档柜) | 暂存区(稿夹) | 工作区(书桌) | 数据安全性 | 适用场景 |
|---|---|---|---|---|---|
| --soft | 回退 | 不变 | 不变 | 最高(无丢失) | 撤销提交,保留所有修改 |
| --mixed(默认) | 回退 | 清空(回工作区) | 不变 | 高(仅暂存区变工作区) | 撤销提交 + 撤销暂存,保留修改 |
| --hard | 回退 | 清空 | 覆盖为旧版本 | 最低(会丢失) | 彻底放弃最新修改,回到旧版本 |
三、不同回退场景的实操指南
场景 1:撤销最近一次提交(保留所有修改)
需求:
刚提交了 v3,发现备注写错了,想改备注后重新提交。
步骤:
- 执行软回退:
git reset --soft HEAD^; - 修改提交备注(或补充内容):编辑文件后重新
git add(如果需要); - 重新提交:
git commit -m "第1章v3:修正反派剧情逻辑"。
当然了,也可以使用git commit -amend -m指令
场景 2:回退到指定版本(比如 3 次前的提交)
需求:
想回到 "第 1 章 v1"(commit ID:d43a5c6),保留工作区修改。
步骤:
- 查 commit ID:
git log --oneline; - 执行混合回退:
git reset --mixed d43a5c6; - 验证:
git status(修改回到工作区,可重新编辑)。
场景 3:找回误删的版本(回退后发现删错了)
需求:
硬回退到 v2 后,发现 v3 的内容有用,想恢复 v3。
步骤:
- 查所有操作记录:
git reflog(找到 v3 的 commit ID:f87c9d0); - 恢复 v3:
git reset --hard f87c9d0; - 验证:
git log --oneline(v3 重新成为最新版本)。
场景 4:放弃所有未提交的修改(回到最新已提交版本)
需求:
工作区 / 暂存区改得一塌糊涂,想一键回到最新的已提交版本。
步骤:
- 执行硬回退到最新版本:
git reset --hard HEAD; - 验证:
git status(工作区干净)。
四、坑
坑 1:滥用git reset --hard
- 后果:永久删除未提交的修改,且无法恢复(除非用
git reflog找回 commit ID); - 避坑:执行前先执行
git status,确认所有需要保留的修改都已提交 / 备份。
坑 2:回退已推送到远程仓库的版本
- 后果:本地回退后,远程仓库还是最新版本,推送时会冲突,甚至导致团队协作混乱;
- 避坑:本地回退随便玩,但已推送到远程的版本,别用
git reset --hard,改用git revert(创建新提交撤销旧版本,不删历史)。
坑 3:记不住 commit ID
- 后果:不知道该回退到哪个版本;
- 避坑:回退前先执行
git log --oneline > 版本记录.txt,把 commit ID 和备注保存到文件里。
坑 4:回退后想撤销回退
- 后果:以为回退后就找不回旧版本了;
- 避坑:用
git reflog查所有操作记录,找到旧版本的 commit ID,执行git reset --hard <ID>就能恢复。
值得说的是,Git 的版本回退速度非常快,因为 Git 在内部有个指向当前分支(此处是master)的
HEAD 指针, refs/heads/master 文件里保存当前 master 分支的最新commit id 。当我们
在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储一个特定的version,可以简单理解
成如下示意图:

总结
- 回退前必做 :用
git log --oneline找目标 commit ID,用git reflog留 "后悔药"; - 三种回退模式选对 :
- 保留所有修改:
--soft(只动版本库); - 保留工作区、清空暂存区:
--mixed(默认); - 彻底放弃所有修改:
--hard(慎用);
- 保留所有修改:
- 安全第一:未提交的修改先备份,已推远程的版本别硬回退。
版本回退是 Git 最强大的功能之一,核心是 "后悔药管够"------ 只要掌握了git reset的三种模式,不管是改坏了代码、写错了提交备注,还是想回到旧版本,都能轻松解决。
删除文件:
Git 删除文件 = 作家 "管理草稿的移除状态"
Git 中删除文件的本质不是 "彻底删掉文件内容",而是标记文件的 "删除状态"(就像作家处理草稿纸,不是扔了就没记录):
- 扔书桌的草稿(仅删工作区):草稿没了,但稿夹 / 存档柜还有备份,能捡回来;
- 扔书桌 + 稿夹的草稿(git rm):标记 "要从存档柜移除",但没真正删存档,提交前能恢复;
- 从存档柜移除终稿(git commit 删除状态):存档柜里记录 "这份终稿被移除了",但仍能从历史记录找回;核心:只要文件进过版本库(执行过
git commit),哪怕 "删到看不见",也能通过 Git 恢复 ------ 这是和 "手动删除文件" 最本质的区别。
前置知识:文件的 "追踪状态"(影响删除逻辑)
| 文件状态 | 作家类比 | 删除操作的核心区别 |
|---|---|---|
| 已跟踪文件(git add/commit 过) | 进过稿夹 / 存档柜的草稿 | 可通过 Git 恢复(有版本记录) |
| 未跟踪文件(从没 git add 过) | 新买的空白草稿纸(只写了几行) | 手动删除后无法通过 Git 恢复(无记录) |
一、Git 删除文件的核心场景(按操作阶段拆分)
场景 1:仅删除「工作区」文件(手动删 /rm命令)------ 扔掉书桌的草稿
这是最基础的删除,只删电脑里的文件(工作区),暂存区 / 版本库的快照还在,可一键恢复。
1. 通俗类比(作家视角)
你书桌上有《第 1 章.txt》的草稿(工作区),觉得写得不好,随手揉掉扔了 ------ 但 "待打印稿夹"(暂存区)里还有这份草稿的备份,存档柜里也有终稿,随时能把草稿重新放回书桌。
2. 技术定义
通过系统命令(rm)或手动删除,仅移除工作区的文件实体,暂存区 / 版本库的文件快照仍保留,Git 会检测到 "工作区文件缺失",标记为「已删除(未暂存)」状态。
3. 实操命令 + 示例
(1)执行删除(两种方式)
# 方式1:系统rm命令删除工作区文件
rm 第1章.txt
# 方式2:手动在文件夹里删除(效果一样)
(2)查看 Git 状态(确认删除状态)
git status
输出示例:
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: 第1章.txt # 标记为"已删除(未暂存)"
关键:Git 明确提示 "文件被删,但未暂存删除状态",并给出两个操作建议:
git rm <file>:暂存删除状态(准备提交到版本库);git restore <file>:恢复工作区文件(捡回扔掉的草稿)。
(3)恢复删除的文件(核心!删错了怎么救)
# 方法1:用git checkout恢复(兼容旧Git版本)
git checkout -- 第1章.txt
# 方法2:用git restore恢复(Git 2.23+推荐,语义更清晰)
git restore 第1章.txt
效果:工作区重新出现《第 1 章.txt》,内容和暂存区 / 版本库的最新快照一致。
场景 2:删除并「暂存删除状态」(git rm)------ 扔掉书桌 + 稿夹的草稿
git rm是 Git 的 "删除并暂存" 命令,既删工作区文件,又把 "删除状态" 加入暂存区(标记为 "要从版本库移除"),那么要是你只是想把工作区和暂存区的文件删除,但是不想去把版本库的文件删除掉,那么你就可以只git rm,不用去把删除提交到版本库中
1. 通俗类比(作家视角)
你不仅扔掉了书桌上的《第 1 章.txt》草稿,还把 "待打印稿夹" 里的备份也抽出来扔了 ------ 现在稿夹里标记 "《第 1 章》要从存档柜移除",但还没真正删存档柜的终稿。
2. 技术定义
- 第一步:删除工作区的文件实体;
- 第二步:将 "文件被删除" 这个状态加入暂存区(相当于
rm <file> + git add <file>); - Git 会标记为「已删除(已暂存)」,等待
git commit提交到版本库。
3. 实操命令 + 示例
(1)执行 git rm 删除并暂存
# 删除工作区文件,并暂存删除状态
git rm 第1章.txt
# 若文件已修改且未暂存,Git会拒绝删除(避免丢失修改),需强制删除:
# git rm -f 第1章.txt
(2)查看 Git 状态(确认暂存删除状态)
git status
输出示例:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: 第1章.txt # 标记为"已删除(已暂存)"
关键:删除状态已进入暂存区,下一步git commit就会把这个删除同步到版本库。
(3)恢复暂存的删除状态(删错了怎么救)
此时文件已从工作区删除,且删除状态已暂存,恢复分两步:
# 步骤1:撤回暂存的删除状态(把稿夹里的"删除标记"撤回来)
git restore --staged 第1章.txt
# 步骤2:恢复工作区文件(把草稿放回书桌)
git restore 第1章.txt
效果:暂存区的删除状态被撤销,工作区重新出现《第 1 章.txt》,恢复到删除前的状态。
场景 3:仅暂存「删除状态」(git rm --cached)------ 停止追踪文件(保留工作区)
这是新手最易误解的命令:git rm --cached不删除工作区文件,只 "停止 Git 追踪该文件"(从暂存区 / 版本库的追踪列表中移除),工作区文件仍保留。
1. 通俗类比(作家视角)
你不想再把《第 1 章.txt》纳入 "存档体系"(比如这是废稿),于是从 "待打印稿夹" 和 "存档柜的追踪清单" 里删掉它的名字 ------ 但书桌上的草稿还在,只是不再被 Git 监控。
2. 技术定义
- 核心:仅移除暂存区 / 版本库中该文件的 "追踪记录",工作区文件完全保留;
- 文件会从「已跟踪」变为「未跟踪」,Git 不再监控它的修改 / 删除;
- 常用场景:不小心把临时文件(如
node_modules/、日志.txt)提交到版本库,想停止追踪但保留本地文件。
3. 实操命令 + 示例
(1)执行 git rm --cached(停止追踪)
# 停止追踪《第1章.txt》,保留工作区文件
git rm --cached 第1章.txt
(2)查看 Git 状态(确认状态变化)
git status
输出示例:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: 第1章.txt # 暂存区标记"删除"(实际是停止追踪)
Untracked files:
(use "git add <file>..." to include in what will be committed)
第1章.txt # 工作区文件变为"未跟踪"
关键:暂存区显示 "deleted"(停止追踪),但工作区文件还在,只是变成未跟踪状态。
(3)恢复追踪(如果后悔了)
# 步骤1:撤回暂存的"停止追踪"状态
git restore --staged 第1章.txt
# 步骤2:重新跟踪(可选,若文件已变为未跟踪)
git add 第1章.txt
场景 4:提交「删除状态」到版本库(git commit)------ 从存档柜移除终稿
前面的删除都是 "临时状态",执行git commit后,删除状态才会被永久记录到版本库(相当于 "从存档柜移除终稿")。
那么说白了其实就是让版本库也知道说,诶我们把这个文件删除了,不要了,那么版本库也会自动把其内所存储的文件删除掉。
1. 通俗类比(作家视角)
你确认要移除《第 1 章.txt》的终稿,于是把 "稿夹里的删除标记" 提交到存档柜 ------ 存档柜里记录 "《第 1 章》终稿已移除",但存档柜的历史记录里仍能查到这份终稿(可恢复)。
2. 技术定义
将暂存区的 "删除状态" 打包成提交快照,存入版本库 ------ 版本库中该文件的最新快照变为 "不存在",但历史提交中仍保留该文件的所有版本。
3. 实操命令 + 示例
# 提交删除状态到版本库
git commit -m "移除第1章:该章节重写,暂移除旧版本"
(1)恢复已提交删除的文件(核心!删错了怎么救)
哪怕文件已从版本库删除,只要有历史提交记录,就能恢复 ------ 步骤:
# 步骤1:查历史提交,找到文件还存在的commit ID
git log --oneline -- 第1章.txt # 只显示和该文件相关的提交
# 输出示例:a87b9c 第1章v1:完成初遇剧情(文件还存在的最后一个版本)
# 步骤2:从该commit ID恢复文件到工作区
git restore --source=a87b9c 第1章.txt
# 或用旧版命令:git checkout a87b9c -- 第1章.txt
# 步骤3:重新暂存+提交(把文件加回版本库)
git add 第1章.txt
git commit -m "恢复第1章:误删,重新加回版本库"
效果:工作区重新出现《第 1 章.txt》,内容和指定版本一致,且重新提交到版本库。
二、Git 删除文件的核心命令对比
| 命令 | 操作对象 | 工作区文件 | 暂存区状态 | 版本库状态 | 适用场景 |
|---|---|---|---|---|---|
rm 文件名 |
仅工作区 | 删除 | 未暂存删除状态 | 不变 | 临时删工作区文件,想保留恢复余地 |
git rm 文件名 |
工作区 + 暂存区 | 删除 | 已暂存删除状态 | 不变 | 确认要从版本库移除文件 |
git rm -f 文件名 |
工作区 + 暂存区(强制) | 删除 | 已暂存删除状态 | 不变 | 文件已修改未暂存,强制删除 |
git rm --cached 文件名 |
仅暂存区 / 追踪状态 | 保留 | 已暂存停止追踪 | 不变 | 停止追踪文件,保留本地文件 |
git commit -m "删文件" |
暂存区→版本库 | 不变 | 清空 | 记录删除 | 永久记录文件删除到版本库 |
三、新手必避的坑(90% 新手会踩)
坑 1:混淆git rm和git rm --cached
- 后果:想保留工作区文件,却用了
git rm(删了工作区文件); - 避坑:记住口诀 ------
git rm:删工作区 + 暂存区(真删文件);git rm --cached:只停追踪,保留工作区(假删)。
坑 2:删除未跟踪文件后想恢复
- 后果:手动删了从没
git add过的文件,以为能通过 Git 恢复; - 避坑:未跟踪文件无任何 Git 记录,删除后只能从系统回收站 / 备份恢复。
坑 3:强制删除(git rm -f)丢失修改
- 后果:文件有未暂存的修改,用
git rm -f强制删除,修改永久丢失; - 避坑:强制删除前先执行
git stash暂存修改(或手动备份)。
坑 4:提交删除后以为 "彻底没了"
- 后果:删了版本库的文件,以为找不回来;
- 避坑:Git 会保留所有历史提交,用
git log找到旧 commit ID 就能恢复。
总结(核心要点回顾)
- Git 删除的本质是 "标记状态":不是删文件内容,而是标记 "删除 / 停止追踪",只要文件进过版本库,就能恢复;
- 不同删除命令的核心区别 :
- 仅删工作区:
rm 文件名(可git restore恢复); - 删工作区 + 暂存删除:
git rm(先git restore --staged再git restore恢复); - 停追踪不删文件:
git rm --cached(保留本地文件);
- 仅删工作区:
- 删错了别慌 :
- 未提交删除:用
git restore/git checkout恢复; - 已提交删除:用
git log找旧 commit ID,再git restore --source=<ID>恢复。
- 未提交删除:用
Git 的删除操作是 "可追溯、可恢复" 的,核心是理解 "工作区 / 暂存区 / 版本库" 的状态流转 ------ 只要不滥用强制删除,哪怕删错了,也能通过 Git 的历史记录找回,这也是版本控制的核心价值。
常见问题汇总与解决方案
学到这里,你已经掌握了 Git 本地操作的三大核心命令 ------ 但新手在实操中,一定会遇到各种问题。下面我整理了10 个新手高频问题,并给出简单易懂的解决方案 ------ 这部分内容一定要收藏!
问题 1:我能撤回 git add 吗?怎么撤回?
答案:能!分两种情况:
- 撤回单个文件 :
git reset HEAD 文件名比如撤回temp.txt:git reset HEAD temp.txt; - 撤回所有暂存的文件 :
git reset HEAD .这个命令会把暂存区的所有文件都撤回到工作区。
原理 :git reset HEAD的作用是 "把暂存区的内容恢复成版本库的最新状态"------ 如果版本库没有这个文件,就会从暂存区移除。
问题 2:我能撤回 git commit 吗?怎么撤回?
答案:能!推荐新手用两种安全的方法:
-
方法一:保留工作区修改,只撤回提交记录(推荐)
git reset --soft HEAD^这个命令的作用是:
- 把最近一次提交的修改撤回到暂存区;
- 工作区的修改会被保留;
- 你可以修改后重新提交。其中
HEAD^表示 "上一个版本",HEAD^^表示 "上上一个版本",以此类推。
-
方法二:彻底回退到上一个版本,丢弃本次提交的修改(慎用!)
git reset --hard HEAD^这个命令会:
- 把工作区、暂存区、版本库全部回退到上一个版本;
- 本次提交的所有修改都会被永久删除;
- 只有确定本次提交的修改完全没用时,才用这个命令。
问题 3:commit 信息写错了,能不能修改?
答案 :能!只能修改最近一次的提交信息:
git commit --amend -m "新的提交信息"
比如把 "添加 git add 三种用法" 改成 "添加 git add 四种用法":
git commit --amend -m "添加git add的四种用法说明"
问题 4:我修改了文件,但不想保留修改,怎么恢复到上一次提交的状态?
答案 :用git checkout -- 文件名------ 这个命令会丢弃工作区的修改 ,恢复成版本库的最新状态。比如恢复notes.md:
git checkout -- notes.md
警告 :这个命令会永久删除工作区的修改------ 如果修改还没提交,执行后就找不回来了!
问题 5:为什么我执行 git commit 时,总是进入编辑器界面?
答案 :因为你省略了 - m 参数------Git 要求必须写提交信息,如果你没写,就会打开编辑器让你输入。
解决办法:
- 下次记得加 - m 参数 :
git commit -m "备注信息"; - 如果已经进入编辑器界面(比如 vim) :
- 想保存并退出:按
Esc→ 输入:wq→ 回车; - 想取消提交:按
Esc→ 输入:q!→ 回车。
- 想保存并退出:按
问题 6:为什么我执行 git 命令时,总是提示 "not a git repository"?
答案 :因为你不在 Git 仓库目录下执行命令 ------Git 的所有命令,都必须在已经执行过git init的目录(或其子目录)下执行。
解决办法:
- 用
cd命令切换到你的仓库目录,比如:cd ~/Desktop/my-first-git-repo; - 执行
pwd确认当前目录是仓库目录; - 再执行 Git 命令。
问题 7:为什么我修改了图片文件,git status 只显示 modified,但看不到具体改了什么?
答案 :因为 Git只能跟踪文本文件的内容变化(比如代码、文档),对于二进制文件(比如图片、视频、压缩包),Git 只能知道 "文件大小变了",但没法知道具体改了什么。
这是正常现象,不用解决 ------ 新手只需要知道:二进制文件可以用 Git 管理,但没法查看修改细节。
问题 8:我不小心删除了.git 目录,怎么办?
答案 :没办法恢复------.git 目录是 Git 的 "控制中心",里面存着所有的提交记录和版本信息。删除.git 目录后,仓库就变回了普通文件夹,所有的版本记录都会消失。
预防办法:千万不要手动删除.git 目录!如果想放弃仓库,一定要确认里面的提交记录都没用了。
问题 9:git add . 和 git add * 有什么区别?
答案 :两者的区别在于是否包含隐藏文件 (以.开头的文件):
git add .:会添加当前目录下的所有文件 ,包括隐藏文件(比如.gitignore);git add *:不会添加隐藏文件,只添加普通文件。
新手推荐用 git add . ------ 因为它更全面,不容易遗漏文件。
问题 10:我能在一个仓库里嵌套另一个仓库吗?
答案 :不推荐!如果在一个 Git 仓库里执行git init,会创建一个 "子仓库"------Git 默认不会跟踪子仓库的内容,容易导致版本管理混乱。
解决办法:如果需要管理多个项目,就创建多个独立的仓库,不要嵌套。
结语:让 Git 成为你编程路上的 "时光守护者"
当你看到这里时,想必已经跟着教程敲完了git init的初始化、git add的筛选、git commit的定格,也试过用git log回看自己的每一步修改,用git reset找回不小心改崩的版本 ------ 恭喜你,你已经彻底告别了 "版本地狱",拥有了属于自己的 "文件时光机"。
我总想起自己刚学编程时的样子:写一段代码改了又改,文件夹里躺着 "项目 v1.py""项目 v2 最终版.py""项目 v3 改到崩溃.py",直到某天手滑覆盖了唯一能运行的版本,盯着空白的屏幕愣了半天,最后只能从头再写。那时还不知道 Git,以为 "手动备份" 就是版本管理的全部,直到第一次用git commit定格了代码状态,又用git checkout找回了半小时前的版本,才突然松了口气:原来真的有工具能接住我的所有试错,不用再怕 "改坏了回不去"。
这也是我想和你说的:Git 从来不是什么 "高大上的程序员专属工具",它本质上是为每一个害怕 "努力白费" 的人准备的 "安全感兜底"。就像我们在教程里用 "作家写小说" 类比的那样:工作区是你挥洒灵感的书桌,暂存区是你筛选精华的稿夹,版本库是你永久存档的保险柜 ------ 这三层架构不是多余的繁琐,而是让你 "大胆创作,安心沉淀" 的智慧。你不用再担心 "写了半天的内容白改了",因为git add会帮你留住值得保留的修改;不用再纠结 "哪个版本是最终版",因为git log会清晰记录每一次提交的时间、内容,哪怕是改了一个错别字,也能精准定位;更不用怕 "删错了文件找不回",因为只要文件进过版本库,哪怕是用git rm删除并提交了,也能从历史记录里一键恢复。
回顾我们走过的路:从安装 Git、配置用户名的第一步,到git init创建第一个仓库,再到用git add .暂存所有修改、用git commit -m写下清晰的备注,最后用git status检查状态、用git reset回退版本 ------ 这些看似简单的命令,背后藏着的是 "可控" 的力量。很多新手会觉得 "记命令好麻烦",但请相信,当你第三次用git reset --soft撤回写错备注的提交,第五次用git reflog找回误删的版本,第十次看着git log --oneline里整齐的提交记录时,你会发现这些命令早已变成肌肉记忆,而这份 "可控感",会让你写代码、做笔记、写论文时都更有底气。
我知道你可能会有过这样的时刻:执行git commit时忘了加-m,不小心进入 vim 编辑器慌了手脚;或者用git reset --hard误删了未提交的修改,急得满头大汗;又或者看着git diff的输出,一时分不清 "+""-" 代表什么 ------ 但这都没关系。Git 最温柔的地方,就是它给了新手足够的 "后悔药":忘了加-m就按Esc+:wq退出,误删了修改就用git reflog找回 commit ID,看不懂git diff就用--oneline简化输出。就像学走路总会摔跤,学 Git 也总会踩坑,但每一次踩坑,都是你理解它 "状态流转" 逻辑的机会 ------ 理解了 "未跟踪→已暂存→已提交" 的本质,就理解了 Git 所有操作的核心;记住了 "先本地,再远程" 的原则,就不会被后续的 GitHub、GitLab 吓住。
这里想和你分享一个小习惯:从现在开始,不管是写代码、记学习笔记,还是整理论文草稿,都把对应的文件夹初始化成 Git 仓库。不用追求 "提交得多完美",哪怕只是改了一行字,也写下一句清晰的备注 ------ 比如 "补充 git rm --cached 的用法""修正论文引言的错别字""给函数加了注释"。这些看似琐碎的提交记录,终会汇成你成长的轨迹。某天你回头看git log时,会清晰地看到自己从 "只会用 git add ." 到 "能精准用 git reset --mixed 回退版本",从 "提交备注写'改了点东西'" 到 "备注清晰到能一眼看懂修改内容" 的变化 ------ 这不仅是 Git 的记录,更是你自己的成长日记。
还要提醒你:Git 的价值不止于 "管理代码"。它教给我们的,是一种 "有序做事" 的思维。手动改版本号的本质是 "混乱的堆积",而 Git 的核心是 "有目的的沉淀"------ 每一次git add都是筛选,每一次git commit都是复盘,每一次git log都是总结。这种思维可以迁移到你做的所有事:写论文时,每完成一个章节就 "提交" 一次,标注清楚修改内容;做项目时,每实现一个功能就 "提交" 一次,留下可追溯的记录;甚至整理日常清单时,也能用 Git 记录每一次调整 ------ 当你习惯了 "每一步都有记录,每一次修改都可回溯",就会发现自己做事不再慌慌张张,而是有条不紊。
可能你现在只掌握了 Git 的本地操作,还没接触远程仓库、分支、合并这些内容,但请不要着急。就像我们在教程里强调的:"先本地,再远程"------ 把本地的基础打牢,理解了工作区、暂存区、版本库的关系,后续的所有操作都是这个核心逻辑的延伸。你可以先在本地把 Git 玩透:试着给同一个文件做十次不同的提交,用git diff对比每两次的差异;故意改坏文件,用git checkout恢复;甚至模拟 "误删文件",用历史记录找回来 ------ 当你能熟练掌控本地仓库的每一个状态,再去学习远程仓库时,就会觉得顺理成章。
最后,想和你说:编程的路上,我们总会写很多 "不完美的代码",做很多 "后悔的修改",但 Git 告诉我们:试错不可怕,怕的是没有回头的路。它就像一个沉默的守护者,安静地记录你每一次的修改、每一次的尝试,不管你走得多远,只要想回头,它都能带你回到任何一个你想要的时刻。
请不要因为 "记不住命令" 而焦虑,也不要因为 "操作失误" 而气馁 ------ 所有程序员都是从 "分不清 git add 和 git commit" 开始的,而你已经迈出了最关键的第一步:从 "被动承受版本混乱" 到 "主动掌控版本管理"。
接下来的日子里,把 Git 当成你的老朋友吧。写代码时,多敲一次git commit,多写一句清晰的备注;改坏了时,坦然地用git reset回退,再重新出发。当你习惯了有 Git 陪伴的日子,就会发现:那些曾经让你焦虑的 "版本问题",早已变成不值一提的小事,而你可以把更多精力放在真正重要的事情上 ------ 创造、思考、成长。
愿你在编程的路上,既有大胆试错的勇气,也有随时回头的底气。Git 会记住你的每一次努力,而你终会在这些有序的记录里,慢慢成为更从容、更优秀的自己。