news 2026/10/6 8:06:38

reset后提交去哪了?用真实Git验证reflog、救援分支和gc的三层边界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
reset后提交去哪了?用真实Git验证reflog、救援分支和gc的三层边界

reset之后,log里没了刚才的提交,cat-file却能读它;运行gc以后,它可能仍然能读。到底是谁“没了”?

这三个现象不矛盾。分支当前指向哪里、引用曾经指向哪里、对象还能不能读取,是三个问题。我们用两次提交把它们拆开,再故意去掉保护,检查恢复能力的边界。

本文只需要标准Git和PowerShell,不依赖私有源码。实验使用Git for Windows 2.45.2.windows.1、SHA-1仓库。下列代码块须在同一个窗口依次执行。

后半段有立即过期日志和立即清理对象的命令,仅用于本篇新建、没有其他进程访问的废弃实验仓库。不要复制到项目仓库“修复Git”。普通恢复操作不需要先gc,更不应该先清空日志。

1. 先准备能丢弃的两次提交

第一个代码块创建随机临时目录。辅助函数G只是检查每次Git调用的退出码,不修改Git行为;预期失败时另用原始git命令。

$ErrorActionPreference = 'Stop' $PSNativeCommandUseErrorActionPreference = $false function G { & git @args; if ($LASTEXITCODE -ne 0) { throw "git failed: $args" } } $lab = Join-Path $env:TEMP ('git-reflog-lab-' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $lab | Out-Null Set-Location $lab Set-Content .lab-only 'disposable reflog experiment' G init --object-format=sha1 -b master G config user.name 'Blog experiment' G config user.email 'blog@example.invalid' G config core.autocrlf false G config gc.auto 0 Set-Content f.txt v1 G add f.txt G commit -m c1 $c1 = G rev-parse HEAD Set-Content f.txt v2 G add f.txt G commit -m c2 $c2 = G rev-parse HEAD

我们提前保存ID,是为了给实验提供判断基准,不代表恢复时总能提前知道它。实际误操作后通常要先从reflog里识别时间和操作,再检查对应提交内容。

2. log里没了,什么仍然存在

这里刻意使用mixed,不用hard:分支和index回到c1,工作区的v2保留。

G reset --mixed $c1 G log --oneline G reflog show HEAD --oneline 'head_is_c1=' + ((G rev-parse HEAD) -eq $c1) 'worktree_still_v2=' + ((Get-Content f.txt -Raw).Trim() -eq 'v2') 'reflog_records_c2=' + (@(G reflog show --format=%H HEAD).Contains($c2)) 'old_type=' + (G cat-file -t $c2) git merge-base --is-ancestor $c2 HEAD $ancestorExit = $LASTEXITCODE if ($ancestorExit -ne 1) { throw 'c2 should not be an ancestor of current HEAD' } 'c2_not_in_current_history=True'

本例四项状态分别是True、True、True、commit。最后的退出码1表示“不具有该祖先关系”,不是命令解析错误。普通log从当前入口沿parent向后走,不是整个对象库的目录。

工具或状态回答的问题本例观察
log当前提交历史能走到谁c1,不显示c2
reflog引用曾经指向哪里留有c2的移动记录
cat-file这个对象能否读取c2仍是commit
工作区文件当前磁盘上有什么f.txt仍为v2

reflog记录的是引用移动,不是持续录制编辑器内容。没进入对象库的修改,不能靠一个从未产生过的提交ID恢复。Git reflog手册

3. 普通gc不等于删除最近离开分支的提交

G count-objects -v G gc G count-objects -v 'ordinary_gc_keeps_c2=' + ((G cat-file -t $c2) -eq 'commit') 'gc_does_not_move_HEAD=' + ((G rev-parse HEAD) -eq $c1)

本例两项均为True。gc会整理存储、处理保留策略;不能把它简化成“删掉所有分支log看不到的提交”。当前reflog提供保护线索,新近对象也可能受保留期限影响。

这个实验不能单独证明“只有reflog在保护c2”。对象刚创建,年龄也是变量。下面先引入一个确定的引用入口,再用相同的立即清理策略做有无入口的对照。Git gc手册

4. 给旧提交一个名字,比一直赌日志保留更明确

先建救援分支。branch不会切换当前工作区,我们只是重新建立一个入口。检查show的内容以后,再决定是否切换、摘取或导出。

G branch rescued $c2 G show rescued:f.txt if ((G rev-parse --show-toplevel) -ne $lab.Replace('\','/')) { throw 'wrong repository' } if (-not (Test-Path .lab-only)) { throw 'not the disposable lab' } G reflog expire --expire=now --all G gc --prune=now 'rescued_ref_is_c2=' + ((G rev-parse rescued) -eq $c2) 'rescued_survives_expiry=' + ((G cat-file -t $c2) -eq 'commit')

两项仍为True:即便实验中立即过期所有reflog,c2仍由rescued引用可达。它的tree/blob也随提交关系得到保留,不是只保存一个提交说明。

路径与标记检查只是防止本篇步骤误跑到其他目录,不是让立即清理在真实项目中变安全。官方手册提醒,取消对象年龄缓冲会增加并发写入时的损坏风险。本篇没有其他访问进程,也没有把这组命令当日常维护建议。

5. 去掉最后入口,旧ID还够用吗

下一块仅用来观察边界,故意删除刚建立的实验分支。若是在真实救援过程中,到上一节就该保留入口并检查内容,不应继续清理。

if ((G rev-parse --show-toplevel) -ne $lab.Replace('\','/')) { throw 'wrong repository' } if (-not (Test-Path .lab-only)) { throw 'not the disposable lab' } G branch -D rescued G reflog expire --expire=now --all G gc --prune=now git cat-file -e "${c2}^{commit}" $missingExit = $LASTEXITCODE if ($missingExit -eq 0) { throw 'expected c2 to be unavailable' } 'old_commit_unavailable=True' 'current_commit_preserved=' + ((G cat-file -t $c1) -eq 'commit') 'worktree_copy_remains=' + ((Get-Content f.txt -Raw).Trim() -eq 'v2') G fsck --full if ((G rev-parse HEAD) -ne $c1) { throw 'HEAD changed unexpectedly' } 'REFLOG_BOUNDARIES_CHECKS_PASSED'

本轮c2已不可读,c1仍在,磁盘上的v2却没有被gc删除。这再次说明文件副本、提交对象和引用入口不能混为一谈。有v2文件可以重新提交,但不能据此说旧提交身份、说明和历史关系已恢复。

这里故意保留cat-file的fatal输出,随即检查退出码。不要为隐藏提示而随手加2>$null:在本轮Windows PowerShell 5.1的Stop设置下,重定向的原生错误可能先触发异常,导致预期失败检查根本没执行。开头同时关闭PowerShell 7可选的原生命令错误自动处理,预期失败由正文显式判断。

控制条件本轮c2能否读取能推出什么
刚reset,普通gc能不能仅凭log判断对象删除
rescued存在,过期日志并立即清理能分支入口足以保留其可达对象
rescued删除,过期日志并立即清理不能知道ID也不能凭空重建已清理对象

6. 为什么验证要分三层

图中是本轮实际标准输出节选,排版后截图,不冒充桌面终端窗口。验证程序逐项检查HEAD、对象类型、日志记录、文件内容与预期失败退出码;路径守卫也随正文执行。

如果只检查“f.txt还在”,会把旧提交已清理误当成恢复成功;只检查cat-file成功,又无法判断分支是否已得到可持续的入口。实验刻意控制这些变量,而不是在重要仓库里尝试一长串恢复命令。

这与rebase后旧提交去哪了是同一条主线:入口变化不等于对象消失,对象暂时存在也不等于永久备份。真正误操作时,先停止进一步改写和清理,识别旧状态、建立救援入口,再核对文件与历史。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 8:06:24

【shell】Termux 的命令

守护进程 pkg install termux-servicessv-enable sshd ssh 注入允许密码和关闭严格模式的底层口令:echo -e "StrictModes no\nPasswordAuthentication yes\nPermitRootLogin yes" >> $PREFIX/etc/ssh/sshd_config 端口监控 查看网络端口natstat -tlpn du…

作者头像 李华
网站建设 2026/10/6 8:05:50

Docker --- 基础理论以及安装、快速上手

Docker基础理论以及安装一、什么是Docker二、安装Docker(1)卸载旧版本(2)搭建 Docker 的仓库(3)安装 Docker 包(4)配置镜像加速源(5)确认 Docker 是否运行三、快速上手指令解析一、什么是Docker DOcker是一种开源的容器化平台,目的是为了将我们写好的程序以及周边依…

作者头像 李华
网站建设 2026/10/6 8:05:01

『ISOBUS 入门』第 12 节 虚拟终端 VT:对象池、消息流与交互实现

农具不带屏幕之后,界面的所有权发生了转移:农具负责描述「界面上有什么」,终端负责把它渲染出来并把操作者的动作回传。这条路走通了,一台拖拉机才能用一个终端操作不同品牌的农具。 1. 会话是怎么建立起来的 一次完整的会话可以拆成六步,每一步都有对应的消息: 建立工作…

作者头像 李华
网站建设 2026/10/6 8:04:42

Codex 自定义 API 配置不生效?很多人把 model_provider 写错位置了

你明明已经在项目中添加了 .codex/config.toml,也写好了 model_provider、base_url 和模型名,但 Codex 启动后仍然连接原来的服务,甚至继续报 401、404 或 model not found。 这种情况不一定是 API Key 或中转线路出了问题。一个很容易忽略的…

作者头像 李华
网站建设 2026/10/6 8:01:55

【毕业设计】智能儿童书桌控制系统的设计与实现

❤小编介绍:小编所在团队为图灵学术中心,我们专注于Java领域,提供程序设计开发、源码分享、技术指导及定制服务。凭借丰富经验和专业团队,满足客户多样化需求。从精准选题到顺利毕业,我们致力于助力大家的技术成长&…

作者头像 李华