Git是现代软件开发中不可或缺的版本控制系统。在日常工作中,我们难免会犯错或需要回溯历史。Git提供了多种“撤销”或“回退”机制,其中最核心且最容易混淆的是 git reset、git revert 和 git checkout(或 git restore)。理解它们的区别和适用场景,是成为Git高手的关键。
1. git reset:修改历史(危险操作)
git reset 的主要作用是移动 HEAD 和当前分支的指针,从而改变分支指向的历史提交。它本质上是重写历史,因此不应该用于已经推送到公共仓库的提交。
适用场景
- 撤销本地尚未推送的提交。
- 撤销暂存区(Staging Area)的文件。
核心参数
- –soft: 仅移动 HEAD 和分支指针。工作目录和暂存区保持不变。提交会被“解开”,变为暂存状态。
- –mixed (默认): 移动 HEAD 和分支指针,同时重置暂存区。工作目录保持不变。提交会被“解开”,变为未暂存状态。
- –hard: 移动 HEAD 和分支指针,重置暂存区,并清空工作目录。这是最具破坏性的操作,所有未提交的本地修改都会丢失。
示例:彻底删除最近两次本地提交
假设我们想回退到 HEAD 前两个提交,并彻底清除工作区和暂存区中的相应文件。
# 确认历史记录
git log --oneline -3
# 执行强硬回退
git reset --hard HEAD~2
# 此时,最新的两次提交在历史中消失了(只存在于reflog中)
git log --oneline
2. git revert:创建新的历史(安全操作)
git revert 不会删除历史提交,而是创建一个新的提交,这个新提交的内容是目标提交的反向操作。如果目标提交是新增一行代码,revert 提交就是删除那一行代码。
适用场景
- 撤销已经推送到公共仓库的提交。
- 需要保留完整的历史记录,例如在代码审查或审计场景下。
示例:撤销指定的提交
假设我们要撤销提交 ID 为 abc1234 的修改。
# 查看提交历史,找到目标 commit ID
git log --oneline
# 撤销该提交,Git 会自动打开编辑器让你编辑新提交的提交信息
git revert abc1234
# 此时历史记录中多了一个新的提交,名为 "Revert 'abc1234 commit message'"
git log --oneline
3. git checkout:切换与丢弃
在较老的 Git 版本中,git checkout 承担了多重职责:切换分支、创建分支,以及丢弃文件修改。
注意: Git 2.23+ 推荐使用 git switch 来切换分支,使用 git restore 来恢复文件。但在当前语境下,我们聚焦于 checkout 丢弃修改的功能。
适用场景
- 丢弃工作目录中针对某个文件的所有未暂存的本地修改。
- 将工作区恢复到某个特定提交的状态(通常用于查看历史,进入“分离 HEAD”状态)。
示例:丢弃某个文件的本地修改
假设我们在 config.js 文件中做了修改,但还没有添加到暂存区,现在想放弃这些修改。
# 查看当前状态,确认 config.js 被修改
git status
# 丢弃 config.js 文件中的所有本地修改,使其回到上一次提交的状态
git checkout -- config.js
# 或者使用更现代的命令 (Git 2.23+)
git restore config.js
深度对比总结表
| 特性 | git reset | git revert | git checkout / restore |
|---|---|---|---|
| 操作对象 | 分支指针和 HEAD | 创建新的提交 | 文件或工作区/暂存区 |
| 是否修改历史 | 是(重写历史) | 否(增加新的历史) | 否(仅影响本地工作区/暂存区) |
| 适用场景 | 仅本地未推送的修改 | 已推送的公共历史 | 丢弃本地未提交的修改 |
| 安全性 | 低(易丢失历史) | 高 | 中(仅影响本地文件) |
| 结果 | 目标提交消失 | 目标提交被抵消 | 文件恢复到指定状态 |
汤不热吧