ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

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-formatsha1 -b master G config user.name Blog experiment G config user.email blogexample.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_historyTrue本例四项状态分别是True、True、True、commit。最后的退出码1表示“不具有该祖先关系”不是命令解析错误。普通log从当前入口沿parent向后走不是整个对象库的目录。工具或状态回答的问题本例观察log当前提交历史能走到谁c1不显示c2reflog引用曾经指向哪里留有c2的移动记录cat-file这个对象能否读取c2仍是commit工作区文件当前磁盘上有什么f.txt仍为v2reflog记录的是引用移动不是持续录制编辑器内容。没进入对象库的修改不能靠一个从未产生过的提交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 --expirenow --all G gc --prunenow rescued_ref_is_c2 ((G rev-parse rescued) -eq $c2) rescued_survives_expiry ((G cat-file -t $c2) -eq commit)两项仍为True即便实验中立即过期所有reflogc2仍由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 --expirenow --all G gc --prunenow git cat-file -e ${c2}^{commit} $missingExit $LASTEXITCODE if ($missingExit -eq 0) { throw expected c2 to be unavailable } old_commit_unavailableTrue 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后旧提交去哪了是同一条主线入口变化不等于对象消失对象暂时存在也不等于永久备份。真正误操作时先停止进一步改写和清理识别旧状态、建立救援入口再核对文件与历史。
返回列表