Git Reset 与 Reflog 详解

GitBeginner
立即练习

介绍

欢迎,Git 时空旅行者!今天,我们将探索两个强大的 Git 功能,它们将赋予你对仓库历史前所未有的控制力:git resetgit reflog。这些工具就像是 Git 时光机的高级控制面板,允许你在项目的不同状态之间切换,甚至能找回「丢失」的工作。

git reset 命令是一个多功能工具,可以帮助你撤销更改、取消暂存文件,甚至重写提交历史。然而,能力越大责任越大,git reset 对初学者来说可能有点令人生畏。这就是 git reflog 大显身手的地方——它就像一张安全网,记录了你对仓库引用(如分支顶端)所做的所有更改,让你即使在执行了最彻底的重置操作后也能恢复数据。

在本实验中,我们将涵盖:

  1. 软重置(Soft Reset):移动 HEAD 指针,但不改变工作目录或暂存区。
  2. 混合重置(Mixed Reset):取消暂存更改,同时保留工作目录中的修改。
  3. 硬重置(Hard Reset):彻底丢弃更改。
  4. 使用 Reflog 从「破坏性」操作中恢复。
  5. 基于时间的重置:将仓库移动到特定时间点的状态。

完成本实验后,你将深入理解如何安全有效地使用这些强大的 Git 功能。你将能够自信地操作仓库历史,因为你知道在需要时总能找到回来的路。

让我们开始深入学习并掌握 git resetreflog 吧!

这是一个引导式实验,提供逐步指导以帮助你学习和练习。请仔细遵循说明完成每个步骤并获得实践经验。历史数据表明,这是一个初学者级别的实验,完成率为 98%。它获得了学习者 100% 的好评率。

设置工作区

在开始重置和查看引用日志(reflog)之前,我们先设置一个包含一些提交记录的工作区来进行实验。我们将创建一个新目录,初始化一个 Git 仓库,并添加一些带有多次提交的文件。

打开你的终端并输入以下命令:

cd ~/project
mkdir git-reset-lab
cd git-reset-lab
git init

现在,让我们创建一些文件并进行一系列提交,将以下命令复制并粘贴到你的终端中:

echo "## Git Reset and Reflog Lab" > README.md
git add README.md
git commit -m "Initial commit"

echo "function add(a, b) { return a + b; }" > math.js
git add math.js
git commit -m "Add addition function"

echo "function subtract(a, b) { return a - b; }" >> math.js
git add math.js
git commit -m "Add subtraction function"

echo "function multiply(a, b) { return a * b; }" >> math.js
git add math.js
git commit -m "Add multiplication function"

让我们回顾一下刚才的操作:

  1. 我们创建了一个 README 文件并进行了初始提交。
  2. 我们创建了一个包含加法函数的 JavaScript 文件并提交。
  3. 在同一个文件中添加了减法函数并提交。
  4. 最后,添加了乘法函数并提交。

现在我们有了一个拥有若干历史记录的仓库可以用来实验了!

软重置:移动 HEAD

我们要探索的第一种重置类型是「软」重置(soft reset)。软重置会将 HEAD(以及当前分支)移动到不同的提交,但它不会改变暂存区或工作目录。当你想要「撤销」一些提交,但保留所有更改以便重新进行一次新的提交时,这非常有用。

让我们尝试一次软重置:

git reset --soft HEAD~2

该命令将 HEAD 回退两个提交(回到「Add subtraction function」之前的那个提交)。~2 表示「当前 HEAD 之前的两个提交」。你可以使用 ~N 来回退 N 个提交。

现在,如果你运行 git status,你会发现来自「Add subtraction function」和「Add multiplication function」这两个提交的更改现在都处于已暂存状态。你工作目录中的文件没有变化。这两个提交中的所有工作现在都已准备好,可以作为一个单一的新提交重新提交。

这在你想将最后几次提交「压缩」(squash)成一个提交的场景中非常有用。你可以软重置回退几个提交,然后用所有这些更改创建一个新的提交。

让我们用一条新消息重新提交这些更改:

git commit -m "Add subtraction and multiplication functions"

请记住,虽然软重置通常是安全的(因为它不会丢弃任何更改),但它确实重写了历史。如果你已经推送了原始提交,则需要强制推送(force push)来更新远程分支,这可能会给协作者带来麻烦。在重写共享历史之前,务必与你的团队沟通!

混合重置:取消暂存更改

我们要看的下一种重置类型是「混合」重置(mixed reset)。如果你不指定任何标志,这实际上是 git reset 的默认模式。混合重置会移动 HEAD 并更新暂存区以匹配该提交,但它不会触动工作目录。

让我们做一些更改并暂存它们:

echo "function divide(a, b) { return a / b; }" >> math.js
git add math.js

