要点速览
-
git reset会将分支指针向后移动,从历史中移除提交。适用于推送前的本地清理。 -
git reset --soft保留更改在暂存区,--mixed(默认)将其移出暂存区,--hard则完全丢弃。 -
git revert会创建一个新提交来撤销先前的提交。适用于已推送到共享分支的更改。 -
切勿在共享分支上使用
git reset。如果已经推送,请改用git revert。 -
如果不小心运行了
git reset --hard,可查看git reflog来找回丢失的提交。
什么是 Git?
Git 是 Linus Torvalds 于 2005 年创建的开源版本控制工具。它主要用于高效跟踪项目文件的变更,使项目成员能记录所有更改并在不丢失重要信息的情况下合并变更。我们的
此外,Introduction to GitHub Concepts 课程涵盖了 GitHub 的关键功能、界面导航以及顺利完成日常协作任务的方法。
Git revert 和 Git reset 都用于在 Git 中撤销项目代码及历史的更改,但方式不同。在开始之前,让我们先创建一个 Git 项目。
创建一个 Git 项目
以下是创建一个 git 项目并使用一些文件进行初始化的步骤:
mkdir git_revert_reset
cd git_revert_reset
git init .
-
mkdir git_revert_reset会创建名为git_revert_reset的文件夹。 -
cd git_revert_reset进入该文件夹。 -
git init .将该文件夹初始化为 git 仓库。
假设这是一个数据科学项目,我们可以使用 touch 命令创建用于数据获取、数据预处理和模型训练的以下文件:
touch data_acquisition.py data_preprocessing.py model_training.py
这是项目文件夹的最终内容。
git_revert_reset
|---------- data_acquisition.py
|---------- data_preprocessing.py
|---------- model_training.py
Git Reset

Git Reset 示意图(来源)
git reset 命令用于撤销工作区中的更改,并回到某个特定提交,同时丢弃该提交之后的所有提交。
例如,假设您进行了十次提交。对第一个提交使用 git reset 会移除随后的九个提交,将您带回到第一次提交的状态。
在使用 git reset 之前,务必要考虑您计划进行的更改类型;否则可能适得其反。
git reset 可搭配多种选项,但以下是主要的三种,分别适用于不同情境:git reset --soft、git reset --mixed 和 git reset --hard
git reset --soft
--soft 的目的是将 HEAD(您本地机器上最后一次提交的位置)引用指向某个特定提交。比如,如果我们发现忘记将一个文件加入提交,可以用 --soft 回退,格式如下:
-
git reset --soft HEAD~n回退到相对引用为 n 的提交。git reset --soft HEAD~1回到上一次提交。 -
git reset --soft <commit ID>回到指定<commit ID>的位置。
来看一些示例。
git add data_acquisition.py data_preprocessing.py
git commit -m "added data acquisition and preprocessing scripts"
上述命令的结果如下。
[master (root-commit) faf864e] added data acquisition and preprocessing scripts
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 data_acquisition.py
create mode 100644 data_preprocessing.py
我们可以如下查看提交状态:
git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
model_training.py
nothing added to commit but untracked files present (use "git add" to track)
但是,我们忘了把 model_training.py 脚本加入此次提交。
由于上一次提交位于 HEAD,我们可以通过运行以下三条命令来修复:
-
使用
git reset --soft HEAD~1撤销上一次提交,但保留更改在暂存区。 -
用
git add添加遗漏的文件。 -
使用
git commit完成最终提交。
git reset --soft HEAD~1
git add model_training.py
git commit -m "added all the python scripts"
完成上述步骤后,git reset 到 master 的结果如下:
[master (root-commit) faf864e] added all the python scripts
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 data_acquisition.py
create mode 100644 data_preprocessing.py
create mode 100644 model_training.py
关于 soft 重置的更深入讲解,请参阅我们的 Git Reset Soft: 完整指南。如果您只需修改上一条提交信息或补加遗漏文件,git commit --amend 往往是比重置更快捷的替代方案。
git reset --mixed
这是 git reset 的默认参数。运行该命令有两点影响:(1)撤销所有提交;(2)将它们移出暂存区。设想我们不小心把 model_training.py 文件也加入了提交,而我们想移除它,因为模型训练尚未完成。操作如下:
- 使用 git reset HEAD 将提交中的文件移出暂存区
- 仅将需要的文件加入此次提交
git reset HEAD
git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
model_training.py
data_acquisition.py
data_preprocessing.py
nothing added to commit but untracked files present (use "git add" to track)
现在我们只需把后两个文件加入提交并进行提交即可。
git add data_acquisition.py data_preprocessing.py
git commit -m "Removed the model_training.py from the commit"
git reset --hard
此选项可能很危险,请谨慎使用!
基本上,对某个特定提交执行 hard 重置会强制将 HEAD 回到该提交,并删除其后的所有内容。
在运行 hard 重置之前,先看一下文件夹的内容。
ls
data_acquisition.py data_preprocessing.py model_training.py
git reset --hard
HEAD is now at 97159bc added data acquisition and preprocessing scripts
现在再查看文件夹内容:
ls
data_acquisition.py data_preprocessing.py
可以看到未跟踪文件 model_training.py 被删除了。再次提醒,请务必理解您使用 reset 语句在做什么,因为后果可能非常严重!
如果您不小心执行了 hard 重置,git reflog 是您的安全网。Git 会在大约 30 天内保留所有引用变更的日志,因此通常可以通过运行 git reflog,然后执行 git reset --hard HEAD@{n} 回到重置前的状态,从而找回“丢失”的提交。请注意,未提交或未暂存的更改无法通过这种方式恢复。
Git Revert

