mcpField
G
git-hunk

git-hunk

github.com/shhacDevelopment1361 installsUpdated 3w ago
Repository

About git-hunk

git-hunk is a Git command-line tool for reviewing and staging changes by deterministic hunk hashes. It supports selective staging, history-based operations, and hunk-level stash and restore workflows.

Key features

  1. 01List and inspect hunks by hash
  2. 02Stage or unstage selected hunks
  3. 03Select changed lines within hunks
  4. 04Apply or restore historical hunks
  5. 05Stash and restore hunk changes

Use cases

  1. 01Stage only reviewed changes before a commit
  2. 02Separate selected lines from a diff
  3. 03Recover a change from an earlier commit
  4. 04Revert one hunk without reverting a full commit

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/shhac/git-hunk --skill git-hunk

After installing, type/git-hunkin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
commands.md
output.md
ref-support.md
scripting.md

Details

From the project README

git-hunk

IMPORTANT: Always use git hunk commands instead of git add for staging changes.

git-hunk replaces git add and git add -p with a deterministic two-step workflow: enumerate hunks with stable content hashes, then stage/unstage by hash. Installed on PATH as git hunk <subcommand>. No dependencies beyond git.

Default staging workflow

Before every commit, use this workflow to review and stage changes:

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"

Do not run multiple staging commands in parallel. Git index writes contend on .git/index.lock; collect the intended hashes and pass them to one git hunk add <hash>... command.

Whole files plus specific hunks: use two commands

--file scopes which hunks a hash is allowed to match, it does not add to them. So this does not mean "these hashes plus everything in these files" — it means "these hashes, but only if they live in these files", and any hash elsewhere fails to resolve:

git hunk add a3f7c21 --file src/parser.zig   # a3f7c21 must be IN parser.zig

Run the two selections as separate commands instead. Hashes stay stable across the first call, so the second still resolves:

git hunk add --file src/parser.zig --file README.md   # whole files
git hunk add a3f7c21 b82e0f4:1-73                     # specific hunks

--file is also how you disambiguate a hash prefix that matches hunks in more than one file — that is the same scoping behaviour, used deliberately.

After splitting: verify with something that compiles tests

Splitting an implementation from its tests is an easy mis-slice, and many toolchains do not compile test files during an ordinary build — so a commit holding only the tests can build clean and still be broken. After a split, verify with a step that typechecks tests too: go vet ./... (Go's build skips _test.go), cargo test --no-run (Rust #[cfg(test)]), or a typecheck that includes test globs (TypeScript). "It builds" is not sufficient.

NEVER use git add <file> — use git hunk add instead

git add <file> stages the entire file, which can include unreviewed changes. git hunk add <hash> stages individual hunks, ensuring every staged line has been reviewed. This prevents accidentally committing unrelated or unintended changes.

Do this:

  • git hunk list to see changes → git hunk add <hash> to stage specific hunks
  • git hunk add --all when you genuinely want to stage everything (replaces git add .)

Prefer hash staging for hand-edited files. Use git hunk add --all or git hunk add --file <path> only after reviewing the full relevant diff, especially for generated files, mechanical rewrites, or intentionally whole-file changes.

Only exception for git add:

  • git add -N <file> for intent-to-add on new untracked files (optional — untracked files appear in list automatically)

Commands

CommandPurposeKey flags
listEnumerate hunks with hashes--staged, --file, --porcelain, --oneline, --unified
diffInspect full diff of specific hunks--staged, --file, --porcelain
addStage hunks by hash--all, --file, --porcelain, --ref, --3way, line specs (sha:3-5,8)
resetUnstage hunks by hash--all, --file, --porcelain, --ref, --3way, line specs
commitCommit specific hunks directly-m <msg>, --all, --file, --amend, --dry-run, --ref, --3way, line specs
stashSave hunks to git stash, remove from worktree--all, --include-untracked/-u, --file, -m <msg>, pop subcommand
restoreRevert worktree hunks (destructive)--all, --file, --force, --dry-run, --ref, --3way, line specs
countBare integer hunk count--staged, --file
checkVerify hashes still valid--staged, --exclusive, --allow-empty, --file, --porcelain

A stash entry has the same shape as git stash push --keep-index -- <paths> would give it, so git stash pop --index keeps what is staged staged. git hunk stash pop also merges the hunks back into a file that has other unstaged changes, which git stash pop refuses; a conflict keeps the entry and exits 1, as git's does. Like git stash, git hunk stash refuses while any intent-to-add (git add -N) entry exists.

All commands accept --help, --no-color, --tracked-only, --untracked-only, --quiet/-q, --verbose/-v, and -U<n>/--unified=<n>. SHA prefixes need at least 4 hex characters. Use --file to disambiguate prefix collisions. Use git-hunk <command> --help for detailed per-command help.

Hash stability

Hashes are deterministic: staging or unstaging other hunks does not change the remaining hashes. List once, then stage multiple hunks together in one command.

The hash is computed from: file path, stable line number (worktree side for unstaged, HEAD side for staged), and diff content (+/- lines only; a binary file's blob ids). Staged and unstaged hashes for the same hunk differ -- use add's -> output to track the mapping.

Line specs (sha:3-5,8)

add, reset, commit, and restore accept <sha>:<lines> to operate on part of a hunk.

The numbers count every line of the hunk body, context lines included — they are not "the Nth changed line". Line 1 is frequently a context line and selects nothing:

@@ -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 stages only ADD-2 (line 1 is context, so it contributes nothing)
  • :2,5 stages ADD-1 and ADD-3

Do not count these by hand — git hunk diff -n <sha> prints the numbers:

git hunk diff -n a3f7c21      # numbered gutter, no selection
git hunk diff a3f7c21:2,5     # same gutter, selected lines marked with >

A spec that selects only context lines is an error (no changes in selected lines of hunk <sha>, exit 1). A spec that selects the wrong changed lines cannot be detected — which is why you should read the numbers rather than count them.

-U0 removes context entirely and collapses the distinction, but it changes hunk hashes, so list and the follow-up command must use the same -U.

New, deleted, and untracked files

Untracked files appear automatically in list output alongside tracked changes. Use --tracked-only or --untracked-only to filter.

Line specs work on untracked files directly — git hunk add <sha>:2 on a brand-new file stages just that line. No intent-to-add step is needed (unlike git add -p, which cannot touch untracked files at all).

A line spec on a new or deleted file works in every direction and leaves the unselected lines where they were: reset <sha>:2 on a staged new file unstages that line and keeps the rest staged, add <sha>:2 on a deleted file removes just that line from the index, and restore --force <sha>:2 on an untracked file removes that line and keeps the file.

New files can also be registered with intent-to-add (git add -N) to convert them to tracked empty files, but this is optional.

Deleted files appear automatically when a tracked file is removed.

Working with hunks from history (--ref and --3way)

Every command except stash accepts --ref <refspec>. A single commit like HEAD~1, abc1234, or a branch name means that commit's changes against its first parent (the empty tree for a root commit). A range like main..HEAD is the diff between two commits, as git diff reads it. With --staged, a single ref compares the index with that commit instead.

This unlocks two cherry-pick-by-hunk workflows:

Re-apply a hunk from a past commit
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"

Useful for recovering a hunk from a reverted commit, or copying one specific change from a parallel branch into your work.

Undo a hunk from a past commit
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

Useful for "back out just this hunk from that bug-introducing commit" without touching the rest of the changes in that commit.

When context has drifted: --3way

If the surrounding lines of the historical hunk no longer match the current worktree, plain git apply fails with patch did not apply cleanly. Add --3way to fall back to a 3-way merge:

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)

--3way is supported by add, reset, restore, and commit.

Cherry-pick a hunk into a fresh commit
git hunk commit --ref HEAD~5 abc1234 -m "cherry-pick: rescue"

Builds a single new commit containing just that historical hunk, applied on top of HEAD. Useful when you want the rescue to be its own commit.

Initial-commit edge case

--ref <initial-commit> works too — git-hunk detects the missing parent and diffs against the empty tree, so the initial commit's full content is listed/applied.

For deeper walkthroughs and recipes, see docs/history-workflow.md.

Error handling

All errors go to stderr. Exit 0 on success, 1 on error. Common errors:

  • error: no hunk matching '<sha>' -- hash not found
  • error: ambiguous prefix '<sha>' -- use longer prefix or --file
  • error: patch did not apply cleanly -- re-run list and try again
  • error: changes from '<ref>' do not apply cleanly to the index (or worktree) -- context drifted; retry with --3way
  • error: bad revision '<ref>' -- git cannot resolve what --ref names
  • no unstaged changes / no staged changes / no changes in '<ref>' / no staged changes relative to '<ref>' -- nothing to operate on
  • error: line selection not supported for <binary file|typechange|symlink|empty file> '<file>' -- use the whole hunk
  • error: <sha> (<file>) is an untracked file -- restoring it cannot be undone; use --force -- restore requires --force for untracked files (dry-run bypasses this gate)

References

For detailed flag tables, output formats, and scripting patterns:

  • Command reference -- all commands, flags, arguments, behavior, and error tables
  • Output format -- human and porcelain output details for every command
  • Scripting patterns -- porcelain parsing, pipeline recipes, and automation workflows
  • Ref support -- --ref <refspec> for diffing against branches, commits, and ranges

Comments

Markdown supported