gabriel / muse public
feat patch checkout feat/46-reflog-checkout-at-n #1 / 1
AI Agent gabriel · 2 days ago · Oct 22, 2026 · Diff

feat(checkout): accept @{N} reflog refs for detached-HEAD recovery (muse#46)

Verified muse#46's full scope (pull/shelf reflog coverage, reflog expire/delete/exists subcommands, gc integration, @{N} resolution in rev-parse/reset/read/diff) was already implemented and covered by 491 passing tests (RL_01-RL_30) across tests/test_cmd_reflog_coverage.py, tests/test_cmd_reflog_expire.py, tests/test_cmd_reflog_delete.py, tests/test_cmd_gc_reflog.py, tests/test_reflog_ref_resolution.py, and tests/test_cmd_reflog_exists.py.

One real gap found during live verification: `muse checkout @{N}` was listed in the ticket's "Commands that must accept @{N}" table but actually raised "Invalid branch name" -- checkout.py's early commit-ref detection (_is_commit_ref) recognized sha256:/hex/~N forms but not @{N}/branch@{N}, so "@{1}" fell through to validate_branch_name, which rejects "{"/"}" as forbidden characters, before ever reaching resolve_commit_ref (the only code path that understands reflog refs).

Fixed by adding muse/core/reflog.py::is_reflog_ref_spec() -- a cheap syntactic @{...} check reusing the existing _AT_N_RE pattern -- and wiring it into checkout.py's _is_commit_ref detection. 4 new tests in tests/test_reflog_ref_resolution.py::TestCheckoutAtN confirm detached-HEAD checkout via @{N} and branch@{N}, out-of-range rejection, and that the "Invalid branch name" regression never recurs. Confirmed via genuine RED (reverted the checkout.py wiring, 3/4 new tests failed) -> GREEN.

Live-verified the ticket's own end-to-end recovery scenario via muse-dev: commit A -> commit B -> muse reset --hard HEAD~1 -> muse reset @{1} -> back at commit B.

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

sha256:c6483b12ab17f2b6b1de0abb0b7f40fe0c53456c1e503f00136d903ed8d48e0e sha
+7 ~2 symbols
3 changed · 1249 in snapshot files
sha256:9e7a45fb90b7ff0b41dc035ec0a30726b0376c6948b6e9bcfa3fe37762040eb2 snapshot
+7
symbols added
~2
symbols modified
3
files changed
1249
files in snapshot
0
dead code introduced
Semantic Changes 9 symbols
~ muse/cli/commands/checkout.py .py 1 symbol added, 2 symbols modified
+ is_reflog_ref_spec import import is_reflog_ref_spec L91–91
~ run
~ muse/core/reflog.py .py 1 symbol added
+ is_reflog_ref_spec function function is_reflog_ref_spec L999–1008
~ tests/test_reflog_ref_resolution.py .py 5 symbols added
+ TestCheckoutAtN class class TestCheckoutAtN L244–291
+ test_checkout_at_n_does_not_raise_invalid_branch_name method method test_checkout_at_n_does_not_raise_invalid_branch_name L260–270
+ test_checkout_at_n_out_of_range_exits_user_error method method test_checkout_at_n_out_of_range_exits_user_error L284–291
+ test_checkout_at_one_detaches_at_second_commit method method test_checkout_at_one_detaches_at_second_commit L247–258
+ test_checkout_branch_at_n_detaches_at_branch_reflog_commit method method test_checkout_branch_at_n_detaches_at_branch_reflog_commit L272–282
Files Changed
~3
1249 in snapshot
← Older Oldest on feat/46-reflog-checkout-at-n
All commits
Newer → Latest on feat/46-reflog-checkout-at-n

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:c6483b12ab17f2b6b1de0abb0b7f40fe0c53456c1e503f00136d903ed8d48e0e --body "your comment"