reset

0 阅读4分钟

回退操作,本质就是:

移动指针,或者创建“反向提交”。

你先要了解:

  • commit 是不可变的
  • 分支是指针
  • HEAD 指向当前分支

先说结论:Git 有 3 种常见“回退”方式:分别是reset(重置)、revert(回滚)和restore(复原)

场景命令是否改历史
回退提交(危险)git reset
安全撤销提交git revert不改历史
撤销工作区修改git restore不改历史

一个个讲清楚。

这篇先说reset

reset 本质是:

移动历史,而不是移动代码。

代码不动
所以不会冲突。

总结:

❌ 不会改 commit 结构
❌ 不会改 parent
❌ 不会断开 C → B

✅ 只会移动分支指针

1.真正的“回退历史”

假设当前:

A --- B --- C --- D   (master)

你想回到 B。

最常见:回退到某个 commit

git reset --hard B

结果:

A --- B   (master)----C --- D   (孤儿状态,没有被分支引用)

master 指针被移动回 B。

C、D 还在仓库里,但没有分支指向,C 的 parent 仍然是 B ,只是 master 分支不再指向 C 了

reset分三种:

1)强reset

git reset --hard B
  • 移动 HEAD 指针
  • 重置暂存区
  • 重置工作目录(危险!会丢失未提交的更改)

即完全恢复到指定的快照状态

2)软reset

git reset --soft B

效果:

  • 分支回到 B
  • C、D 的修改进入“暂存区”

这是很多人最容易搞混的地方

关键点在于理解 Git 的 三层结构

工作区 (Working Directory)
暂存区 (Index / Staging Area)
提交历史 (HEAD / Commit)

假设历史是:

A --- B --- C --- D   (master, HEAD)

现在:

  • CD 是两个已经提交的 commit

同时你又有:

  • 工作区修改
  • 暂存区修改

状态可能是:

commit: A --- B --- C --- D (HEAD)

暂存区:
  file2 修改

工作区:
  file3 修改

执行

git reset --soft B

结果会变成:

A --- B   (master, HEAD)
      C --- D

但:

commit C、D 的改动会进入暂存区

因为 HEAD 从 D 回到了 B。

所以 Git 会认为:

B → D 之间的改动现在是“未提交状态”。

暂存区:
C 的修改 + D 的修改 + 原来就有的暂存修改

工作区:
原来就有的工作区修改

2.历史仍然记得他们

看到这里,我们暂时不管第三种reset,不管是强reset还是软reset

C和D这两个commit是不是就被删了?

其实,commit 对象仍然存在于 .git/objects

  • C 的 parent 仍然是 B

  • D 的 parent 仍然是 C

只是分支指针不在他们头上了,当后续出现新的提交,因为 Git 的 log 默认只沿着 当前分支向上走

所以看起来,就消失了

但 Git 仍然记得它们

Git 有一个非常重要的机制:

reflog

查看:

git reflog

你会看到类似:

D HEAD@{1}: commit
B HEAD@{0}: reset --soft

这说明:

Git 仍然记得 HEAD 曾经在 D。

所以你可以直接恢复:

git reset --hard D

历史立刻回来:

A --- B --- C --- D   (master)

什么时候 C、D 才会真正消失?

只有当满足两个条件:

1️⃣ 没有任何引用指向它们
(branch、tag、reflog)

2️⃣ Git 垃圾回收执行

git gc

默认情况下:

  • reflog 会保存 90 天
  • unreachable commit 也会保存一段时间

所以:

你有很长时间可以找回。

3.为什么 git reset --mixed 是默认模式?

reset 三种模式的区别

命令HEAD暂存区工作区
--soft移动不变不变
--mixed (默认)移动重置不变
--hard移动重置重置

因为 它最符合开发者最常见的需求

撤销 commit,但保留代码修改。

开发中最常见的错误是:

  • commit 太早
  • commit message 写错
  • 多提交了一次
  • add 错文件

例如:

git commit -m "fix bug"

结果发现:

  • message 写错了
  • **代码还没写完**

你希望:

撤销 commit
但代码还在
并且不要在暂存区

这就是 --mixed 的效果。

举个例子:

假设你刚提交了C:

A --- B --- C (HEAD)

但你发现:

  • commit message 不好
  • 代码还需要再改

执行:

git reset HEAD~1

(默认就是 --mixed

结果:

A --- B (HEAD)
      \
       C

但是代码仍然在 工作区

git status

会看到:

modified: file1
modified: file2

你可以:

继续修改代码
重新 add
重新 commit

非常符合开发习惯。

比起soft:

很多时候开发者其实希望:

重新选择哪些文件 add

所以 --mixed 更灵活。