Skip to content

feat: apply check_diff output directly using git apply#561

Merged
jdx merged 7 commits intomainfrom
feat/apply-check-diff
Jan 6, 2026
Merged

feat: apply check_diff output directly using git apply#561
jdx merged 7 commits intomainfrom
feat/apply-check-diff

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Dec 30, 2025

Summary

When check_diff is defined, hk will now 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.) since we can skip invoking the fixer entirely.

Falls back to the fix command if git apply fails.

Changes

  • Always run check_first when check_diff is defined
  • Apply diff output via git apply -p1 --whitespace=nowarn
  • Skip fixer when diff applies successfully
  • Fall back to fixer on failure

Test plan

  • New bats tests for diff application and fallback

🤖 Generated with Claude Code


Note

Implements direct application of check_diff output to speed up fixes and reduce extra fixer runs.

  • Always enables check_first when check_diff is defined; computes <JOB_FILES> filtering once for consistency
  • New apply_diff_output() uses git apply -p1 (or -p0 when no a//b/ prefixes) with --whitespace=nowarn; runs in step dir when set
  • On fix runs: if check_diff fails with a diff, parse file list from stdout-only and try to apply it; skip fixer on success, otherwise fall back to fix
  • filter_files_from_check_diff() now reads paths from stdout only and supports diffs with/without a//b/ prefixes and timestamps
  • Updates Config.pkl docs for check_diff behavior
  • Adds bats tests covering direct apply, fallback on invalid diff, nonzero-exit with valid diff, check-mode non-modifying, mixed diagnostic output, and no-prefix diffs

Written by Cursor Bugbot for commit f2d0b9d. This will update automatically on new commits. Configure here.

@jdx jdx force-pushed the feat/apply-check-diff branch from db2c226 to 7bf900c Compare December 30, 2025 18:57
@jdx jdx changed the title feat: add apply_check_diff option to apply diff output directly feat: apply check_diff output directly using git apply Dec 30, 2025
@jdx jdx force-pushed the feat/apply-check-diff branch from 7bf900c to 858e7a3 Compare December 30, 2025 19:05
Comment thread src/step.rs Outdated
@jdx jdx force-pushed the feat/apply-check-diff branch from 858e7a3 to 2887088 Compare December 30, 2025 19:28
Comment thread src/step.rs
@jdx
Copy link
Copy Markdown
Owner Author

jdx commented Dec 30, 2025

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>
@jdx jdx force-pushed the feat/apply-check-diff branch from 2887088 to 7a128ac Compare December 30, 2025 19:44
@thejcannon
Copy link
Copy Markdown
Contributor

any thoughts on this one @thejcannon ?

I'll be back at a keyboard to take a closer look next week.

run cat test.txt
assert_output "hello
FIXED"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@jdx jdx enabled auto-merge (squash) January 6, 2026 14:37
Comment thread src/step.rs
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>
@jdx jdx disabled auto-merge January 6, 2026 14:54
jdx and others added 2 commits January 6, 2026 08:54
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>
Comment thread src/step.rs
} else {
debug!("{}: no diff content to apply", self.name);
return Ok(false);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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>
Comment thread src/step.rs Outdated
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>
@jdx jdx merged commit 04778eb into main Jan 6, 2026
16 checks passed
@jdx jdx deleted the feat/apply-check-diff branch January 6, 2026 17:35
@jdx jdx mentioned this pull request Jan 6, 2026
jdx added a commit that referenced this pull request Jan 6, 2026
## [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>
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Jan 9, 2026
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 [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;551](jdx/hk#551)
- **(pinact)** add pinact config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;552](jdx/hk#552)
- **(pkl)** add pkl file type by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;571](jdx/hk#571)
- **(stylua)** use check\_diff instead of check command by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;563](jdx/hk#563)
- **(vale)** add vale config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;554](jdx/hk#554)
- **(zizmor)** add zizmor config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;550](jdx/hk#550)
- add rumdl config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;541](jdx/hk#541)
- add selene config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;544](jdx/hk#544)
- Add `fix` to `cargo_check` builtin (running `cargo fix`) by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;555](jdx/hk#555)
- Lua file type support by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;558](jdx/hk#558)
- add editorconfig-checker config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;557](jdx/hk#557)
- add ryl config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;543](jdx/hk#543)
- add buf\_format builtin using buf cli by [@&#8203;joonas](https://github.com/joonas) in [#&#8203;565](jdx/hk#565)
- add buf\_lint builtin using buf cli by [@&#8203;joonas](https://github.com/joonas) in [#&#8203;562](jdx/hk#562)
- apply check\_diff output directly using git apply by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;561](jdx/hk#561)

##### 🐛 Bug Fixes

- **(ci)** pre-install nightly toolchain for cargo\_check tests by [@&#8203;joonas](https://github.com/joonas) in [#&#8203;567](jdx/hk#567)
- **(rubocop)** fix rubocop fix command by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;572](jdx/hk#572)
- Fix the `jq` builtin by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;533](jdx/hk#533)
- Fix the `rg` command in the tasks by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;534](jdx/hk#534)
- Make `settings.toml` and `Config.pkl` agree on keys by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;539](jdx/hk#539)
- Add `check_list_files` to `cargo_fmt` builtin by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;542](jdx/hk#542)
- Remove `jq`'s `check` command by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;549](jdx/hk#549)

##### 🚜 Refactor

- **(ruby)** use types instead of glob for Ruby builtins by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;564](jdx/hk#564)
- Simplify RunType/CheckType by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;547](jdx/hk#547)

##### 📚 Documentation

- **(config)** fix indentations in `workspace_indicator` example by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;573](jdx/hk#573)

##### 🧪 Testing

- Add `actionlint` test by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;537](jdx/hk#537)
- shfmt by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;538](jdx/hk#538)
- Clean up some `check_` builtin tests by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;536](jdx/hk#536)
- Test `rustfmt` and `cargo_format` by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;540](jdx/hk#540)
- Add tests to the mypy builtin by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;560](jdx/hk#560)
- Add tests to the isort builtin by [@&#8203;thejcannon](https://github.com/thejcannon) in [#&#8203;559](jdx/hk#559)

##### 📦️ Dependency Updates

- update anthropics/claude-code-action digest to [`7145c3e`](jdx/hk@7145c3e) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;545](jdx/hk#545)
- update rust crate serde\_json to v1.0.147 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;546](jdx/hk#546)
- update rust crate serde\_json to v1.0.148 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;569](jdx/hk#569)
- update rust crate tracing to v0.1.44 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;570](jdx/hk#570)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;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-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants