my-git

Reachability and Recovery Lab / 可达性与恢复实验

中文文章 English article

中文

这个实验只在新建的临时 Git 仓库中执行一次 reset --hard、reflog 过期和 gc --prune=now。初始状态包含两个没有 branch 名的 commit:

main -> baseline

recoverable commit
  no ref reaches it
  still listed by HEAD reflog

expired commit
  no ref reaches it
  still listed by HEAD reflog

运行:

lab_path=$(bash setup.sh)

# 先观察还受 reflog 保护的状态
git -C "$lab_path" reflog show --oneline HEAD
git -C "$lab_path" fsck --no-reflogs --unreachable

# verify.sh 会恢复第一个 commit,并故意过期和清理第二个 commit
bash verify.sh "$lab_path"
bash cleanup.sh "$lab_path"

setup.sh 的标准输出只有临时仓库路径。它禁用系统和全局 Git 配置,为仓库设置本地身份、关闭提交和标签签名,并把 hooks 路径设为空目录。reset --hard、reflog expire 和 gc --prune=now 全部只作用于这个带实验标记的临时库。

verify.sh 会先用对象 ID、refs、HEAD reflog 和 git fsck --no-reflogs --unreachable 检查两个 commit 仍在对象库中且没有 ref 能到达它们。随后它创建 recovered branch 找回第一个 commit。最后它显式过期所有 reflog,并运行 git gc --prune=now,断言第二个 commit 已经无法用 git cat-file 解析。任何断言失败都会以非零状态退出。

运行完整自测:

bash test.sh

verify.sh 会不可逆地销毁第二个临时 commit。若要重新查看受 reflog 保护的中间状态,请重新运行 setup.sh 创建新实验库。

cleanup.sh 只删除带有本实验标记的目录,并拒绝仓库根目录、用户主目录和根目录。

English

This lab performs one reset --hard, reflog expiration, and gc --prune=now only inside a newly created temporary Git repository. Its initial state has two commits without a branch name:

main -> baseline

recoverable commit
  no ref reaches it
  still listed by HEAD reflog

expired commit
  no ref reaches it
  still listed by HEAD reflog

Run:

lab_path=$(bash setup.sh)

# Inspect the state that is still protected by reflog first.
git -C "$lab_path" reflog show --oneline HEAD
git -C "$lab_path" fsck --no-reflogs --unreachable

# verify.sh recovers the first commit and intentionally expires and prunes the second.
bash verify.sh "$lab_path"
bash cleanup.sh "$lab_path"

setup.sh prints only the temporary-repository path. It disables system and global Git configuration, sets a local identity, disables commit and tag signing, and points hooks to an empty directory. reset --hard, reflog expire, and gc --prune=now all operate only on this marked temporary repository.

verify.sh first asserts through object IDs, refs, the HEAD reflog, and git fsck --no-reflogs --unreachable that both commits still exist in the object database with no ref reaching them. It then creates a recovered branch for the first commit. Finally it explicitly expires every reflog and runs git gc --prune=now, asserting that the second commit no longer resolves through git cat-file. Any failed assertion exits nonzero.

Run the full self-test:

bash test.sh

verify.sh irreversibly destroys the second temporary commit. To inspect the reflog-protected intermediate state again, run setup.sh to create a new lab repository.

cleanup.sh removes only a directory marked for this lab and refuses the repository root, the user home directory, and the filesystem root.