github jdx/hk v1.46.0
v1.46.0: --staged scope, global install, and a stash trilogy

4 hours ago

A feature-and-fix release: hooks can now target staged files without touching your worktree, hk install cooperates with global installs, and three separate stash/merge bugs that could clobber fixer output or staged deletions are fixed.

Added

  • --staged flag for hk run, check, fix, and hook subcommands (@jdx) #950. Runs hooks against the staged file set while leaving unstaged and untracked changes alone — no stash, no worktree mutation. It conflicts with --all and --stash, and forces StashMethod::None even when the hook config opts into stashing. Fixes #940.

    hk run pre-commit --staged
    hk fix --staged
  • hk install skips when hk is configured globally (@jdx) #934. If any hook.hk-* entry exists in ~/.gitconfig, hk install is a no-op and additionally cleans up stale per-repo hooks left behind from a prior install, so the global install is the single source of truth and hk doesn't fire twice per event. Pass --force-local to install per-repo hooks anyway. This means postinstall workarounds like git config --get-regexp hook\.hk- || hk install can now be replaced with a plain hk install. Closes #933.

  • Named template variables for post-checkout hooks (@jdx) #951. Steps can now reference prev_head, new_head, and is_branch_checkout (a real boolean, mapped from git's 1/0 flag) instead of having to parse the combined hook_args string. docs/hooks.md documents the per-hook variables for prepare-commit-msg, commit-msg, and post-checkout.

  • oxfmt builtin (@hituzi-no-sippo) #914. Adds oxfmt as a builtin formatter for JS/TS, JSON, YAML, and TOML.

  • Vite+ builtin (@hituzi-no-sippo) #913. Adds Vite+ as a builtin formatter/linter for JavaScript/TypeScript.

  • oxlint builtin upgrades (@hituzi-no-sippo) #911. Adds --deny-warnings so violations exit non-zero, extends the file glob to .vue, .svelte, .astro, .mjs, .cjs, .mts, and .cts, and registers oxlint config files as project indicators so the builtin is auto-suggested.

Fixed

  • pre-push ref filter was inverted (@jdx) #932. The filter was checking the local sha for all-zeros (a deletion) when the intent was to check the remote sha (a new branch). Two visible consequences:

    • First push of a new branch was dropped and fell through to resolving refs/remotes/origin/HEAD, which often failed with Failed to parse reference: refs/remotes/origin/HEAD (likely the root cause of #172).
    • Branch deletions were kept and triggered linting against the deleted ref.

    The filter now drops only deletions, falls back to the real remote-tracking branch (or Git::resolve_default_branch()) for new-branch pushes, and uses a new git::is_zero_sha() helper that works for both SHA-1 and SHA-256 repos.

  • hk install --global now uses absolute paths (@jdx) #939. Global hook commands previously assumed hk/mise were on PATH when git invoked the hook, which broke in environments with a sanitized PATH. The installer now resolves hk (or mise) to an absolute path at install time (using ~/ when home-relative and quoting otherwise), and --mise global installs use mise x hk -- hk so the hk tool is requested explicitly. Global installs also pick hook events from the project's hk.pkl when present. Fixes #937.

  • fail_on_fix no longer loses fixer output through stash = "git" (@jdx) #909. git stash show --name-only can list staged files stored in the stash commit that were not part of the unstaged set being restored, so the manual unstash could rewrite a staged-only file and discard the fixer's output that should remain visible as an unstaged diff. hk now tracks the exact path set selected for stashing and filters restore to that set. Follow-up to the fail_on_fix fix in v1.44.3.

  • Staged deletions survive pop_stash (@jdx) #927. pop_stash() walked every path returned by git stash show --name-only and wrote a merged blob to disk, even for paths the user had staged for deletion with git rm. After the commit, the deleted file reappeared on disk as untracked. hk now queries git diff --cached --diff-filter=D before unstashing and skips those paths. Fixes #926.

  • Fixer tail-line deletions are preserved across three-way merge (@jdx) #931. In merge.rs::diff_hunks, when the LCS walk consumed other entirely after a matching line, a pure tail deletion of base[i..n] was dropped, so three_way_merge_hunks silently copied the removed lines back in. The classic symptom: a fixer that strips trailing blank lines, applied to a file where you have an unrelated unstaged change in the middle, would have its trailing-line cleanup silently undone. Fixes #929.

  • check_diff failures get accurate fix suggestions (@jdx) #949. When a step defined both check_diff and check_list_files, the "To fix, run" hint always parsed output with the list-files parser regardless of which check actually ran. hk now passes the executed command into collect_fix_suggestion and dispatches to the diff parser for check_diff output, so the suggested files match the real failure. Fixes #942.

Full Changelog: v1.45.0...v1.46.0

💚 Sponsor hk

hk is developed by @jdx at en.dev — a small independent studio behind developer tools like mise, aube, hk, and more. Work on hk is funded by sponsorships.

If hk has sped up your pre-commit loop or made linting feel less painful, please consider sponsoring at en.dev. Sponsorships are what keep hk moving and the project independent.

Don't miss a new hk release

NewReleases is sending notifications on new releases.