Revert 前后对比(来源)
Git revert 与 git reset 类似,但处理方式略有不同。它不会一路移除提交,而是仅撤销单个提交,将您带回到该提交前的已暂存文件状态。
因此,git revert 并不是删除一个提交,而是通过创建一个包含相反更改的新提交来反转原提交引入的改动。这是一种安全的撤销方式,因为它不会丢失您的历史记录。
需要注意的是,revert 只有在指定提交哈希或引用时才会生效。
让我们来看看几个常见的 git revert 选项。
git revert --no-edit <commit ID>
--no-edit 选项允许您不更改要撤销的那个提交所使用的信息,Git 将直接使用默认的回滚信息,并把 revert 的变更提交到 master 分支。
为演示这一场景,我们先创建两个新文件。
touch model_monitoring.py data_monitoring.py
创建文件后,需要将它们加入到修改版本的暂存区。
git add model_monitoring.py
继续之前,先通过以下命令查看状态。
git status
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: data_monitoring.py
new file: model_monitoring.py
可以看到两个文件都已添加,现在可以提交更改了。
git commit -m "Added the monitoring script files"
[master (root-commit) eae84e7] Added the monitoring script files
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 data_monitoring.py
create mode 100644 model_monitoring.py
接着,可以通过以下命令生成提交哈希。当需要针对这个特定提交执行操作时很有用。
git reflog
eae84e7 (HEAD -> master) HEAD@{0}: commit (initial): Added the monitoring script files
现在,假设上一个提交并非有意而为,我们想要撤销它。
git revert --no-edit eae84e7
[master c61ef6b] Revert "Added the monitoring script files"
Date: Sat Nov 12 20:14:17 2022 -0600
2 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 data_monitoring.py
delete mode 100644 model_monitoring.py
来看一下提交日志:
git log
commit c61ef6b4e86f41f47c8c77de3b5fca3945a7c075 (HEAD -> master)
Author: Zoumana Keita <[email protected]>
Date: Sat Nov 12 20:14:17 2022 -0600
Revert "Added the monitoring script files"
This reverts commit eae84e7669af733ee6c1b854f2fcd9acfea9d4a3.
commit eae84e7669af733ee6c1b854f2fcd9acfea9d4a3
Author: Zoumana Keita <[email protected]>
Date: Sat Nov 12 20:05:57 2022 -0600
Added the monitoring script files
从日志可以看到,创建了一个新的提交 ID c61ef6b4e86f41f47c8c77de3b5fca3945a7c075 来反转原始提交 eae84e7669af733ee6c1b854f2fcd9acfea9d4a3。
在此示例中,由于只有一个提交,我们也可以使用 git revert HEAD,效果相同。更多示例参见我们的 回滚上一条提交指南。
git revert <commit hash>
这只会移除与该提交哈希相关的更改,不会影响其他提交。
考虑到前面生成的 ID,您认为哪个 git revert 会生效?
如果您的答案是 c61ef6b4e86f41f47c8c77de3b5fca3945a7c075,那么正确。针对 eae84e7669af733ee6c1b854f2fcd9acfea9d4a3 将不会生效,因为它已被新的提交替代。
我们来看看执行 revert 时会发生什么。
git revert c61ef6b4e86f41f47c8c77de3b5fca3945a7c075
[master aaead40] Revert "Revert "Added the monitoring script files""
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 data_monitoring.py
create mode 100644 model_monitoring.py
如您所见,最初创建并提交的两个文件又回来了!
git revert --no-commit <commit ID>
使用 --no-commit 或 -n 选项的目的是阻止 Git 在回滚后自动提交。如果您希望在提交前先审查由 revert 语句带来的更改,这将十分有用。
来看下面的示例:
git revert --no-commit c61ef6b4e86f41f47c8c77de3b5fca3945a7c075
这将撤销提交 ID 为 c61ef6b4e86f41f47c8c77de3b5fca3945a7c075 所做的更改,但不会自动创建记录该回滚的新提交。相反,它会将反向更改暂存,便于您审查并决定是否手动提交回滚。
Git Revert 与 Reset 对比
git reset 与 git revert 都能撤销更改,但工作方式不同,适用场景也不同。
| 对比维度 | git reset | git revert |
|---|---|---|
| 工作原理 | 将分支指针向后移动,从历史中丢弃提交 | 创建一个新提交,反转先前提交的更改 |
| 提交历史 | 重写历史(提交被移除) | 保留完整历史(新增一个回滚提交) |
| 适用于共享分支? | 否(协作者若已拉取被移除的提交将产生冲突) | 是(所有协作者的历史一致) |
| 作用范围 | 可一次撤销多个提交 | 一次撤销一个提交(使用 --no-commit 可批量) |
| 最佳用例 | 推送前清理本地或私有分支 | 撤销已推送到远端的更改 |
| 风险级别 | 高,搭配 --hard 时(会永久删除未提交的工作) | 低(不会丢失任何数据) |
经验法则:使用 git reset 清理未推送的本地历史;使用 git revert 安全撤销他人可能已拉取的任何更改。关于回滚合并提交的细节,请参阅我们的 Git Revert 合并提交 指南。
结论
要点:对尚未推送的本地历史使用 git reset 清理;对共享分支上的更改使用 git revert 安全撤销。尤其要小心 git reset --hard,它会永久移除工作目录中未提交的更改。
要继续提升您的 Git 技能,请查看以下相关 DataCamp 教程:
Git Reset 与 Revert 常见问答
git reset 和 git revert 有何区别?
git reset 通过将分支指针移动到较早的提交来撤销本地仓库中的提交,从而丢弃该时间点之后的提交。而 git revert 通过创建一个新提交来撤销指定提交所做的更改。
是否应该使用 git reset hard?
使用 git reset --hard 时要格外谨慎,因为它会完全销毁更改并将其从本地目录移除。使用前请 100% 确认自己在做什么。
何时应使用 git reset?
当您想撤销尚未推送到远程仓库的提交时,git reset 很有用。它可用于移除包含错误或已不再需要的提交。
需要注意的是,git reset 是本地操作,不会影响远程仓库。如果您已将想要撤销的提交推送到了远程,请改用 git revert。
何时应使用 git revert?
当您想撤销已经推送到远程仓库的提交时,git revert 很有用。它是安全的选择,因为不会销毁任何提交,并会在仓库中留下清晰的变更记录。
需要注意的是,git revert 只能撤销尚未合并进其他分支的提交。如果您想撤销已并入分支的提交,应使用 git reset 或其他方法来移除这些提交。
什么是 git reset?
git reset 用于撤销提交历史或工作目录中的更改。它允许您将 HEAD 重置到指定提交,并可选地修改索引(暂存区)与工作目录。
git reset 中的 --soft、--mixed 与 --hard 有何不同?
--soft:将 HEAD 移动到指定提交,但保留更改在暂存区。--mixed(默认):将 HEAD 移动到指定提交并将更改移出暂存区(保留工作目录不变)。--hard:将 HEAD 移动到指定提交并丢弃索引与工作目录中的所有更改。
Git 起初是为软件开发者打造的工具,但如今已成为所有代码工作者的标准。现在,数据科学家、数据工程师和机器学习工程师也在采用这些最佳实践。
在本教程中,我将讲解
git reset和git revert这两个用于撤销更改的 Git 命令之间的关键差异。我会通过实际示例演示每种模式的用法,从初始化项目到回滚特定提交。