对于Git:基础操作的超详细保姆级解析

开篇介绍:

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即可,但是要这么做的话,我们就必须是要在已经进入了创建好了的仓库中,再去使用,不然就会报错哦,这一点需要注意。

    bash 复制代码
    git config user.name "你的昵称"
    
    git config user.email "你的邮箱@示例.com"
  • 用户名和邮箱只是标识,不用和任何平台绑定,只要格式正确就行。

验证配置是否成功:输入

bash 复制代码
git config -l

能看到你刚才配置的user.nameuser.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 addgit commit等命令时,Git 会直接报错:

复制代码
fatal: not a git repository (or any of the parent directories): .git

翻译过来就是:"这不是一个 Git 仓库,我没法干活!"

简单说:git init是所有 Git 操作的 "起点",没有它,后面的一切都免谈。


第二大核心操作:git add & git commit(添加与提交)

初始化仓库后,你终于可以让 Git 帮你管理文件了 ------ 而git addgit 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:
    1. 修改已暂存的文件后,没重新执行git add------ 此时暂存区还是旧版本,git commit只会存档旧内容;
    2. 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:
    1. 提交备注写 "改了点东西""123"------ 后期用git log查历史时,完全不知道这次存档改了啥;
    2. 滥用git reset --hard HEAD^------ 会彻底删除版本库的最新提交,且丢弃工作区 / 暂存区的所有修改,新手慎用。

三、超详细汇总表格

区域 通俗类比(作家场景) 技术定义 对应物理位置 核心状态 核心操作 关键特征 新手必避坑点
工作区 书桌(草稿纸) 可直接编辑的 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

总结

  1. 工作区是 "临时创作区":看得见、改得快,但没被 Git 保护,修改前一定要确认是否需要保留;
  2. 暂存区是 "筛选整理区" :承上启下,能灵活调整要存档的内容,修改文件后务必重新git add同步;
  3. 版本库是 "永久存档区" :一旦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.mdtodo.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,才能把新的修改加入暂存区。

举个例子:

  1. 你新建notes.md,执行git add notes.md,此时暂存区里是notes.md第一版内容
  2. 你又修改了notes.md,添加了一行新内容,此时工作区的notes.md第二版内容,但暂存区里还是第一版;
  3. 如果你直接执行git commit,提交的是第一版内容,第二版的修改会留在工作区;
  4. 想要提交第二版,必须再次执行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 后不想保留修改,怎么回到原来的状态?

分两种情况:

  1. 只执行了 git add,没执行 commit :先执行git reset HEAD 文件名撤回暂存,再执行git checkout -- 文件名,就能把工作区的文件恢复到暂存前的状态;
  2. 已经执行了 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"------ 这种备注信息等于没写。正确的备注信息要满足两个条件:

  1. 简洁:尽量控制在 50 个字符以内;
  2. 明确:让人一眼就知道这次提交改了什么。

比如:

  • 好的备注: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 后发现漏改了内容,怎么办?

有两种简单的解决办法:

  1. 方法一:修改后再次 commit (推荐新手用)继续修改文件,然后执行git add 文件名git commit -m "补充修改xxx"------ 这是最安全、最简单的方式,不会破坏历史记录;
  2. 方法二:修改后用 --amend 合并到上一次提交 修改文件后,执行git add 文件名,然后执行git commit --amend -m "原来的备注信息+补充的内容"------ 这个方法会把新的修改合并到最近一次提交里,适合 "漏改的内容很少,不想多一条提交记录" 的场景。
问题 3:commit 后发现改错题了,想撤回这次提交,怎么办?

git reset命令,这个命令我们后面在 "常见问题汇总" 里会详细讲,可以先记这个简单的用法:

复制代码
# 回到上一次提交的状态,保留工作区的修改
git reset --soft HEAD^

执行后,你会发现这次提交的修改又回到了暂存区,你可以修改后重新提交。

3. 工作区、暂存区、版本库的状态转换

