跳转到内容

Git Reset 揭秘:三棵树理论

git reset 是 Git 中最令人困惑但也最强大的命令之一。很多教程只告诉你“用这个命令撤销提交”,但如果不理解背后的原理,很容易误删代码。

要掌握 reset,我们必须先理解 Git 的三棵树模型。

1. Git 的“三棵树” (The Three Trees)

Section titled “1. Git 的“三棵树” (The Three Trees)”

在 Git 中,“树”指的是文件集合的快照。

树 (Tree)描述作用
HEAD指向当前分支引用的指针通常指向当前分支名,间接指向最后一次提交。
Index (暂存区)预期的下一次提交git add 后的区域,准备生成的快照。
Working Directory (工作目录)沙盒你在文件系统中实际看到和编辑的文件。

当你运行 git commit 时,Git 实际上是把 Index 的状态保存为一个永久的快照,并更新 HEAD 指向它。

git reset 有两种截然不同的使用形式,理解这一点非常重要:

2.1 提交级别的 Reset(移动 HEAD)

Section titled “2.1 提交级别的 Reset(移动 HEAD)”

当不指定路径时,reset移动 HEAD 指针到指定的提交:

Terminal window
git reset [--soft | --mixed | --hard] <commit>

2.2 文件级别的 Reset(不移动 HEAD)

Section titled “2.2 文件级别的 Reset(不移动 HEAD)”

当指定路径时,reset 不会移动 HEAD,只会用指定提交中的文件内容更新暂存区:

Terminal window
git reset <commit> -- <file>
# 例如:git reset HEAD -- src/app.js

这种用法常用于取消暂存(unstage),等价于 git restore --staged <file>(Git 2.23+)。


以下讨论的是提交级别reset。假设我们要撤销最近的一次提交(回到 v1 状态):

Terminal window
git reset --soft HEAD~1
  • HEAD: 移动到上一个提交。
  • Index: 不变(保留了原先 commit 的内容)。
  • Working Directory: 不变

结果:你刚刚提交的改动回到了暂存区(Staged)。 场景:你提交了代码,但想撤销提交动作,保留代码并重新 commit(比如为了合并多个 commit)。

3.2 Mixed Reset (--mixed) —— 默认模式

Section titled “3.2 Mixed Reset (--mixed) —— 默认模式”
Terminal window
git reset HEAD~1
# 等同于 git reset --mixed HEAD~1
  • HEAD: 移动到上一个提交。
  • Index: 重置(同步为 HEAD 的内容)。
  • Working Directory: 不变

结果:你提交的改动回到了工作目录,并且是未暂存(Unstaged)的状态。 场景:你想完全撤销 commit 和 add 操作,重新在这个文件上工作。

Terminal window
git reset --hard HEAD~1
  • HEAD: 移动到上一个提交。
  • Index: 重置
  • Working Directory: 重置(仅限被追踪文件)。

结果:回到上一个提交的状态。你在该提交之后对被追踪文件的修改会丢失。 场景:彻底放弃当前的乱七八糟的改动,重头再来。

下面的流程图展示了 reset 如何影响这三个区域。假设我们处于 Commit v2,想退回 v1。

graph TD
subgraph States [状态变化]
v2[当前状态: v2]
soft[Soft Reset: 仅移动 HEAD]
mixed[Mixed Reset: 移动 HEAD + 重置 Index]
hard[Hard Reset: 移动 HEAD + 重置 Index + 重置 WorkDir]
end
v2 -->|git reset --soft v1| soft
v2 -->|git reset --mixed v1| mixed
v2 -->|git reset --hard v1| hard
classDef current fill:#4a5568,stroke:#2d3748,color:#fff
classDef safeOp fill:#38a169,stroke:#276749,color:#fff
classDef mixedOp fill:#3182ce,stroke:#2c5282,color:#fff
classDef dangerOp fill:#e53e3e,stroke:#c53030,color:#fff
class v2 current
class soft safeOp
class mixed mixedOp
class hard dangerOp
命令移动 HEAD?更新 Index?更新工作目录?你的改动去哪了?
--soft暂存区 (Staged)
--mixed工作目录 (Unstaged)
--hard被追踪文件的修改消失

如果你不小心 reset --hard 到了错误的提交,别慌!只要该提交曾经存在过,就可以通过 reflog 找回。

Terminal window
# 1. 查看 HEAD 的移动历史
git reflog
Terminal window
3a1b2c (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
9d8e7f HEAD@{1}: commit: feat: add user auth
...
Terminal window
# 2. 找到你想恢复的提交哈希(如 9d8e7f)
# 3. 恢复到该提交
git reset --hard 9d8e7f
  • 想撤销 commit 但保留代码继续提交?用 --soft
  • 想撤销 commit 和 add,把代码打回原形继续修改?用 --mixed(默认)。
  • 当前改动太乱想彻底重来?用 --hard慎用)。

理解了“三棵树”,你就掌握了 Git 时间旅行的钥匙。