gabriel / muse public
fix patch diff feat/36-diff-pathspec #1 / 1
AI Agent gabriel · 1 day ago · Oct 22, 2026 · Diff

fix(diff): bare path scopes the diff instead of failing as an unknown commit (muse#36)

`muse diff <path>` previously always treated its single positional as a commit ref, failing with "Commit '<path>' not found" for any ordinary file path -- a surprising divergence from `git diff <path>`.

- `commit_a` with no `commit_b`: try resolving as a commit ref first (100% unchanged behavior for every existing script/ref). Only when that fails, AND the token matches a tracked file or directory (HEAD manifest or working tree), fall back to treating it as a path filter -- folded into the existing --path/-p _filter_manifest machinery, not a parallel code path. - This transparently covers `muse diff -- <path>` too: argparse consumes the literal "--" before run() ever sees args, so an unresolvable commit_a from "-- <path>" is indistinguishable from the bare "<path>" case by the time this code runs -- same fallback, same fix. - Ref/path ambiguity (a token that is BOTH a valid ref and a tracked path): muse always prefers the ref, deterministically. True forced-path disambiguation via a literal "--" isn't observable after argparse finishes parsing without deeper parser-pipeline changes; this is the alternative the ticket explicitly sanctioned when "--" can't be detected. Documented in --help and the Gitism Glossary. - Exit-code consistency (ticket problem 3): verified already fixed on current code -- both the bare-path and "--"-prefixed forms of an unresolvable token already exit 1 identically; added a regression test so this can't silently regress.

Found and fixed a real, independent bug while building this: resolve_commit_ref (and thus every ref-taking command) crashed with an uncaught ValueError instead of returning "not found" whenever a ref-like candidate string contained '/' and wasn't a branch or remote-tracking ref -- read_version_tag called version_tag_path unconditionally, which raises for path-traversal- looking names (by design, for its own normal callers) but was never meant to be exposed to callers like resolve_commit_ref that probe arbitrary strings and need a clean None for "not a tag" rather than a crash. This blocked bare-path scoping for every path under a subdirectory (e.g. `muse diff sub/file.py`) -- not something this ticket introduced, but something it newly exercised and needed fixed to meet its own acceptance criteria. Fixed in read_version_tag() itself so every caller benefits, not just diff.

14 new tests in tests/test_diff_pathspec.py covering bare-path scoping (file and directory), the "--" separator, ref-vs-path ambiguity, exit-code consistency, and regression guards for --path/-p and the two-ref/no-args forms staying unaffected. 1 new regression test in test_core_version_tags.py for the read_version_tag fix. Confirmed via genuine RED (reverted both fixes) -> 7 of 15 failed as expected -> GREEN. 672-test regression sweep across every diff/version-tag/checkout-relevant suite passes with zero regressions.

Co-Authored-By: Claude Sonnet 5 <[email protected]>

sha256:0d3de0ee53b63667de053f3ad712184124142df86473b80939347b487a65d6af sha
+33 ~6 symbols
5 changed · 1252 in snapshot files
sha256:f8e2c4a67728b36218f4e212922ce88f86864cb7bc7ab27f15109c7a4320a170 snapshot
+33
symbols added
~6
symbols modified
5
files changed
1252
files in snapshot
0
dead code introduced
Semantic Changes 39 symbols
~ tests/test_diff_pathspec.py .py 31 symbols added
+ TestBarePathScoping class class TestBarePathScoping L62–90
+ test_bare_directory_path_scopes_by_prefix method method test_bare_directory_path_scopes_by_prefix L77–82
+ test_bare_path_matches_text_output_of_explicit_path_flag method method test_bare_path_matches_text_output_of_explicit_path_flag L69–75
+ test_bare_path_scopes_diff_instead_of_erroring method method test_bare_path_scopes_diff_instead_of_erroring L63–67
+ test_bare_path_with_no_changes_reports_no_changes method method test_bare_path_with_no_changes_reports_no_changes L84–90
+ TestDashDashSeparator class class TestDashDashSeparator L93–102
+ test_dash_dash_path_json_matches_bare_path method method test_dash_dash_path_json_matches_bare_path L98–102
+ test_dash_dash_path_scopes_diff method method test_dash_dash_path_scopes_diff L94–96
+ TestDefaultAndTwoRefFormsUnaffected class class TestDefaultAndTwoRefFormsUnaffected L153–171
+ test_no_args_default_diff_unaffected method method test_no_args_default_diff_unaffected L154–157
+ test_single_valid_ref_unaffected method method test_single_valid_ref_unaffected L164–171
+ test_two_ref_form_unaffected method method test_two_ref_form_unaffected L159–162
+ TestPathFlagUnaffected class class TestPathFlagUnaffected L139–150
+ test_path_flag_still_works method method test_path_flag_still_works L142–145
+ test_short_path_flag_still_works method method test_short_path_flag_still_works L147–150
+ TestRefVsPathAmbiguity class class TestRefVsPathAmbiguity L105–136
+ test_exit_code_consistent_for_unresolvable_single_positional method method test_exit_code_consistent_for_unresolvable_single_positional L128–136
+ test_unresolvable_token_still_errors_consistently method method test_unresolvable_token_still_errors_consistently L106–110
+ test_valid_ref_still_takes_precedence_over_same_named_path method method test_valid_ref_still_takes_precedence_over_same_named_path L112–126
+ _commit function function _commit L40–42
+ _diff function function _diff L36–37
+ _invoke function function _invoke L27–33
+ CliRunner import import CliRunner L22–22
+ InvokeResult import import InvokeResult L22–22
+ annotations import import annotations L14–14
+ json import import json L16–16
+ os import import os L17–17
+ pathlib import import pathlib L18–18
+ pytest import import pytest L20–20
+ repo function function repo L46–59
+ runner variable variable runner L24–24
~ docs/agent-guide.md .md 1 symbol added, 2 symbols modified
+ muse diff <path> — bare paths now scope the diff, like git (muse#36) section muse diff <path> — bare paths now scope the diff, like git (muse#36) L1502–1525
~ muse/cli/commands/diff.py .py 2 symbols modified
~ muse/core/version_tags.py .py 1 symbol modified
~ tests/test_core_version_tags.py .py 1 symbol added, 1 symbol modified
+ test_returns_none_for_syntactically_invalid_tag_name_instead_of_raising method method test_returns_none_for_syntactically_invalid_tag_name_instead_of_raising L572–585
Files Changed
+1 ~4
1252 in snapshot
← Older Oldest on feat/36-diff-pathspec
All commits
Newer → Latest on feat/36-diff-pathspec

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:0d3de0ee53b63667de053f3ad712184124142df86473b80939347b487a65d6af --body "your comment"