学到这里,你已经掌握了git addgit 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. 你写完《第 1 章》,执行git add 第1章草稿.txt(已暂存);
  2. 你又觉得剧情不好,修改了第1章草稿.txt(加了一段对话);
  3. 此时工作区的文件是 "新版",但暂存区的文件还是 "旧版";
  4. 如果你直接git commit,只会提交 "旧版",新版修改仍留在工作区;
  5. 正确操作:修改后再次执行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:以为 "已提交就不能改"------ 可以改,但要走规范流程(比如新增提交,而非删旧版本)。

总结(核心要点回顾)

  1. 未跟踪:Git "认识但不管" ------ 新建文件默认状态,无版本保护,删了找不回,需git add纳入跟踪;
  2. 已暂存:Git "管了但没存档" ------ 进了暂存区,有快照记录,需git commit才会永久保存,修改后要重新git add同步;
  3. 已提交:Git "永久保护"------ 生成唯一 commit ID,可追溯、可恢复,备注一定要清晰,别乱删版本。
状态名称 通俗解释 对应的文件状态
未跟踪(Untracked) Git 知道这个文件存在,但没开始管理它 新建的文件,从未执行过 git add
已暂存(Staged) 文件已经进入暂存区,等待被 commit 到版本库 执行过 git add,但没执行 git commit
已提交(Committed) 文件的修改已经被存入版本库,生成了快照 执行过 git commit,修改被永久保存
3.2 状态转换的触发操作)

我们用 "箭头 + 命令" 的形式,把转换关系列出来:

  1. 未跟踪 → 已暂存 :执行 git add 文件名(新建的文件,只有执行 add,才会被 Git 跟踪)
  2. 已暂存 → 已提交 :执行 git commit -m "备注"(暂存区的内容,只有执行 commit,才会变成版本快照)
  3. 已提交 → 已暂存 :修改已提交的文件后,执行 git add 文件名(对已提交的文件做修改,修改后的内容会回到 "未暂存" 状态,执行 add 后变回已暂存)
  4. 已暂存 → 未跟踪 :执行 git reset HEAD 文件名(把文件从暂存区撤回到工作区,变回未跟踪状态)
  5. 已提交 → 工作区(回退) :执行 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.mdtodo.md加入暂存区 git add . todo.md:未跟踪 → 已暂存;notes.md:已提交 → 已暂存(无修改,状态不变)
9 第三次提交,备注 "新增 todo.md 待办" git commit -m "新增todo.md,记录Git学习待办" todo.md:已暂存 → 已提交

执行完这 9 步,你的仓库里就有了 3 条提交记录,notes.mdtodo.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 diffgit 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,别手动输。

总结(核心要点回顾)

  1. git diff的核心是 "对比差异" :不同参数对应不同对比对象,核心口诀:
    • 无参数:工作区 vs 暂存区(看未暂存的修改);
    • --staged/--cached:暂存区 vs 版本库(看要提交的修改);
    • git diff <ID>:工作区 vs 指定版本(看所有修改);
    • git diff <ID1> <ID2>:版本库两个版本对比(看历史变更);
  2. 输出解读关键:+ 号 = 新增行,- 号 = 删除行,@@行号 @@= 差异位置;
  3. 提交前必做两步 :先git diff查未暂存修改,再git diff --staged查待提交修改,避免提交错漏。

第三大核心操作:git status & git log(查看工作区状态与提交历史)

学会了git initgit addgit commit,你已经能完成基本的版本管理了 ------ 但你还需要两个命令,来 "监控" 你的仓库:

  • git status:查看当前工作区和暂存区的状态------ 比如哪些文件被修改了、哪些文件在暂存区、哪些文件是未跟踪的;
  • git log:查看所有的提交历史记录------ 比如你提交了多少次、每次提交的时间和内容、commit id 是什么。

这两个命令是 "诊断工具"------ 不管你在操作中遇到什么问题,先执行git statusgit log,就能快速理清当前仓库的情况。

1. git status:你的仓库 "状态诊断仪"

git status的核心作用是:告诉你当前工作区和暂存区的 "差异"------ 它会用清晰的文字,告诉你哪些文件需要git add,哪些文件需要git commit,哪些文件是未跟踪的。

一定要养成一个习惯:每次执行git addgit 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 没反应?

有两种可能:

  1. 文件是未跟踪状态,你修改了但没执行 git add ------ 这种情况 git status 会显示?? 文件名,不是没反应;
  2. 你修改的是二进制文件(比如图片、视频) ------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 的搭配使用场景

这两个命令不是孤立的,而是经常搭配使用 ------ 比如:

  1. 提交前检查 :执行git status确认所有需要提交的文件都在暂存区 → 执行git commit -m "备注" → 执行git log --oneline确认提交成功;
  2. 回退版本前检查 :执行git log --oneline找到要回退的commit id → 执行git reset --hard 目标commit id → 执行git status确认工作区状态;
  3. 排查问题时检查 :执行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] 的执行原理(从哪恢复?)

这个指令的恢复逻辑有明确的 "优先级",核心看「暂存区」是否有该文件的快照:

  1. 若文件已被 git add(暂存区有快照):从「暂存区」恢复文件 → 覆盖工作区;
  2. 若文件未被 git add(暂存区无快照):从「版本库最新提交(HEAD)」恢复文件 → 覆盖工作区;
  3. 若文件是「未跟踪文件」(从没 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 版本仍常用)。


总结(核心要点回顾)

  1. 核心作用git checkout -- [file] 是恢复单个文件的工作区版本,覆盖现有修改(不可逆);
  2. 恢复来源:优先从暂存区恢复,暂存区无快照则从版本库 HEAD 恢复,未跟踪文件无效;
  3. 关键避坑
    • 执行前备份重要修改,避免不可逆丢失;
    • 必须加 -- 区分文件名和分支名;
    • 子文件夹文件要写全路径。
  4. 那么大家在日常使用的话,也大可以使用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,发现备注写错了,想改备注后重新提交。

步骤:
  1. 执行软回退:git reset --soft HEAD^
  2. 修改提交备注(或补充内容):编辑文件后重新git add(如果需要);
  3. 重新提交:git commit -m "第1章v3:修正反派剧情逻辑"

当然了,也可以使用git commit -amend -m指令

场景 2:回退到指定版本(比如 3 次前的提交)

需求:

想回到 "第 1 章 v1"(commit ID:d43a5c6),保留工作区修改。

步骤:
  1. 查 commit ID:git log --oneline
  2. 执行混合回退:git reset --mixed d43a5c6
  3. 验证:git status(修改回到工作区,可重新编辑)。

场景 3:找回误删的版本(回退后发现删错了)

需求:

硬回退到 v2 后,发现 v3 的内容有用,想恢复 v3。

步骤:
  1. 查所有操作记录:git reflog(找到 v3 的 commit ID:f87c9d0);
  2. 恢复 v3:git reset --hard f87c9d0
  3. 验证:git log --oneline(v3 重新成为最新版本)。

场景 4:放弃所有未提交的修改(回到最新已提交版本)

需求:

工作区 / 暂存区改得一塌糊涂,想一键回到最新的已提交版本。

步骤:
  1. 执行硬回退到最新版本:git reset --hard HEAD
  2. 验证: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,可以简单理解

成如下示意图:

总结

  1. 回退前必做 :用git log --oneline找目标 commit ID,用git reflog留 "后悔药";
  2. 三种回退模式选对
    • 保留所有修改:--soft(只动版本库);
    • 保留工作区、清空暂存区:--mixed(默认);
    • 彻底放弃所有修改:--hard(慎用);
  3. 安全第一:未提交的修改先备份,已推远程的版本别硬回退。

版本回退是 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 rmgit 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 就能恢复。

总结(核心要点回顾)

  1. Git 删除的本质是 "标记状态":不是删文件内容,而是标记 "删除 / 停止追踪",只要文件进过版本库,就能恢复;
  2. 不同删除命令的核心区别
    • 仅删工作区:rm 文件名(可git restore恢复);
    • 删工作区 + 暂存删除:git rm(先git restore --stagedgit restore恢复);
    • 停追踪不删文件:git rm --cached(保留本地文件);
  3. 删错了别慌
    • 未提交删除:用git restore/git checkout恢复;
    • 已提交删除:用git log找旧 commit ID,再git restore --source=<ID>恢复。

