In hk fix, many more builtins now apply their check_diff patch instead of running the tool a second time as the fixer. hk now applies those patches itself instead of calling git apply, and read-only check_diff commands can run in parallel with other steps that read the same files.
Added
-
hk util format-diff(#1514, @jdx). Builds an applicable patch from any formatter that reads stdin and writes the formatted file to stdout. It runs the formatter once per file, in parallel, and replaces{}with the file's path. It exits 1 with a patch when a file would change, and 0 when nothing would. If the formatter fails for any file or prints nothing for a non-empty file, it prints no patch, so hk runsfixand that reports the error. Use--no-stdinfor formatters that read the file from its path.check_diff = "hk util format-diff {{files}} -- stylua --stdin-filepath {} -"
-
hk util sarif-diff(#1515, @jdx). Runs a tool that reports fixes in a SARIF log and turns them into a patch. It prints a patch only when every result has a usable fix. If any result has no fix, or the tool fails, it prints no patch and hk runsfix. The findings exit code defaults to 1 and can be changed with--findings-exit-code.check_diff = "hk util sarif-diff -- pinact run --check --format sarif {{files}}"
-
run = "diff"step tests (#1518, @jdx). A diff test runscheck_diffand applies its output the way fix mode does. If the patch doesn't apply, the test fails instead of falling back tofix. If the step setscheck_after_diff, the test rerunscheckafter the patch applies.TestMakergainsdiffPassanddiffFail. Every builtin with acheck_diffnow has a diff test. -
Warning when a
check_diffpatch doesn't apply (#1517, #1520, @jdx). Before, hk ran the fixer without saying why. It now prints a warning that names the file and hunk, for examplefmt: check_diff printed a patch that doesn't apply, so the fixer ran instead: test.txt: error applying hunk #1. Output that isn't a patch still only shows at debug level.
Changed
- More builtins apply patches in fix mode instead of running the tool twice (@jdx):
stylua,yamlfmt,taplo_format,tombi_format,buildifier_format,terraformandtofunow usehk util format-diff(#1514). Forstylua,yamlfmt,taplo_formatandtombi_format, the old non-patch diff view is now theircheck, sohk checkoutput doesn't change. The yamlfmt and taplo patch scripts need a POSIX shell. On Windows those two steps run the fixer instead.ruff,oxfmt,shellhardenandgo_linesgain acheck_diff(#1516). After ruff applies its patch, it rerunsruff checkso issues it can't fix are still reported.pinactandpinact_updateapply pinact's SARIF fixes, which also avoids a second round of GitHub API calls (#1515).isortgains acheck_diff, andgomod_tidypatches now apply (#1513).trailing_whitespaceandnewlinesnow apply their diffs instead of settingapply_check_diff = false(#1528).seleneandzizmorno longer run their tool twice in fix mode (#1511).
- hk applies
check_diffpatches itself instead of runninggit apply(#1520, @jdx). Patches still apply all or nothing, with exact context matching. Existing files are written in place, so they keep their permissions and owner. If a write fails, hk restores the files it changed. Patches whose header labels one side with a temporary file or a label now apply, such as those frombuildifier -mode=diffand phpcs--report=diff. hk refuses the following patches and runs the fixer instead:- paths outside the working directory
- paths through symlinks
- files with other hard links
- renames, copies and mode changes
- binary patches
- Read-only
check_diffsteps run in parallel (#1519, @jdx). When a step'scheck_diffdeclareseffect = "read", hk computes the patch under read locks. It takes write locks only on the files the patch names. If another step wrote any of the job's files in the meantime, hk recomputes the patch. On an already-formatted tree, two such steps over the same files now run at the same time instead of one after the other. 28 of the 30 builtincheck_diffcommands declare a read effect. Acheck_diffthat declareseffect = "read"must now really only read the step's files. - Steps with an argv
prefixor customshellno longer fail to load because of a shell-basedcheck_difforcheck_list_files(#1514, @jdx). hk skips that command, and anything that depends on it, and runscheckandfixinstead. As a result,yamlfmt,taplo_format,blackandruffnow load with a prefix such asprefix = List("mise", "x", "--"). Acheckorfixthat can't run with the prefix is still an error. go_linesaccepts an argvprefix(#1523, @jdx). Its shellcheckwrapper is gone. As a result,hk checknow shows the patch golines would apply instead of a list of files.
Fixed
- More
check_diffpatches apply (#1513, #1528, @jdx). Before, these patches were rejected and hk ran the fixer:go mod tidy -diff'scurrent//tidy/headers- isort's
:before/:afterlabels - hunk lines that look like
---/+++headers - changes to lines with CRLF endings
- file names containing tabs, which diff headers now quote the way git does
- Files created by
check_diffpatches are staged (#1524, @jdx). For example, whengo mod tidy -diffcreatesgo.sum, hk no longer warns that the file is outside the job.stage = "<JOB_FILES>"now stages the new file in pre-commit. - No frozen spinner left after
check_diffjobs (#1530, @jdx). In an interactive terminal, a read-lockedcheck_diffstep could leave a stale spinner, its command, and its last line of output under the finished step. - Concurrent
hk installruns are reliable (#1533, @jdx). Local install and uninstall now hold a lock while they update thehook.hk-*git config entries, including across linked worktrees. Before, concurrent runs could fail on a missing key or git's config lock and leave hooks half-installed. Fixes #1531.
Full Changelog: v2.3.1...v2.4.0
💚 Sponsor hk
hk is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If hk speeds up your pre-commit loop or makes linting less painful, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep hk fast, free, and independent.