关于 git-hunk
git-hunk 是用于按确定性 hunk 哈希审查和暂存改动的 Git 命令行工具。它支持选择性暂存、基于历史版本的操作,以及按 hunk 存储和还原改动。
主要功能
- 01按哈希列出并检查 hunk
- 02暂存或取消暂存指定 hunk
- 03选择 hunk 内的变更行
- 04应用或还原历史 hunk
- 05存储并还原 hunk 改动
适用场景
- 01提交前只暂存已审查的改动
- 02从差异中拆分指定行
- 03恢复较早提交中的改动
- 04仅撤销某个 hunk 而非整个提交
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ shhac/ git-hunk --skill git-hunk安装后在 Claude Code 中输入/git-hunk确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/commands.mdoutput.mdref-support.mdscripting.md详细介绍
git-hunk
重要:暂存更改时,始终使用 git hunk 命令,不要使用 git add。
git-hunk 以确定性的两步工作流取代 git add 和 git add -p:先列出带有稳定内容哈希的代码块,再按哈希暂存或取消暂存。
安装后可通过 PATH 使用 git hunk <subcommand>。除 git 外没有其他依赖。
默认暂存工作流
每次提交前,都使用此工作流来审查并暂存更改:
git hunk list --oneline # compact inventory of changed hunks
git hunk diff a3f7c21 b82e0f4 # inspect the hunks you plan to stage
git hunk add a3f7c21 b82e0f4 # stage exact hunks in one index write
git hunk list --staged --oneline # verify the staged hunk inventory
git diff --cached --check # catch whitespace/conflict-marker issues
git diff --cached --stat # final staged summary
git commit -m "feat: add error handling and update parser"
不要并行运行多个暂存命令。Git 索引写入会竞争 .git/index.lock;先收集所需的哈希,再将它们传给一个 git hunk add <hash>... 命令。
整个文件加特定代码块:使用两个命令
--file 限定哈希可以匹配哪些代码块,而不是将这些文件添加进来。因此,这并不表示“这些哈希加上这些文件中的所有内容”,而是表示“这些哈希,但仅当它们位于这些文件中”;其他位置的任何哈希都无法解析:
git hunk add a3f7c21 --file src/parser.zig # a3f7c21 must be IN parser.zig
改为分别运行这两次选择。哈希在第一次调用后仍保持稳定,因此第二次仍可解析:
git hunk add --file src/parser.zig --file README.md # whole files
git hunk add a3f7c21 b82e0f4:1-73 # specific hunks
--file 也可用于消除匹配多个文件中代码块的哈希前缀歧义——这使用的是相同的限定行为,只是有意为之。
拆分后:使用能编译测试的步骤进行验证
将实现与测试拆分很容易出错,而且许多工具链在常规构建期间不会编译测试文件,因此仅包含测试的提交可能构建成功,却仍然存在问题。拆分后,应使用也会进行测试类型检查的步骤来验证:go vet ./...(Go 的构建会跳过 _test.go)、cargo test --no-run(Rust 的 #[cfg(test)]),或包含测试 glob 的类型检查(TypeScript)。“构建成功”并不足够。
绝不要使用 git add <file>——改用 git hunk add
git add <file> 会暂存整个文件,其中可能包含未经审查的更改。git hunk add <hash> 会暂存单个代码块,确保暂存的每一行都经过审查。这可以避免意外提交无关或非预期的更改。
请这样做:
- 使用
git hunk list查看更改 → 使用git hunk add <hash>暂存指定代码块 - 确实要暂存所有内容时,使用
git hunk add --all(取代git add .)
对于手动编辑的文件,优先按哈希暂存。只有在审查过完整的相关差异后,才使用 git hunk add --all 或 git hunk add --file <path>,尤其是对于生成文件、机械式重写或有意进行的整文件更改。
唯一例外是 git add:
- 对新建的未跟踪文件使用
git add -N <file>标记意图添加(可选——未跟踪文件会自动显示在list中)
命令
| 命令 | 用途 | 关键标志 |
|---|---|---|
list | 列出分块及其哈希 | --staged、--file、--porcelain、--oneline、--unified |
diff | 查看指定分块的完整差异 | --staged、--file、--porcelain |
add | 按哈希暂存分块 | --all、--file、--porcelain、--ref、--3way、行指定(sha:3-5,8) |
reset | 按哈希取消暂存分块 | --all、--file、--porcelain、--ref、--3way、行指定 |
commit | 直接提交指定分块 | -m <msg>、--all、--file、--amend、--dry-run、--ref、--3way、行指定 |
stash | 将分块保存到 git stash,并从工作树中移除 | --all、--include-untracked/-u、--file、-m <msg>、pop 子命令 |
restore | 还原工作树中的分块(具有破坏性) | --all、--file、--force、--dry-run、--ref、--3way、行指定 |
count | 输出纯整数分块计数 | --staged、--file |
check | 验证哈希是否仍然有效 | --staged、--exclusive、--allow-empty、--file、--porcelain |
stash 条目的格式与 git stash push --keep-index -- <paths>
生成的格式相同,因此 git stash pop --index 会让已暂存的内容保持暂存状态。
git hunk stash pop 还会将分块合并回包含其他未暂存更改的文件中,而 git stash pop 会拒绝这样做;发生冲突时会保留该条目并以状态码 1 退出,这与 git 的行为相同。与 git stash 一样,只要存在任何 intent-to-add(git add -N)条目,git hunk stash 就会拒绝执行。
所有命令均接受 --help、--no-color、--tracked-only、--untracked-only、
--quiet/-q、--verbose/-v 和 -U<n>/--unified=<n>。SHA 前缀至少需要 4 个十六进制字符。使用 --file
可消除前缀冲突。使用 git-hunk <command> --help 查看各命令的详细帮助。
哈希稳定性
哈希是确定性的:暂存或取消暂存其他分块不会改变剩余分块的哈希。先列出分块,再用一条命令暂存多个分块。
哈希根据以下内容计算:文件路径、稳定行号(未暂存时为工作树一侧,已暂存时为 HEAD 一侧)以及差异内容(仅 +/- 行;二进制文件则使用 blob ID)。同一分块的已暂存哈希和未暂存哈希不同——使用 add 的 -> 输出跟踪映射关系。
行指定(sha:3-5,8)
add、reset、commit 和 restore 接受 <sha>:<lines>,用于操作分块的一部分。
编号会计算分块主体中的每一行,包括上下文行——它们并非“第 N 个更改行”。第 1 行通常是上下文行,因此不会选中任何内容:
@@ -1,3 +1,6 @@
keep-A <- 1 (context)
+ADD-1 <- 2
+ADD-2 <- 3
keep-B <- 4 (context)
+ADD-3 <- 5
keep-C <- 6 (context)
:1,3仅暂存ADD-2(第 1 行是上下文行,因此不产生效果):2,5暂存ADD-1和ADD-3
不要手动计数——git hunk diff -n <sha> 会显示行号:
git hunk diff -n a3f7c21 # numbered gutter, no selection
git hunk diff a3f7c21:2,5 # same gutter, selected lines marked with >
如果指定内容只选中上下文行,则会报错(no changes in selected lines of hunk <sha>,退出状态码为 1)。如果指定内容选中了错误的更改行,则无法检测出来——因此你应该查看行号,而不是自行计数。
-U0 会完全移除上下文,消除这一区别,但会改变分块哈希,因此 list 和后续命令必须使用相同的 -U。
新建、已删除和未跟踪文件
未跟踪文件会自动与已跟踪的更改一同显示在 list 输出中。
使用 --tracked-only 或 --untracked-only 进行筛选。
行规格可直接用于未跟踪文件——对新建文件执行 git hunk add <sha>:2,即可只暂存该行。无需执行 intent-to-add 步骤(与 git add -p 不同,后者完全无法处理未跟踪文件)。
对新建或已删除文件使用行规格时,所有方向都适用,并且未选中的行会保持原样:对已暂存的新建文件执行 reset <sha>:2 会取消暂存该行并保留其余已暂存内容;对已删除文件执行 add <sha>:2 会只从索引中移除该行;对未跟踪文件执行 restore --force <sha>:2 会移除该行并保留文件。
也可以使用 intent-to-add(git add -N)登记新文件,将其转换为空的已跟踪文件,但这不是必需的。
删除已跟踪文件后,它会自动显示为已删除文件。
处理历史记录中的代码块(--ref 和 --3way)
除 stash 外,每个命令都接受 --ref <refspec>。像 HEAD~1、abc1234 或分支名称这样的单个提交,表示该提交相对于其第一个父提交的更改(根提交则相对于空树)。像 main..HEAD 这样的范围,表示两个提交之间的差异,与 git diff 的解释方式相同。使用 --staged 时,单个引用会将索引与该提交进行比较。
这支持两种按代码块挑选提交的工作流:
重新应用过去提交中的代码块
git hunk list --ref HEAD~3 # see hunks introduced by HEAD~3
git hunk add --ref HEAD~3 abc1234 # forward-apply that hunk into the index
git commit -m "rescue: re-apply lost helper"
适用于恢复已还原提交中的代码块,或将某个特定更改从并行分支复制到当前工作中。
撤销过去提交中的代码块
git hunk list --ref HEAD~3 # see hunks introduced by HEAD~3
git hunk restore --ref HEAD~3 abc1234 # reverse-apply that hunk to the worktree
适用于“只撤销那个引入错误的提交中的这一个代码块”,而不触及该提交中的其他更改。
上下文发生偏移时:--3way
如果历史代码块周围的行与当前工作树不再匹配,普通的 git apply 会失败并提示 patch did not apply cleanly。添加 --3way 可回退到三方合并:
git hunk restore --ref HEAD~10 --3way abc1234
# either succeeds cleanly, or leaves <<<<<<< conflict markers in the worktree
# (restore changes the index only to record such a conflict)
add、reset、restore 和 commit 支持 --3way。
将代码块挑选到新的提交中
git hunk commit --ref HEAD~5 abc1234 -m "cherry-pick: rescue"
这会创建一个新的提交,其中只包含该历史代码块,并将其应用于 HEAD 之上。适用于希望让恢复操作单独成为一个提交的情况。
初始提交的特殊情况
--ref <initial-commit> 同样适用——git-hunk 会检测到父提交缺失,并与空树进行比较,因此会列出并应用初始提交的全部内容。
如需更深入的操作说明和示例,请参阅 docs/history-workflow.md。
错误处理
所有错误都会输出到 stderr。成功时退出码为 0,出错时为 1。常见错误:
error: no hunk matching '<sha>'-- 未找到哈希值error: ambiguous prefix '<sha>'-- 请使用更长的前缀或--fileerror: patch did not apply cleanly-- 重新运行list并重试error: changes from '<ref>' do not apply cleanly to the index(或worktree)-- 上下文已偏移;请使用--3way重试error: bad revision '<ref>'-- git 无法解析--ref指定的内容no unstaged changes/no staged changes/no changes in '<ref>'/no staged changes relative to '<ref>'-- 没有可操作的内容error: line selection not supported for <binary file|typechange|symlink|empty file> '<file>'-- 请使用整个代码块error: <sha> (<file>) is an untracked file -- restoring it cannot be undone; use --force-- 还原未跟踪文件需要使用--force(试运行会绕过此限制)
参考资料
如需详细的标志表、输出格式和脚本编写模式: