[git] 浅解 git reset 命令

背景

相信不少朋友在使用 git \text{git} git 时,都对以下三种 git reset \text{git reset} git reset 命令的区别产生过疑惑。

bash 复制代码
git reset --soft <commit>
git reset --mixed <commit> (和 git reset <commit> 效果相同)
git reset --hard <commit>

本文会探讨它们的区别。

要点

如果将 HEAD \text{HEAD} HEAD/暂存区/工作目录 都看作树 🌲(即,文件的集合),在不同的 git reset \text{git reset} git reset 命令中,对应的树是否会被更新可以这样总结 ⬇️

HEAD \text{HEAD} HEAD 🌲 是否会被更新 暂存区( Index \text{Index} Index) 🌲 是否会被更新 工作目录 🌲 是否会被更新
git reset --soft <commit>
git reset --mixed <commit>git reset <commit>
git reset --hard <commit>

正文

参考 7.7 Git 工具 - 重置揭密 一文的描述,我们可以将以下三者想象成三棵树 🌲(这里的树 🌲 是指文件的集合,而不是指数据结构中的树)

  • HEAD \text{HEAD} HEAD (可以将它简单理解为当前分支的最近一次 commit \text{commit} commit 的指针或者引用)🌲
  • 暂存区(即, Index \text{Index} Index)🌲
  • 工作目录 🌲

代码

我们可以用代码验证不同选项的 git reset \text{git reset} git reset 命令的效果。请将以下代码保存为 main.sh \text{main.sh} main.sh

bash 复制代码
## Prepare a temp dir for our test
TEST_DIR=temp_git_test_dir
if [[ -d "${TEST_DIR}" ]];
then
    rm -rf "${TEST_DIR}"
fi

mkdir "${TEST_DIR}" 
cd "${TEST_DIR}" 

git init

function append() {
    echo "$1" >> f.txt
}

function add_and_commit() {
    git add f.txt
    git commit -m "$1"
}

## Prepare for the 1st commit 
append "1st line"
add_and_commit "1st commit" 

## Prepare for the 2nd commit
append "2nd line"
add_and_commit "2nd commit"

## Prepare for the 3rd commit
append "3rd line"
add_and_commit "3rd commit"

## Prepare for the 4th commit
append "4th line"
add_and_commit "4th commit"

通过执行下方的命令就可以运行 main.sh \text{main.sh} main.sh ⬇️

bash 复制代码
bash main.sh

使用 --soft \text{{-}{-}soft} --soft 选项

我们把 HEAD \text{HEAD} HEAD 视为一个指针或者引用,那么 git reset --soft <commit> 命令的作用就是 ⬇️

  • HEAD \text{HEAD} HEAD 指向 <commit> 所对应的位置

运行完 main.sh \text{main.sh} main.sh 之后,当前目录中会多出一个 temp_git_test_dir 目录。在当前目录执行 tree temp_git_test_dir 命令后,应该可以看到如下的内容

text 复制代码
temp_git_test_dir
└── f.txt

1 directory, 1 file

通过执行 cd temp_git_test_dir 命令就可以切换到 temp_git_test_dir 目录。不难验证,现在 HEAD \text{HEAD} HEAD 的位置如下图所示 ⬇️ (下图是借助 Visual Studio Code 而生成的)

通过查看 git status \text{git status} git status 命令的执行结果,可以知道以下三棵树 🌲 当前的内容是一样的 ⬇️

  • 工作目录 🌲
  • 暂存区(即, Index \text{Index} Index)🌲
  • HEAD \text{HEAD} HEAD 🌲

现在我们执行以下命令 ⬇️(HEAD~2 表示当前 HEAD \text{HEAD} HEAD 的上一个 commit \text{commit} commit 的上一个 commit \text{commit} commit)

bash 复制代码
git reset --soft HEAD~2

执行命令后, HEAD \text{HEAD} HEAD 会有对应的变化 ⬇️

