feat: apply check_diff output directly using git apply#561
Conversation
db2c226 to
7bf900c
Compare
apply_check_diff option to apply diff output directly7bf900c to
858e7a3
Compare
858e7a3 to
2887088
Compare
|
any thoughts on this one @thejcannon ? |
When `check_diff` is defined, hk will now attempt to apply the diff output directly using `git apply` instead of running the fix command. This is more efficient for tools that produce standard unified diffs (black, ruff, shfmt, etc.). If patch application fails, hk falls back to running the fix command. Changes: - Always run check_first when check_diff is defined to enable diff application - Add apply_diff_output() method that pipes diff to `git apply -p1` - Add bats tests to verify diff application and fallback behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2887088 to
7a128ac
Compare
I'll be back at a keyboard to take a closer look next week. |
| run cat test.txt | ||
| assert_output "hello | ||
| FIXED" | ||
| } |
There was a problem hiding this comment.
Probably deserves a test for the behavior when check_diff exits nonzero (but still outputs a diff)
And another one for the tools that output a diff but also extra information (I forget which ones do this)
Add tests addressing PR feedback: - Test that valid diffs are applied even when check_diff exits nonzero - Test that diffs embedded in diagnostic output are correctly applied 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, when check_diff was defined, the diff would be applied even during `hk check` runs, modifying files unexpectedly. The condition at line 703 only checked job.run_type (which was temporarily changed to CheckType::Diff) without verifying the original run type was Fix. Fix by adding a check for prev_run_type == RunType::Fix before applying diffs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After merging main, RunType::Check is now a unit variant (not a tuple with CheckType). Simplified the condition to use the already-computed is_check_diff variable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| } else { | ||
| debug!("{}: no diff content to apply", self.name); | ||
| return Ok(false); | ||
| }; |
There was a problem hiding this comment.
Diff content selection inconsistent with file parsing
The apply_diff_output function's comment says "Combine stdout and stderr" but the code only selects one or the other, preferring stdout if non-empty. This is inconsistent with filter_files_from_check_diff which iterates over both streams (for output in [stdout, stderr]) to parse files. If a tool outputs diagnostics to stdout and the actual diff to stderr, the file parsing will correctly find files from stderr, but apply_diff_output will pass the diagnostics to git apply, causing it to fail and fall back to the fixer unnecessarily.
Simplify diff handling to only use stdout, removing the inconsistency where filter_files_from_check_diff iterated over both stdout/stderr but apply_diff_output selected only one. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The apply_diff_output function was hardcoded to use -p1, which strips the first path component. This works for git-style diffs with a/b prefixes, but breaks diffs without prefixes (e.g., "--- src/file.py"). Now detects the diff format (same logic as filter_files_from_check_diff) and uses -p1 for a/b prefixed diffs, -p0 otherwise. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## [1.29.0](https://github.com/jdx/hk/compare/v1.28.0..v1.29.0) - 2026-01-06 ### 🚀 Features - **(ghalint)** add ghalint config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#551](#551) - **(pinact)** add pinact config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#552](#552) - **(pkl)** add pkl file type by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#571](#571) - **(stylua)** use check_diff instead of check command by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#563](#563) - **(vale)** add vale config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#554](#554) - **(zizmor)** add zizmor config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#550](#550) - add rumdl config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#541](#541) - add selene config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#544](#544) - Add `fix` to `cargo_check` builtin (running `cargo fix`) by [@thejcannon](https://github.com/thejcannon) in [#555](#555) - Lua file type support by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#558](#558) - add editorconfig-checker config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#557](#557) - add ryl config to hk builtin config by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#543](#543) - add buf_format builtin using buf cli by [@joonas](https://github.com/joonas) in [#565](#565) - add buf_lint builtin using buf cli by [@joonas](https://github.com/joonas) in [#562](#562) - apply check_diff output directly using git apply by [@jdx](https://github.com/jdx) in [#561](#561) ### 🐛 Bug Fixes - **(ci)** pre-install nightly toolchain for cargo_check tests by [@joonas](https://github.com/joonas) in [#567](#567) - **(rubocop)** fix rubocop fix command by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#572](#572) - Fix the `jq` builtin by [@thejcannon](https://github.com/thejcannon) in [#533](#533) - Fix the `rg` command in the tasks by [@thejcannon](https://github.com/thejcannon) in [#534](#534) - Make `settings.toml` and `Config.pkl` agree on keys by [@thejcannon](https://github.com/thejcannon) in [#539](#539) - Add `check_list_files` to `cargo_fmt` builtin by [@thejcannon](https://github.com/thejcannon) in [#542](#542) - Remove `jq`'s `check` command by [@thejcannon](https://github.com/thejcannon) in [#549](#549) ### 🚜 Refactor - **(ruby)** use types instead of glob for Ruby builtins by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#564](#564) - Simplify RunType/CheckType by [@thejcannon](https://github.com/thejcannon) in [#547](#547) ### 📚 Documentation - **(config)** fix indentations in `workspace_indicator` example by [@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#573](#573) ### 🧪 Testing - Add `actionlint` test by [@thejcannon](https://github.com/thejcannon) in [#537](#537) - shfmt by [@thejcannon](https://github.com/thejcannon) in [#538](#538) - Clean up some `check_` builtin tests by [@thejcannon](https://github.com/thejcannon) in [#536](#536) - Test `rustfmt` and `cargo_format` by [@thejcannon](https://github.com/thejcannon) in [#540](#540) - Add tests to the mypy builtin by [@thejcannon](https://github.com/thejcannon) in [#560](#560) - Add tests to the isort builtin by [@thejcannon](https://github.com/thejcannon) in [#559](#559) ### 📦️ Dependency Updates - update anthropics/claude-code-action digest to 7145c3e by [@renovate[bot]](https://github.com/renovate[bot]) in [#545](#545) - update rust crate serde_json to v1.0.147 by [@renovate[bot]](https://github.com/renovate[bot]) in [#546](#546) - update rust crate serde_json to v1.0.148 by [@renovate[bot]](https://github.com/renovate[bot]) in [#569](#569) - update rust crate tracing to v0.1.44 by [@renovate[bot]](https://github.com/renovate[bot]) in [#570](#570) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Releases `v1.29.0` and syncs version references across the project. > > - Add `1.29.0` entry to `CHANGELOG.md` > - Update version to `1.29.0` in `Cargo.toml`, `hk.usage.kdl`, CLI docs (`docs/cli/index.md`, `docs/cli/commands.json`), and examples > - Replace Pkl `amends`/`import` URLs to `v1.29.0` in docs and sample configs (e.g., `docs/*.md`, `docs/public/*.pkl`, `hk-example.pkl`, `hk.pkl`) > - Update init template and error help to reference `v1.29.0` (`src/cli/init.rs`, `src/config.rs`) > - Refresh `Cargo.lock` with minor dependency bumps > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit e23a13c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: mise-en-dev <123107610+mise-en-dev@users.noreply.github.com>
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [hk](https://github.com/jdx/hk) | minor | `1.28.0` → `1.29.0` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>jdx/hk (hk)</summary> ### [`v1.29.0`](https://github.com/jdx/hk/blob/HEAD/CHANGELOG.md#1290---2026-01-06) [Compare Source](jdx/hk@v1.28.0...v1.29.0) ##### 🚀 Features - **(ghalint)** add ghalint config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​551](jdx/hk#551) - **(pinact)** add pinact config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​552](jdx/hk#552) - **(pkl)** add pkl file type by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​571](jdx/hk#571) - **(stylua)** use check\_diff instead of check command by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​563](jdx/hk#563) - **(vale)** add vale config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​554](jdx/hk#554) - **(zizmor)** add zizmor config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​550](jdx/hk#550) - add rumdl config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​541](jdx/hk#541) - add selene config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​544](jdx/hk#544) - Add `fix` to `cargo_check` builtin (running `cargo fix`) by [@​thejcannon](https://github.com/thejcannon) in [#​555](jdx/hk#555) - Lua file type support by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​558](jdx/hk#558) - add editorconfig-checker config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​557](jdx/hk#557) - add ryl config to hk builtin config by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​543](jdx/hk#543) - add buf\_format builtin using buf cli by [@​joonas](https://github.com/joonas) in [#​565](jdx/hk#565) - add buf\_lint builtin using buf cli by [@​joonas](https://github.com/joonas) in [#​562](jdx/hk#562) - apply check\_diff output directly using git apply by [@​jdx](https://github.com/jdx) in [#​561](jdx/hk#561) ##### 🐛 Bug Fixes - **(ci)** pre-install nightly toolchain for cargo\_check tests by [@​joonas](https://github.com/joonas) in [#​567](jdx/hk#567) - **(rubocop)** fix rubocop fix command by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​572](jdx/hk#572) - Fix the `jq` builtin by [@​thejcannon](https://github.com/thejcannon) in [#​533](jdx/hk#533) - Fix the `rg` command in the tasks by [@​thejcannon](https://github.com/thejcannon) in [#​534](jdx/hk#534) - Make `settings.toml` and `Config.pkl` agree on keys by [@​thejcannon](https://github.com/thejcannon) in [#​539](jdx/hk#539) - Add `check_list_files` to `cargo_fmt` builtin by [@​thejcannon](https://github.com/thejcannon) in [#​542](jdx/hk#542) - Remove `jq`'s `check` command by [@​thejcannon](https://github.com/thejcannon) in [#​549](jdx/hk#549) ##### 🚜 Refactor - **(ruby)** use types instead of glob for Ruby builtins by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​564](jdx/hk#564) - Simplify RunType/CheckType by [@​thejcannon](https://github.com/thejcannon) in [#​547](jdx/hk#547) ##### 📚 Documentation - **(config)** fix indentations in `workspace_indicator` example by [@​hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#​573](jdx/hk#573) ##### 🧪 Testing - Add `actionlint` test by [@​thejcannon](https://github.com/thejcannon) in [#​537](jdx/hk#537) - shfmt by [@​thejcannon](https://github.com/thejcannon) in [#​538](jdx/hk#538) - Clean up some `check_` builtin tests by [@​thejcannon](https://github.com/thejcannon) in [#​536](jdx/hk#536) - Test `rustfmt` and `cargo_format` by [@​thejcannon](https://github.com/thejcannon) in [#​540](jdx/hk#540) - Add tests to the mypy builtin by [@​thejcannon](https://github.com/thejcannon) in [#​560](jdx/hk#560) - Add tests to the isort builtin by [@​thejcannon](https://github.com/thejcannon) in [#​559](jdx/hk#559) ##### 📦️ Dependency Updates - update anthropics/claude-code-action digest to [`7145c3e`](jdx/hk@7145c3e) by [@​renovate\[bot\]](https://github.com/renovate\[bot]) in [#​545](jdx/hk#545) - update rust crate serde\_json to v1.0.147 by [@​renovate\[bot\]](https://github.com/renovate\[bot]) in [#​546](jdx/hk#546) - update rust crate serde\_json to v1.0.148 by [@​renovate\[bot\]](https://github.com/renovate\[bot]) in [#​569](jdx/hk#569) - update rust crate tracing to v0.1.44 by [@​renovate\[bot\]](https://github.com/renovate\[bot]) in [#​570](jdx/hk#570) - lock file maintenance by [@​renovate\[bot\]](https://github.com/renovate\[bot]) in [#​553](jdx/hk#553) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi43NS4xIiwidXBkYXRlZEluVmVyIjoiNDIuNzUuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
Summary
When
check_diffis defined, hk will now apply the diff output directly usinggit applyinstead of running the fix command. This is more efficient for tools that produce standard unified diffs (black, ruff, shfmt, etc.) since we can skip invoking the fixer entirely.Falls back to the fix command if
git applyfails.Changes
check_firstwhencheck_diffis definedgit apply -p1 --whitespace=nowarnTest plan
🤖 Generated with Claude Code
Note
Implements direct application of
check_diffoutput to speed up fixes and reduce extra fixer runs.check_firstwhencheck_diffis defined; computes<JOB_FILES>filtering once for consistencyapply_diff_output()usesgit apply -p1(or-p0when noa//b/prefixes) with--whitespace=nowarn; runs in stepdirwhen setcheck_difffails with a diff, parse file list from stdout-only and try to apply it; skip fixer on success, otherwise fall back tofixfilter_files_from_check_diff()now reads paths from stdout only and supports diffs with/withouta//b/prefixes and timestampsConfig.pkldocs forcheck_diffbehaviorWritten by Cursor Bugbot for commit f2d0b9d. This will update automatically on new commits. Configure here.