Skip to content

feat(generate): let git hooks carry mise flags - #11820

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:feat/git-pre-commit-mise-args
Aug 10, 2026
Merged

jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:feat/git-pre-commit-mise-args

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Closes #4304.

The problem

git runs hooks from the repository root, so a generated hook's mise run <task> never finds a config kept in a subdirectory. The reporter's only option was editing the hook by hand.

-C looks like it should help, and it is accepted — but it applies to the generate invocation, not to the hook it writes. Measured on v2026.8.2:

$ mise generate git-pre-commit --task lint -C subdir
#!/bin/sh
STAGED="$(git diff-index --cached --name-only -z HEAD | xargs -0)"
export STAGED
export MISE_PRE_COMMIT=1
exec mise run lint          # -C is gone

GitPreCommit held only task, write and hook, and the script was a fixed template, so there was nothing to embed with.

Why a passthrough rather than a --cd of its own

Adding a dedicated --cd would fix the reported case and leave every neighbouring flag stuck. -C is one of a dozen global flags — -E/--env, -j/--jobs, --locked, --raw, -y/--yes and so on — and a hook can plausibly want any of them. One option per flag does not scale, and picking -C alone would be arbitrary.

So everything after -- is inserted between mise and run:

$ mise generate git-pre-commit --task lint -- -C subdir -E ci
...
exec mise -C subdir -E ci run lint "$@"

last = true means those tokens are positional, so -C is not swallowed by the global parser on the way in. That is the same shape Cli::task_args_last already uses.

Properties

  • With no arguments the output is byte-identical to before. Hooks already written stay valid, and a test pins this.
  • Arguments are quoted with shell_words, so -C "my dir" survives as one word. generate task-stubs already uses shell_words::quote for the same reason, so this follows an existing pattern rather than introducing one.
  • The flags land before run, which is where mise expects global flags.

Scope

{task} is still interpolated unquoted, as it was. Running it through shell_words::quote would be a small robustness win but produces identical output for ordinary task names, so it is left for a separate change rather than mixed in here.

Tests

Three unit tests alongside the existing two:

  • no arguments leaves exec mise run <task> "$@" untouched
  • ["-C", "subdir", "-E", "ci"] produces exec mise -C subdir -E ci run lint "$@"
  • ["-C", "my dir"] produces -C 'my dir' rather than a bare space

No e2e: the generated text is fully determined by these, and firing a real hook would depend on git state for no extra coverage.

Summary by CodeRabbit

  • New Features

    • Git pre-commit hooks now support passing additional mise arguments.
    • Arguments are safely preserved in generated shell commands, including values that require quoting.
    • Added support for configuring hooks from subdirectories with -C.
  • Documentation

    • Updated CLI help text, usage references, and examples to explain the new options.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e5bd2faf-8942-4911-8b0b-8a4008cdf79c

📥 Commits

Reviewing files that changed from the base of the PR and between b819a15 and 9195acc.

📒 Files selected for processing (5)
  • docs/cli/generate.md
  • docs/cli/generate/git-pre-commit.md
  • docs/cli/index.md
  • man/man1/mise.1
  • mise.usage.kdl

📝 Walkthrough

Walkthrough

Git pre-commit generation now accepts trailing mise arguments, shell-quotes them, and places them before run. CLI parsing, hook output, and documentation cover the new arguments, including -C subdir.

Changes

Git pre-commit argument support

Layer / File(s) Summary
Argument contract and hook generation
src/cli/generate/git_pre_commit.rs
GitPreCommit stores trailing mise arguments. Generated hooks shell-quote and place them before run.
Argument construction and tests
src/cli/generate/git_pre_commit.rs
Tests cover empty arguments, argument placement, shell quoting, and CLI parsing after --.
CLI usage documentation
docs/cli/generate.md, docs/cli/generate/git-pre-commit.md, docs/cli/index.md, man/man1/mise.1, mise.usage.kdl
Documentation describes variadic MISE_ARG values and the -C subdir configuration example.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • jdx/mise#11801: Both PRs modify GitPreCommit hook command generation and argument handling.

Suggested reviewers: jdx

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant GitPreCommit
  participant Hook
  CLI->>GitPreCommit: pass trailing mise arguments
  GitPreCommit->>Hook: generate shell-quoted command
  Hook->>Hook: run mise arguments before run
Loading

Poem

A rabbit checks the hook,
mise args take their place.
Quotes keep words together,
run follows in the trace.
-C subdir joins the race.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding mise flag support to generated Git hooks.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR allows generated Git hooks to preserve mise flags supplied after --.

  • Adds a variadic mise_args field and shell-quotes those arguments before embedding them ahead of run.
  • Adds rendering tests and parser-level coverage for the documented passthrough invocation.
  • Regenerates CLI and man-page documentation with usage details and examples.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the new parser-level test now verifies that passthrough tokens reach the nested command without being consumed as global flags.

Important Files Changed

Filename Overview
src/cli/generate/git_pre_commit.rs Adds safely quoted flag passthrough to generated hooks and completes the previously requested parser-level regression coverage.
mise.usage.kdl Defines the variadic post-separator argument and documents its placement and subdirectory use case.
docs/cli/generate/git-pre-commit.md Regenerates command documentation to describe and demonstrate hook-level mise flags.
man/man1/mise.1 Updates the generated manual with the new variadic argument and corresponding explanation.

Reviews (4): Last reviewed commit: "feat(generate): let git hooks carry mise..." | Re-trigger Greptile

Comment thread src/cli/generate/git_pre_commit.rs Resolved

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (3)
src/cli/generate/git_pre_commit.rs (3)

135-141: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make the no-argument test enforce byte identity.

Line [140] only checks that the expected command appears somewhere in the output. The test still passes if the hook contains extra output or a duplicate command. Compare out with a frozen pre-change hook fixture when byte-identical output is required.

Suggested assertion
-        assert!(out.contains(r#"exec mise run pre-commit "$@""#), "{out}");
+        assert_eq!(out, EXPECTED_NO_MISE_ARGS_HOOK);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/generate/git_pre_commit.rs` around lines 135 - 141, Update
no_mise_args_leaves_the_command_unchanged to assert exact byte-for-byte equality
between out and the frozen pre-change hook fixture, rather than checking that
the command merely appears via contains. Preserve the existing
generate("pre-commit", "pre-commit") invocation and use the fixture’s complete
expected output so extra or duplicate content fails the test.

106-111: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a parser-level test for the new clap contract.

Line [106] constructs GitPreCommit directly. The tests do not parse mise generate git-pre-commit -- -C subdir -E ci. A regression in #[clap(last = true, ...)] could reject this command while the current tests still pass. Add a test through the actual CLI parser and verify the parsed mise_args values. Clap defines last as a positional argument accepted after --. (docs.rs)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/generate/git_pre_commit.rs` around lines 106 - 111, Add a
parser-level test for the CLI command that parses `mise generate git-pre-commit
-- -C subdir -E ci`, rather than constructing `GitPreCommit` directly through
`generate_with_args`. Assert that parsing succeeds and produces `mise_args`
containing `-C`, `subdir`, `-E`, and `ci`, covering the `clap` positional `last`
behavior after `--`.

154-160: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert shell-argument semantics, not quote spelling.

Line [158] hardcodes single quotes, but shell_words::join only guarantees that output is shell-safe, not that it uses one quoting style. Use shell_words::split on the generated exec command and compare the parsed argument list so tests cover argument boundaries instead of a specific quote character.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/generate/git_pre_commit.rs` around lines 154 - 160, Update the
mise_args_are_shell_quoted test to validate shell argument boundaries rather
than requiring single-quote syntax. Parse the generated exec command with
shell_words::split and compare the resulting arguments, preserving the expected
“my dir” argument as one element and the existing "$@" behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/cli/generate/git_pre_commit.rs`:
- Around line 135-141: Update no_mise_args_leaves_the_command_unchanged to
assert exact byte-for-byte equality between out and the frozen pre-change hook
fixture, rather than checking that the command merely appears via contains.
Preserve the existing generate("pre-commit", "pre-commit") invocation and use
the fixture’s complete expected output so extra or duplicate content fails the
test.
- Around line 106-111: Add a parser-level test for the CLI command that parses
`mise generate git-pre-commit -- -C subdir -E ci`, rather than constructing
`GitPreCommit` directly through `generate_with_args`. Assert that parsing
succeeds and produces `mise_args` containing `-C`, `subdir`, `-E`, and `ci`,
covering the `clap` positional `last` behavior after `--`.
- Around line 154-160: Update the mise_args_are_shell_quoted test to validate
shell argument boundaries rather than requiring single-quote syntax. Parse the
generated exec command with shell_words::split and compare the resulting
arguments, preserving the expected “my dir” argument as one element and the
existing "$@" behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f68bd09-469d-408d-a81d-d4994879acd9

📥 Commits

Reviewing files that changed from the base of the PR and between f6ac131 and ef5e75a.

📒 Files selected for processing (1)
  • src/cli/generate/git_pre_commit.rs

@JamBalaya56562
JamBalaya56562 force-pushed the feat/git-pre-commit-mise-args branch 2 times, most recently from 080985d to b819a15 Compare August 10, 2026 01:00
@JamBalaya56562

Copy link
Copy Markdown
Contributor Author

Two of the three were worth acting on; the parser one had already landed.

Byte identity for the no-argument case — fixed. The comment claimed byte-identical output while the assertion only did a contains, so an extra or duplicated line would have slipped through. It now compares the whole hook against a frozen fixture of the pre-change output.

Quote spelling — fixed, and your framing was the right one. shell_words::join only promises shell-safety, so asserting single quotes pinned an implementation detail. The test now splits the exec line back apart with shell_words::split and compares the argument list:

assert_eq!(words, ["exec", "mise", "-C", "my dir", "run", "lint", "$@"]);

That checks the property the feature actually needs — my dir survives as one word — rather than how it was spelled.

Parser-level test — already added before this review landed, in response to the same point from Greptile. passthrough_args_reach_the_command_through_the_parser parses the documented invocation through Cli::command() and additionally asserts that cd and env are not set, which is the half that catches -C being swallowed as a global flag.

A generated hook runs `mise run <task>` from the repository root, because that
is where git invokes hooks. A config kept in a subdirectory is therefore never
found, and the only fix was editing the hook by hand.

Everything after `--` is now inserted between `mise` and `run`, so
`-- -C subdir` gets the hook there. Passing flags through rather than adding a
`--cd` of its own keeps every other global flag — `-E`, `--locked`, `-j` —
reachable by the same mechanism instead of one option per flag.

Arguments are joined with shell_words so one containing a space stays a single
word. With none given the command is byte-identical to before.
@JamBalaya56562
JamBalaya56562 force-pushed the feat/git-pre-commit-mise-args branch from b819a15 to 9195acc Compare August 10, 2026 01:10
@jdx
jdx merged commit 9074c1c into jdx:main Aug 10, 2026
31 checks passed
@JamBalaya56562
JamBalaya56562 deleted the feat/git-pre-commit-mise-args branch August 10, 2026 01:56
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