通过查看 git status \text{git status} git status 命令的执行结果,可以知道 工作目录暂存区 的内容是一样的 ⬇️

小结

git reset --soft <commit> 会将 HEAD \text{HEAD} HEAD 指向 <commit> 所对应的 commit \text{commit} commit。但是 暂存区工作目录 的内容都不受影响。

哪棵树 🌲 执行 git reset --soft <commit> 命令后 有变化吗? 会变成什么?
HEAD \text{HEAD} HEAD 🌲 变为指向 <commit> 对应的 commit \text{commit} commit
暂存区(即, Index \text{Index} Index)🌲 没有 -
工作目录 🌲 没有 -

使用 --mixed \text{{-}{-}mixed} --mixed 选项

说明: git reset <commit>git reset --mixed <commit> 的作用相同。

我们把 HEAD \text{HEAD} HEAD 视为一个指针或者引用,那么 git reset --mixed <commit> 命令的作用是

  • HEAD \text{HEAD} HEAD 指向 <commit> 所对应的位置
  • 用更新后的 HEAD \text{HEAD} HEAD 来更新 暂存区 (暂存区的内容会和最新的 HEAD \text{HEAD} HEAD 相同)

运行完 main.sh \text{main.sh} main.sh 之后,当前目录中会多出一个 temp_git_test_dir 目录。在当前目录执行 tree temp_git_test_dir 命令后,应该可以看到如下的内容

text 复制代码
temp_git_test_dir
└── f.txt

1 directory, 1 file

通过执行 cd temp_git_test_dir 命令就可以切换到 temp_git_test_dir 目录。不难验证,现在 HEAD \text{HEAD} HEAD 的位置如下图所示 ⬇️ (下图是借助 Visual Studio Code 而生成的)

通过查看 git status \text{git status} git status 命令的执行结果,可以知道以下三棵树 🌲 当前的内容是一样的 ⬇️

  • 工作目录 🌲
  • 暂存区(即, Index \text{Index} Index)🌲
  • HEAD \text{HEAD} HEAD 🌲

现在我们执行以下命令 ⬇️(HEAD~2 表示当前 HEAD \text{HEAD} HEAD 的上一个 commit \text{commit} commit 的上一个 commit \text{commit} commit)

bash 复制代码
git reset --mixed HEAD~2

执行命令后, HEAD \text{HEAD} HEAD 会有对应的变化 ⬇️

通过查看 git status \text{git status} git status 命令的执行结果,可以知道 HEAD \text{HEAD} HEAD 和 暂存区 的内容是一样的 (但是工作目录的内容和它们不同) ⬇️

小结

git reset --mixed <commit> 会将 HEAD \text{HEAD} HEAD 指向 <commit> 所对应的 commit \text{commit} commit,并让 暂存区 的内容与最新的 HEAD \text{HEAD} HEAD 相同。工作目录 的内容不受影响。

哪棵树 🌲 执行 git reset --mixed <commit> 命令后 有变化吗? 会变成什么?
HEAD \text{HEAD} HEAD 🌲 变为指向 <commit> 对应的 commit \text{commit} commit
暂存区(即, Index \text{Index} Index)🌲 内容会和最新的 HEAD \text{HEAD} HEAD 相同
工作目录 🌲 没有 -

使用 --hard \text{{-}{-}hard} --hard 选项

我们把 HEAD \text{HEAD} HEAD 视为一个指针或者引用,那么 git reset --hard <commit> 命令的作用是

  • HEAD \text{HEAD} HEAD 指向 <commit> 所对应的位置
  • 用更新后的 HEAD \text{HEAD} HEAD 来更新 暂存区 (暂存区的内容会和最新的 HEAD \text{HEAD} HEAD 相同)
  • 用更新后的 HEAD \text{HEAD} HEAD 来更新工作目录(工作目录中,所有已被追踪的文件,其内容会和最新的 HEAD \text{HEAD} HEAD 中的内容相同)

运行完 main.sh \text{main.sh} main.sh 之后,当前目录中会多出一个 temp_git_test_dir 目录。在当前目录执行 tree temp_git_test_dir 命令后,应该可以看到如下的内容

text 复制代码
temp_git_test_dir
└── f.txt

1 directory, 1 file

通过执行 cd temp_git_test_dir 命令就可以切换到 temp_git_test_dir 目录。不难验证,现在 HEAD \text{HEAD} HEAD 的位置如下图所示 ⬇️ (下图是借助 Visual Studio Code 而生成的)

通过查看 git status \text{git status} git status 命令的执行结果,可以知道以下三棵树 🌲 当前的内容是一样的 ⬇️

  • 工作目录 🌲
  • 暂存区(即, Index \text{Index} Index)🌲
  • HEAD \text{HEAD} HEAD 🌲

现在我们执行以下命令 ⬇️(HEAD~2 表示当前 HEAD \text{HEAD} HEAD 的上一个 commit \text{commit} commit 的上一个 commit \text{commit} commit)

bash 复制代码
git reset --hard HEAD~2

执行命令后, HEAD \text{HEAD} HEAD 会有对应的变化 ⬇️

通过查看 git status \text{git status} git status 命令的执行结果,可以知道以下三者的内容相同

  • HEAD \text{HEAD} HEAD
  • 暂存区
  • 工作目录

小结

git reset --hard <commit> 会将 HEAD \text{HEAD} HEAD 指向 <commit> 所对应的 commit \text{commit} commit,并让 暂存区 的内容与最新的 HEAD \text{HEAD} HEAD 相同。工作目录 中已被追踪的文件,也会变得和最新的 HEAD \text{HEAD} HEAD 相同。

哪棵树 🌲 执行 git reset --hard <commit> 命令后 有变化吗? 会变成什么?
HEAD \text{HEAD} HEAD 🌲 变为指向 <commit> 对应的 commit \text{commit} commit
暂存区(即, Index \text{Index} Index)🌲 内容会和最新的 HEAD \text{HEAD} HEAD 相同
工作目录 🌲 对所有已被追踪的文件而言,工作目录的内容和最新的 HEAD \text{HEAD} HEAD 相同

参考资料

关于 git reset \text{git reset} git reset 命令的更多介绍,可以参考以下两个链接(两个链接来自同一个网站,第一个链接的内容是简体中文,第二个链接的内容是英文)

相关推荐
葫芦和十三7 小时前
图解 MongoDB 30|查询路由与广播查询:带片键定向,不带就广播
后端·mongodb·agent
葫芦和十三7 小时前
图解 MongoDB 29|分片键陷阱:jumbo chunk、孤儿文档和写入热点
后端·mongodb·agent
进击的程序猿~7 小时前
Go Zero源码阅读1
后端·golang
2601_961593428 小时前
Rust 开发环境配置繁琐?RustRover 开箱即用搞定编码调试
开发语言·后端·macos·rust
犀利豆11 小时前
AI in Harness(三)
人工智能·后端
IT_陈寒11 小时前
Java的Stream.parallel()把我CPU跑爆了,这种优化要谨慎
前端·人工智能·后端
子兮曰12 小时前
Bun 重写为 Rust:11天、64个Claude、16.5万美元,一次改变行业认知的激进实验
前端·后端·bun
子兮曰12 小时前
Node.js v26.5.0 深度解读:import Text、流式 ReadableStreamTee、以及隐藏的安全加固浪潮
前端·后端·node.js
子兮曰12 小时前
TypeScript 7.0 RC 深度解读:Go 重写完成,10 倍提速,编译器的「奇点时刻」
前端·后端·typescript
kosan12 小时前
.NET/C# 实战:我如何把登录验证码从“裸奔”改到“铜墙铁壁”
后端