Git 的删除操作是 "可追溯、可恢复" 的,核心是理解 "工作区 / 暂存区 / 版本库" 的状态流转 ------ 只要不滥用强制删除,哪怕删错了,也能通过 Git 的历史记录找回,这也是版本控制的核心价值。

常见问题汇总与解决方案

学到这里,你已经掌握了 Git 本地操作的三大核心命令 ------ 但新手在实操中,一定会遇到各种问题。下面我整理了10 个新手高频问题,并给出简单易懂的解决方案 ------ 这部分内容一定要收藏!

问题 1:我能撤回 git add 吗?怎么撤回?

答案:能!分两种情况:

  1. 撤回单个文件git reset HEAD 文件名比如撤回temp.txtgit reset HEAD temp.txt
  2. 撤回所有暂存的文件git reset HEAD .这个命令会把暂存区的所有文件都撤回到工作区。

原理git reset HEAD的作用是 "把暂存区的内容恢复成版本库的最新状态"------ 如果版本库没有这个文件,就会从暂存区移除。

问题 2:我能撤回 git commit 吗?怎么撤回?

答案:能!推荐新手用两种安全的方法:

  1. 方法一:保留工作区修改,只撤回提交记录(推荐)

    复制代码
    git reset --soft HEAD^

    这个命令的作用是:

    • 把最近一次提交的修改撤回到暂存区
    • 工作区的修改会被保留;
    • 你可以修改后重新提交。其中HEAD^表示 "上一个版本",HEAD^^表示 "上上一个版本",以此类推。
  2. 方法二:彻底回退到上一个版本,丢弃本次提交的修改(慎用!)

    复制代码
    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 要求必须写提交信息,如果你没写,就会打开编辑器让你输入。

解决办法

  1. 下次记得加 - m 参数git commit -m "备注信息"
  2. 如果已经进入编辑器界面(比如 vim)
    • 想保存并退出:按Esc → 输入:wq → 回车;
    • 想取消提交:按Esc → 输入:q! → 回车。

问题 6:为什么我执行 git 命令时,总是提示 "not a git repository"?

答案 :因为你不在 Git 仓库目录下执行命令 ------Git 的所有命令,都必须在已经执行过git init的目录(或其子目录)下执行。

解决办法

  1. cd命令切换到你的仓库目录,比如:cd ~/Desktop/my-first-git-repo
  2. 执行pwd确认当前目录是仓库目录;
  3. 再执行 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 会记住你的每一次努力,而你终会在这些有序的记录里,慢慢成为更从容、更优秀的自己。

相关推荐
DeepAgent7 小时前
AI Agent 面试篇(03):在线测评——认知、性格、情境题到底怎么考
面试
Methy8 小时前
一次线上死锁排查:std::list::size () 居然是 O (n)?
c++·后端
2401_868534788 小时前
利弊比较类 社会生活类
c++·需求分析
云计算练习生8 小时前
什么是内核?操作系统内核到底管哪些事
linux·windows·操作系统·内核·shell
可涵不会debug8 小时前
公司外怎么连接管家婆?从 SQL Server 安装到 cpolar 固定 TCP 远程登录
linux·运维·ubuntu
wuyk5558 小时前
《WiFi 嵌入式物联网开发全套实战》| 第 12 章 Linux 系统 WiFi 常用命令全集:iw/iwlist/ip/wpa_cli 实战
linux·网络·stm32·物联网·tcp/ip
wuminyu8 小时前
纯轻量级锁体系下C2编译器锁粗化和消除机制剖析
java·linux·c语言·jvm·c++
运维全栈笔记8 小时前
Nginx 模块化多业务站点通用配置模板
linux·运维·nginx
weilx12348 小时前
C++笔记-mutex
c++
辛迪聊物业数字化8 小时前
【拆解智慧物业管理系统架构:从SaaS落地到IoT全链路实现】
大数据·linux·组合模式