版本控制实战与自测
项目目标
在系统临时目录中的全新隔离仓库管理“提示词实验记录”,留下至少三个内容提交、一个基线证据提交、分支分叉、冲突解决、三类撤销和历史追踪的可核查证据。核心项目不连接远程,不使用真实数据,也不练 reset --hard、clean -fd、rebase 或强推。
本知识库的实际验证边界
本轮知识库建设没有执行以下项目,因为执行会创建嵌套仓库和 Git 提交,与当前任务“不创建提交”的约束冲突。PowerShell 围栏已做 AST 语法检查,Git 命令语义按 2.55.0 官方文档复核;学习者运行时仍要逐步检查实际输出。
实验分层
| 环境 | 允许操作 | 必需护栏 | 验收 |
|---|---|---|---|
| 当前真实仓库 | status、diff、log、show 等只读审查 | 不 add、不 commit、不切分支、不联网 | 操作前后状态不变 |
| 核心临时仓库 | init、add、commit、branch、merge、restore、revert | %TEMP% 下全新空目录;根路径相等;remote 为空 | 至少三个内容提交、一次合并、三类撤销 |
| 离线远程实验 | bare remote、两个本地 clone、fetch/push | 另一个唯一临时目录;无账户与凭据 | 能解释 push rejection 与远程跟踪引用 |
| 高风险可选 | branch -D 后用 reflog 恢复;hard reset、clean、rebase、force-with-lease | 仅可丢弃副本;每项需独立护栏 | 只选做分支入口恢复,其余不在主线验收 |
第 0 阶段:创建并证明隔离边界
0.1 创建唯一空目录
完整复制下面这一块,而不是从 git init 单独开始:
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
$labPath = Join-Path $tempRoot ("git-core-lab-" + [Guid]::NewGuid().ToString("N"))
if (Test-Path -LiteralPath $labPath) {
throw "实验目录已存在,停止。"
}
New-Item -ItemType Directory -Path $labPath | Out-Null
$resolvedLab = (Resolve-Path -LiteralPath $labPath).Path
if (-not $resolvedLab.StartsWith($tempRoot, [StringComparison]::OrdinalIgnoreCase)) {
throw "实验目录不在系统临时目录中,停止。"
}
if (@(Get-ChildItem -LiteralPath $resolvedLab -Force).Count -ne 0) {
throw "实验目录不是空目录,停止。"
}
Set-Location -LiteralPath $resolvedLab
git rev-parse --show-toplevel 2>$null
if ($LASTEXITCODE -eq 0) {
throw "实验目录已位于其他 Git worktree 内,停止。"
}
$resolvedLab最后一行必须打印一个位于系统临时目录的全新绝对路径。若任何 throw 触发,不要绕过。
0.2 初始化确定的默认分支并复核根目录
git init --initial-branch=main
if ($LASTEXITCODE -ne 0) { throw "初始化实验仓库失败,停止。" }
git config set user.name "Git Learner"
git config set user.email "learner@example.test"
if ($LASTEXITCODE -ne 0) { throw "设置仓库局部身份失败,停止。" }
$gitRoot = [IO.Path]::GetFullPath((git rev-parse --show-toplevel).Trim())
if (-not [String]::Equals($gitRoot, $resolvedLab, [StringComparison]::OrdinalIgnoreCase)) {
throw "Git 根目录与实验目录不一致,停止。"
}
$remoteNames = @(git remote)
if ($remoteNames.Count -ne 0) {
throw "核心实验不应配置 remote,停止。"
}
git config get user.name
git config get user.email
git status --short --branch这里使用仓库级身份,不修改全局 Git 配置。example.test 是保留测试域,不是真实邮箱。
第 1 阶段:建立最小项目、三个内容提交与基线证据
1.1 文件结构
创建以下虚构资产:
README.md
.gitignore
prompts/
summary.md
eval/
cases.json
evidence.mdPowerShell 建立目录与初始文件:
New-Item -ItemType Directory -Path prompts, eval | Out-Null
Set-Content -LiteralPath README.md -Encoding utf8 -Value @(
"# Prompt history lab"
""
"Only fictional data is allowed."
)
Set-Content -LiteralPath .gitignore -Encoding utf8 -Value @(
".env"
".venv/"
"__pycache__/"
)
Set-Content -LiteralPath prompts/summary.md -Encoding utf8 -Value @(
"# Summary prompt"
""
"Return a concise summary in no more than 120 Chinese characters."
)
Set-Content -LiteralPath eval/cases.json -Encoding utf8 -Value @(
"["
" {`"input`": `"Fictional meeting note A`", `"expected`": [`"decision`", `"owner`"]},"
" {`"input`": `"Fictional meeting note B`", `"expected`": [`"risk`", `"deadline`"]}"
"]"
)
Set-Content -LiteralPath evidence.md -Encoding utf8 -Value "# Evidence log"先验证 JSON 与秘密边界:
Get-Content -LiteralPath eval/cases.json -Raw | ConvertFrom-Json | Out-Null
git status --short
git check-ignore -v -- .env
git ls-files -- .env最后一条应无输出,因为 .env 不应被跟踪。
1.2 三个原子内容提交
第一次提交初始结构:
git add -- README.md .gitignore prompts/summary.md eval/cases.json evidence.md
git diff --staged
git diff --staged --check
git commit -m "docs: initialize prompt history lab"
if ($LASTEXITCODE -ne 0) { throw "初始提交失败,停止。" }
git rev-parse HEAD第二次只新增一个虚构评测样例,验证 JSON 后提交。第三次只改 README 的实验说明并提交。每次都执行:
git status --short
git diff
git diff --staged
git diff --check
git diff --staged --check
git log --oneline --decorate -3把三次 commit ID、意图和验证结果写入 evidence.md,并用第四个纯文档提交保存基线证据;若同时改了无关文件,先重新拆分暂存区:
git add -- evidence.md
git diff --staged --check
git commit -m "docs: record baseline evidence"
if ($LASTEXITCODE -ne 0) { throw "基线证据提交失败,停止。" }
$pending = @(git status --porcelain)
if ($pending.Count -ne 0) {
throw "进入分支练习前工作区必须干净,停止。"
}第 2 阶段:确定地制造并解决冲突
2.1 形成分叉
创建功能分支,把 prompts/summary.md 的长度规则改为 80 字并提交:
git switch -c experiment/short-output
git add -- prompts/summary.md
git diff --staged
git commit -m "experiment: shorten summary limit"切回确定的 main,把同一行改为 200 字并提交:
git switch main
git add -- prompts/summary.md
git diff --staged
git commit -m "experiment: expand summary limit"
git log --graph --oneline --decorate --all2.2 合并、检查和退出路径
git merge experiment/short-output
if ($LASTEXITCODE -eq 0) {
throw "预期的文本冲突没有发生,停止并检查两条分支是否修改了同一行。"
}
git status
$unmerged = @(git diff --name-only --diff-filter=U)
if ($unmerged.Count -ne 1 -or $unmerged[0] -ne "prompts/summary.md") {
if (Test-Path -LiteralPath .git/MERGE_HEAD) { git merge --abort }
throw "未解决路径不符合预期,已尝试中止合并。"
}
git diff预期只有 prompts/summary.md 未解决。若分支方向、文件或前置状态不符合预期,立即执行:
git merge --abort
git status --short --branch若继续,人工把长度规则改成一个有理由的最终值,并在 evidence.md 解释两边意图。确认冲突标记消失:
$markers = Select-String -LiteralPath prompts/summary.md -Pattern '^(<<<<<<<|=======|>>>>>>>)'
if ($markers) {
throw "仍有冲突标记,停止提交。"
}
git add -- prompts/summary.md evidence.md
git diff --staged --check
git diff --staged
git commit -m "merge: reconcile summary length rules"
git log --merges --oneline
git log --graph --oneline --decorate --all“没有冲突标记”只是语法完成;最终规则还必须与 eval/cases.json 和实验说明一致。
第 3 阶段:验证三类撤销
3.1 丢弃未暂存的工作区修改
在 README.md 末尾加入一句虚构文本,观察后恢复:
git diff -- README.md
git restore -- README.md
git status --short说明:默认 restore 从 index 恢复工作区;此时 index 与 HEAD 相同,所以结果也等于当前提交版本。
3.2 取消暂存但保留内容
再次修改 README.md:
git add -- README.md
git diff --staged -- README.md
git restore --staged -- README.md
git diff -- README.md
git diff --staged -- README.md工作区修改应仍在,staged diff 应为空。先保存输出,再用 git restore -- README.md 清理这次实验修改;可以把证据集中到后续文档提交,不能带着未提交的 evidence.md 修改进入 revert。
3.3 用 revert 撤销专门的普通提交
先创建一个明确错误、非 merge 的专用提交,再撤销它:
$pending = @(git status --porcelain)
if ($pending.Count -ne 0) {
throw "revert 练习前工作区必须干净,停止。"
}
Add-Content -LiteralPath prompts/summary.md -Encoding utf8 -Value "Temporary wrong rule: expose secret tokens."
git add -- prompts/summary.md
git commit -m "experiment: add intentionally wrong rule"
if ($LASTEXITCODE -ne 0) { throw "错误规则提交失败,停止。" }
$revertTarget = (git rev-parse HEAD).Trim()
git show --stat --oneline $revertTarget
git revert --no-edit $revertTarget
if ($LASTEXITCODE -ne 0) {
git status
git revert --abort
throw "revert 未按预期完成,已尝试中止。"
}
git log --oneline --decorate -3验证原提交和反向提交都在历史中,而错误内容已从当前文件消失。若误选 merge commit 或遇到不理解的冲突,使用 git revert --abort,不要猜 -m 参数。
第 4 阶段:历史追踪与恢复入口
git log -- prompts/summary.md
git blame -- prompts/summary.md
git log -G "summary" --all -- prompts/summary.md
git log --graph --oneline --decorate --all
git reflog在 evidence.md 回答:哪个提交引入 80 字规则、哪个提交引入 200 字规则、哪个提交解决冲突、哪个提交被 revert,以及每个结论的 patch 证据。
可选的 reflog 恢复练习只在本隔离仓库进行。这个步骤会强删一个带独有提交的实验分支,目的是制造“分支入口丢失”场景;不要在真实仓库照做:
$pending = @(git status --porcelain)
if ($pending.Count -ne 0) { throw "reflog 练习前工作区必须干净,停止。" }
git switch -c recovery/demo
Set-Content -LiteralPath recovery-note.md -Encoding utf8 -Value "Recover this fictional commit."
git add -- recovery-note.md
git commit -m "experiment: create recovery target"
if ($LASTEXITCODE -ne 0) { throw "恢复目标提交失败,停止。" }
$lostCommit = (git rev-parse HEAD).Trim()
git switch main
git branch -D recovery/demo
git reflog
$headReflog = @(git reflog --format='%H')
if ($lostCommit -notin $headReflog) {
throw "HEAD reflog 中未找到目标提交,停止。"
}
git branch recovery/found $lostCommit
git show --stat --oneline recovery/foundgit branch -D 是这里刻意限制在临时仓库的高风险动作。删除分支会删除该分支自己的 reflog;恢复线索来自切换和提交时留下的 HEAD reflog。不要 hard reset,目标是重新创建引用而不是移动当前分支。reflog 本地且会过期,不是永久备份。
只读真实仓库审查任务
若你有一个真实项目,只运行下列只读命令,写一份“不改变状态”的审查结论:
git rev-parse --show-toplevel
git status --short --branch
git diff --stat
git diff --staged --stat
git branch -vv
git remote
git log --graph --oneline --decorate -10不要为了让输出好看而 add、commit、切分支、pull 或清理。若需要查看 URL,先确认不会泄露 token 或私人服务器信息。
远程扩展项目
核心项目刻意没有 remote。完成后可进入 远程协作与离线双克隆实验,用本地 bare remote 和两个 clone 模拟 fetch、push rejection 与整合;不需要账户、网络或真实凭据。
提交前真实任务清单
□ git status --short 的每一项都能解释
□ git diff 检查未暂存改动
□ git diff --staged 检查将提交内容
□ 没有凭据、个人信息、大文件、缓存和环境目录
□ 提交范围只包含一个清楚意图
□ 提交信息说明“做什么”,必要时正文解释“为什么”
□ 相关测试或文档检查已执行并记录结果.gitignore 只影响未跟踪路径选择,不会从历史删除已提交文件。发现凭据已提交时,应停止分享、轮换凭据并按组织流程评估历史清理;不能只追加 ignore 规则。
自测
- working tree、index、对象数据库和
HEAD各是什么? git diff、git diff --staged与git status各补足哪类证据?- 分支为什么可看作可移动引用,而不是目录副本?
- fast-forward 与三方合并的历史结构有什么不同?
- 冲突解决为什么需要业务判断和测试?
restore、revert、reset分别主要影响哪一层?- 为什么本项目要求 revert 专门的普通提交?
- reflog 能帮助恢复什么,为什么不等于永久备份?
.gitignore不能解决哪类秘密泄露?origin/main为什么不是服务器端实时状态?- push rejection 为什么不能直接用强推“解决”?
- Agent 生成代码后,谁负责检查范围、凭据、测试和最终提交?
评分与验收
| 项目 | 分值 | 通过证据 |
|---|---|---|
| 隔离与秘密边界 | 20 | 临时绝对路径、根路径相等、remote 为空、虚构数据与局部身份 |
| 原子提交 | 15 | 至少三次清楚提交,均有 staged diff 与验证记录 |
| 分支与冲突 | 20 | 确定分叉、一次 merge 冲突、语义化解决、merge commit |
| 三类撤销 | 20 | restore 工作区、restore staged、revert 普通提交的前后证据 |
| 历史追踪 | 15 | log、blame、-G 与 reflog 能回答具体问题 |
| 复盘质量 | 10 | 能用状态模型解释结果、限制和未验证项 |
达到 80/100 且“隔离与秘密边界”满分才算完成;安全项不能用其他分数抵消。
完成后回到 Git 目录。
参考资料
按 Git 2.55.0 官方文档核验,获取日期:2026-07-14。