现在,假设我们改变了主意,暂时不想暂存这个更改。我们可以使用混合重置:

git reset HEAD

这会取消暂存我们的更改,但将它们保留在工作目录中。如果你现在运行 git status,你会看到 math.js 已修改但未暂存。

当你暂存了一些更改,但随后决定还没准备好提交它们时,混合重置非常有用。也许你想再次检查更改,或者在暂存之前进行进一步的修改。

请记住,与软重置不同,混合重置确实会改变暂存区。然而,它仍然是安全的,因为它不会丢弃你的任何工作——所有内容仍然保留在你的工作目录中。

硬重置:丢弃更改

第三种也是最彻底的重置类型是「硬」重置(hard reset)。硬重置会移动 HEAD,更新暂存区,并且更新工作目录以匹配。这意味着它会丢弃自你重置到的那个提交以来的所有更改。

让我们尝试一次硬重置:

git add math.js
git commit -m "Add division function"
git status
git reset --hard HEAD~1

这会暂存并提交我们的除法函数,然后执行硬重置到前一个提交,实际上「撤销」了我们的最后一次提交并丢弃了更改。

如果你现在查看 math.js,你会发现除法函数不见了。就像我们从未写过它一样。

硬重置功能强大但很危险。当你想要完全丢弃某些工作并从之前的提交重新开始时,它非常有用。但是,使用时要非常小心,因为它会永久丢弃更改。

在执行硬重置之前,务必仔细检查你是否重置到了正确的提交。如果你不确定,使用软重置或混合重置会更安全,或者在实验前创建一个新分支。

使用 Reflog 恢复丢失的提交

现在,如果你意识到你并不想丢弃那个除法函数该怎么办?这就是 git reflog 大显身手的地方。引用日志(reflog)是本地仓库中 HEAD 经过的所有位置的记录。它就像一个超级历史记录,甚至记录了像 reset 这样重写历史的命令。

让我们查看引用日志:

git reflog

你应该会看到你最近采取的所有操作列表,包括你的重置操作。每个条目都有一个 HEAD@{n} 标识符。

要找回丢失的提交,你可以重置到硬重置之前的状态:

git reset --hard HEAD@{1}

这会重置到 HEAD 在你执行上一个动作(即硬重置)之前的状态。

现在检查 math.js,你应该会看到除法函数回来了!

cat math.js
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function multiply(a, b) { return a * b; }
function divide(a, b) { return a / b; }

引用日志是一个强大的安全网,允许你从几乎任何 Git 意外中恢复。但是,请记住它是你机器本地的,并且是临时的(条目通常保留 30 到 90 天)。它不能替代定期备份或将工作推送到远程仓库。

基于时间的重置

Git 还允许你将仓库重置到特定时间点的状态。如果你大概记得仓库处于你想要返回的状态的时间,这会非常有用。

让我们尝试一次基于时间的重置:

git reset --hard master@{"1 hour ago"}

这会将你的仓库重置到 1 小时前的状态。你可以使用各种时间描述词,如「yesterday」(昨天)、「2 days ago」(2 天前)、「3 minutes ago」(3 分钟前)等。

使用基于时间的重置时要小心,因为它们可能不如重置到特定提交那么精确。在基于时间的重置后,务必检查仓库的状态,以确保你到达了预期的位置。

记住,如果基于时间的重置没有给你预期的结果,你始终可以使用引用日志来撤销它。

总结

恭喜你,Git 时空领主!你刚刚掌握了 Git 中一些最强大且具有潜在危险的命令。让我们回顾一下我们涵盖的核心概念:

  1. 软重置(Soft Reset):移动 HEAD,但不改变暂存区或工作目录。适用于压缩提交。
  2. 混合重置(Mixed Reset):移动 HEAD 并更新暂存区,但不触动工作目录。非常适合取消暂存更改。
  3. 硬重置(Hard Reset):移动 HEAD,更新暂存区,并更新工作目录。功能强大但具有潜在的破坏性。
  4. 引用日志(Reflog):记录 HEAD 所有更改的安全网,允许你从几乎任何 Git 意外中恢复。
  5. 基于时间的重置:允许你将仓库重置到特定时间点的状态。

请记住,能力越大责任越大。虽然这些命令赋予了你对仓库历史惊人的控制力,但如果使用不当,它们也可能带来危险。在执行重置(尤其是硬重置)之前,务必反复检查,并记住如果出了问题,引用日志是你的好帮手。

在继续你的 Git 之旅时,请在安全的环境中练习这些命令,直到你熟练掌握。如果使用得当,它们是能够极大地增强你 Git 工作流的强大工具。

祝你重置愉快,愿你的 Git 历史永远整洁且有意义!