LabHub
学习 学习路径 课程

Git 实战

reset、revert、reflog 实操

在 LabHub 中继续学习

目标

分别在独立仓库中执行 reset 的三种模式、amend、revert 和 reflog,比较结果,并用速查表整理不同情境下应该使用什么。产物放在 /root/gitx2/ 下。

为什么重要

大多数撤销事故并不是因为不知道命令,而是因为不了解该命令会覆盖到什么范围就贸然执行。--soft 不会丢失任何内容;默认的 --mixed 只会丢失暂存状态;--hard 则会丢失所有尚未 commit 的变更。评估风险时不应看命令名称,而应判断“这条命令是否会覆盖未 commit 的变更”。此外还有第二个维度:如果某个 commit 已经存在于其他人的仓库中,任何删除该 commit 的方法都会破坏他人的历史。此时正确答案不是修改历史,而是向历史追加内容。本实验不靠文字说明,而是通过 commit 数量和文件是否存在来验证这一差异。

步骤

**没有全局 git 身份。**每次创建仓库时,都必须设置本地 user.email / user.name 才能 commit。

  1. 创建 /root/gitx2/mkrepo.sh 并执行 chmod +x。在第一个参数($1)指定的路径初始化仓库(不存在时先创建),分支为 main,并生成 5 个 commit。第 n 个 commit 添加 fn.txt(f1.txt … f5.txt),标题为 c1c5。评分器会在临时目录中执行此脚本进行检查,因此不要硬编码路径,必须使用 "$1"
  2. 用脚本在 /root/gitx2/amend 创建仓库,并且只把最后一个 commit 的标题改为 c5 fixed。commit 数量必须仍为 5 个;reflog 中必须留下 amend 记录;修改前的哈希与当前哈希必须不同。
  3. /root/gitx2/soft 创建仓库并撤销一个 commit。结果:4 个 commit;f5.txt 在索引中保持暂存状态;工作树中也存在 f5.txt
  4. /root/gitx2/mixed 创建仓库并撤销一个 commit。结果:4 个 commit;f5.txt 未被暂存,但必须仍保留在工作树中(在 git status --porcelain 中显示为 ?? M)。
  5. /root/gitx2/hard 创建仓库并撤销一个 commit。结果:4 个 commit;f5.txt 消失f4.txt 仍存在;工作树必须完全干净
  6. /root/gitx2/revert 创建仓库,且只抵消 c3 commit 的变更。结果:6 个 commit;存在标题以 Revert 开头的 commit;原来的 c3 commit 也完整保留在历史中f3.txt 消失,而 f1/f2/f4/f5.txt 保留。
  7. /root/gitx2/reflog 创建仓库。撤销之前,将当前 HEAD 哈希保存到 /root/gitx2/before.txt文件中只能有哈希字符串)。然后强制丢弃 2 个 commit,再查看引用日志并恢复到原位置。结果:HEAD 与先前记录的哈希相同;commit 数量为 5 个;reflog 中必须留下 reset 记录。
  8. /root/gitx2/undo.md 编写速查表。正文必须出现 --soft--mixed--hardrevertreflog,并且必须包含以下三行。
    • pushed= — 撤销已经推送且被他人拉取的 commit 时应使用的命令(一个单词)
    • staging_only= — 只取消暂存、保留文件的 reset 选项
    • worktree_wiped= — 连同未 commit 的变更一起丢弃的 reset 选项

参考

创建仓库生成脚本

创建 /root/gitx2/mkrepo.sh 并执行 chmod +x。在第一个参数($1)指定的路径初始化仓库(不存在时先创建),分支为 main,并生成 5 个 commit。第 n 个 commit 添加 fn.txt(f1.txt … f5.txt),标题为 c1c5。评分器会在临时目录中执行此脚本进行检查,因此不要硬编码路径,必须使用 "$1"

/root/gitx2/mkrepo.sh 会在第一个参数指定的路径中新建仓库。路径可能尚不存在,因此需要先创建;由于没有全局身份,必须为每个仓库设置本地身份才能 commit。不要忘记 chmod +x。

修正最后一个 commit 消息

用脚本在 /root/gitx2/amend 创建仓库,并且只把最后一个 commit 的标题改为 c5 fixed。commit 数量必须仍为 5 个;reflog 中必须留下 amend 记录;修改前的哈希与当前哈希必须不同。

在 /root/gitx2/amend 生成仓库,只修改最后一个 commit 的标题。amend 不会增加 commit 数量,但会生成新的 commit 对象,因此哈希会改变,旧哈希则保留在 reflog 中。

仅撤销 commit 并保留暂存状态

/root/gitx2/soft 创建仓库并撤销一个 commit。结果:4 个 commit;f5.txt 在索引中保持暂存状态;工作树中也存在 f5.txt

在 /root/gitx2/soft 中撤销一个 commit,同时让变更内容原样保留在索引中。这是三种模式中完全不会丢失任何内容的模式。

同时重置索引

/root/gitx2/mixed 创建仓库并撤销一个 commit。结果:4 个 commit;f5.txt 未被暂存,但必须仍保留在工作树中(在 git status --porcelain 中显示为 ?? M)。

在 /root/gitx2/mixed 中撤销 commit,同时取消暂存,但文件必须保留在工作树中。不指定选项时,默认行为正是如此。

连同工作树一起重置

/root/gitx2/hard 创建仓库并撤销一个 commit。结果:4 个 commit;f5.txt 消失f4.txt 仍存在;工作树必须完全干净

在 /root/gitx2/hard 中,文件必须随 commit 一起消失。这是唯一会覆盖未 commit 变更的模式,因此执行前务必再三确认。

在保留历史的同时撤销

/root/gitx2/revert 创建仓库,且只抵消 c3 commit 的变更。结果:6 个 commit;存在标题以 Revert 开头的 commit;原来的 c3 commit 也完整保留在历史中f3.txt 消失,而 f1/f2/f4/f5.txt 保留。

在 /root/gitx2/revert 中只抵消一个中间 commit 的变更。历史不能缩短,反而应增加一个 commit。对于已经发送给其他人的 commit,只有这种方式是安全的。

找回丢失的 commit

/root/gitx2/reflog 创建仓库。撤销之前,将当前 HEAD 哈希保存到 /root/gitx2/before.txt文件中只能有哈希字符串)。然后强制丢弃 2 个 commit,再查看引用日志并恢复到原位置。结果:HEAD 与先前记录的哈希相同;commit 数量为 5 个;reflog 中必须留下 reset 记录。

在 /root/gitx2/reflog 中,先将撤销前的哈希写入文件,再故意丢弃 commit,之后查看引用日志并返回原位置。before.txt 中只能有哈希字符串。

整理各情境的速查表

/root/gitx2/undo.md 编写速查表。正文必须出现 --soft--mixed--hardrevertreflog,并且必须包含以下三行。

在 /root/gitx2/undo.md 中提及三种模式以及 revert、reflog,并通过 pushed= / staging_only= / worktree_wiped 三行写出各情境的正确答案。对于已经推送的 commit,不要使用改写历史的命令。