Git Reset 和 Reflog

GitGitBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

欢迎来到 Git 时间旅行者的世界!今天,我们将探索两个强大的 Git 功能,它们将赋予你对仓库历史的空前控制力:git resetgit reflog。这些工具就像是 Git 时间机器的高级控制面板,允许你在项目的不同状态之间穿梭,甚至恢复“丢失”的工作。

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

在本实验中,我们将涵盖以下内容:

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

通过本实验,你将深入理解如何安全有效地使用这些强大的 Git 功能。你将能够自信地操作仓库的历史,并知道在需要时总能找到回退的方法。

让我们开始掌握 git resetreflog 吧!


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL git(("`Git`")) -.-> git/SetupandConfigGroup(["`Setup and Config`"]) git(("`Git`")) -.-> git/BasicOperationsGroup(["`Basic Operations`"]) git(("`Git`")) -.-> git/DataManagementGroup(["`Data Management`"]) git(("`Git`")) -.-> git/BranchManagementGroup(["`Branch Management`"]) git/SetupandConfigGroup -.-> git/init("`Initialize Repo`") git/BasicOperationsGroup -.-> git/status("`Check Status`") git/BasicOperationsGroup -.-> git/commit("`Create Commit`") git/DataManagementGroup -.-> git/reset("`Undo Changes`") git/BranchManagementGroup -.-> git/log("`Show Commits`") git/BranchManagementGroup -.-> git/reflog("`Log Ref Changes`") subgraph Lab Skills git/init -.-> lab-387491{{"`Git Reset 和 Reflog`"}} git/status -.-> lab-387491{{"`Git Reset 和 Reflog`"}} git/commit -.-> lab-387491{{"`Git Reset 和 Reflog`"}} git/reset -.-> lab-387491{{"`Git Reset 和 Reflog`"}} git/log -.-> lab-387491{{"`Git Reset 和 Reflog`"}} git/reflog -.-> lab-387491{{"`Git Reset 和 Reflog`"}} end

设置你的工作区

在我们开始重置和 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. 最后,我们添加了乘法函数并提交了它。

现在,我们有了一个包含一些历史的仓库,可以用来进行实验了!

软重置(Soft Reset):移动 HEAD

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

让我们尝试一个软重置:

git reset --soft HEAD~1

这条命令将 HEAD 向后移动一个提交。~1 表示“当前提交的前一个提交”。你可以使用 ~2~3 等来向后移动多个提交。

现在,如果你运行 git status,你会看到最后一次提交的更改已被暂存,并准备好再次提交。工作目录中的文件没有发生任何变化。

这在你想将最后几次提交“压缩”(squash)为一次提交时非常有用。你可以软重置回几个提交,然后使用所有这些更改进行一次新的提交。

让我们用一条新的提交信息再次提交这些更改:

git commit -m "Add subtraction and multiplication functions"

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

混合重置(Mixed Reset):取消暂存更改

接下来我们将探讨的另一种重置类型是“混合重置”(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):丢弃更改

第三种也是最彻底的重置类型是“硬重置”(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 这样的历史重写操作。

让我们查看 reflog:

git reflog

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

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

git reset --hard HEAD@{1}

这会重置到你上次操作(即硬重置)之前的状态。

现在检查 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; }

Reflog 是一个强大的安全网,允许你从几乎任何 Git 事故中恢复。然而,请记住它是本地且临时的(条目通常保留 30 到 90 天)。它不能替代定期备份或将工作推送到远程仓库的操作。

基于时间的重置

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

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

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

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

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

请记住,如果基于时间的重置没有达到你预期的结果,你随时可以使用 reflog 来撤销它。

总结

恭喜你,Git 时间领主!你已经掌握了一些 Git 最强大但也最具潜在危险性的命令。让我们回顾一下我们涵盖的关键概念:

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

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

在你继续 Git 之旅时,请在一个安全的环境中练习这些命令,直到你熟练掌握它们。如果正确使用,这些强大的工具可以极大地提升你的 Git 工作流程。

祝你重置愉快,愿你的 Git 历史始终清晰且有意义!

您可能感兴趣的其他 Git 教程