gabriel / muse public
feat patch checkout task/checkout-autostash #1 / 1
gabriel · 167 days ago · Apr 13, 2026 · Diff

feat(checkout): --autostash flag with full test coverage

- Add --autostash to muse checkout: stash → switch → pop via try/finally, so the stash is always popped regardless of success or SystemExit - Stash message mirrors git idiom: 'WIP on <branch>: autostash' - --autostash + --dry-run: no stash created, assume clean for preview - --autostash + -b: no-op (no snapshot switch, changes preserved) - --autostash is mutually exclusive with --force and --merge - Add _stash_push_programmatic / _stash_pop_programmatic to stash.py as a clean programmatic API (no argparse, no prints, raises exceptions) - _stash_pop_programmatic wraps OSError from object-store integrity checks into RuntimeError for consistent caller contracts - _apply_autostash: text mode prints to stdout, JSON mode to stderr (keeps stdout machine-readable in --json mode) - Improve guard.py error to list all four remedies: stash / --merge / --force / --autostash with one-line descriptions - 33 tests: unit (programmatic API, _apply_autostash output/streams/ corrupt-object), integration (clean/dirty tree, JSON mode, new files, mutually exclusive flags, --dry-run, -b, round-trips)

sha256:ccc437307c53f10bd6f01134e361b8576bcc669bad0bcbfbdd7d6e31d59d3519 sha
+61 ~3 symbols
sha256:20bdc5bb333af3c1b1675c488941cd93d6fa1169d7007e5723e89e650a6b46f1 snapshot
+61
symbols added
~3
symbols modified
0
dead code introduced
Semantic Changes 64 symbols
~ tests/test_checkout_autostash.py .py 56 symbols added
+ TestApplyAutostash class class TestApplyAutostash L170–205
+ test_corrupt_stash_warns_stderr_does_not_raise method method test_corrupt_stash_warns_stderr_does_not_raise L191–205
+ test_json_mode_prints_to_stderr method method test_json_mode_prints_to_stderr L181–189
+ test_text_mode_prints_to_stdout method method test_text_mode_prints_to_stdout L173–179
+ TestAutostashCLI class class TestAutostashCLI L211–394
+ test_already_on_branch_no_stash method method test_already_on_branch_no_stash L339–343
+ test_autostash_and_force_rejected method method test_autostash_and_force_rejected L291–294
+ test_autostash_and_merge_rejected method method test_autostash_and_merge_rejected L296–299
+ test_autostash_message_matches_git_idiom method method test_autostash_message_matches_git_idiom L360–372
+ test_clean_tree_no_autostash_message method method test_clean_tree_no_autostash_message L223–227
+ test_clean_tree_no_stash_entry_created method method test_clean_tree_no_stash_entry_created L216–221
+ test_create_branch_autostash_is_noop method method test_create_branch_autostash_is_noop L327–335
+ test_dirty_tree_changes_restored_on_new_branch method method test_dirty_tree_changes_restored_on_new_branch L244–248
+ test_dirty_tree_stash_popped_after_switch method method test_dirty_tree_stash_popped_after_switch L238–242
+ test_dirty_tree_switch_succeeds method method test_dirty_tree_switch_succeeds L231–236
+ test_dirty_without_autostash_shows_all_remedies method method test_dirty_without_autostash_shows_all_remedies L347–356
+ test_dry_run_exits_zero_on_existing_branch method method test_dry_run_exits_zero_on_existing_branch L321–323
+ test_dry_run_no_branch_switch method method test_dry_run_no_branch_switch L309–313
+ test_dry_run_no_stash_created method method test_dry_run_no_stash_created L303–307
+ test_dry_run_working_tree_unchanged method method test_dry_run_working_tree_unchanged L315–319
+ test_json_mode_applied_autostash_goes_to_stderr method method test_json_mode_applied_autostash_goes_to_stderr L273–277
+ test_json_mode_stdout_is_valid_json method method test_json_mode_stdout_is_valid_json L259–271
+ test_multiple_dirty_files_all_restored method method test_multiple_dirty_files_all_restored L376–383
+ test_new_file_on_main_survives_to_feat method method test_new_file_on_main_survives_to_feat L281–287
+ test_switch_back_restores_original_state method method test_switch_back_restores_original_state L385–394
+ test_text_mode_prints_stash_then_applied method method test_text_mode_prints_stash_then_applied L250–255
+ TestProgrammaticStashAPI class class TestProgrammaticStashAPI L99–164
+ test_pop_raises_on_empty_stash method method test_pop_raises_on_empty_stash L135–137
+ test_pop_raises_on_out_of_range_index method method test_pop_raises_on_out_of_range_index L139–143
+ test_pop_removes_entry_from_stack method method test_pop_removes_entry_from_stack L153–158
+ test_pop_restores_file_content method method test_pop_restores_file_content L145–151
+ test_push_captures_added_files method method test_push_captures_added_files L125–133
+ test_push_pop_roundtrip_message_preserved method method test_push_pop_roundtrip_message_preserved L160–164
+ test_push_restores_working_tree_to_head method method test_push_restores_working_tree_to_head L119–123
+ test_push_returns_entry_when_dirty method method test_push_returns_entry_when_dirty L109–117
+ test_push_returns_none_when_clean method method test_push_returns_none_when_clean L103–107
+ _commit function function _commit L62–65
+ _invoke function function _invoke L53–59
+ _stash_size function function _stash_size L68–69
+ CliRunner import import CliRunner L38–38
+ InvokeResult import import InvokeResult L38–38
+ _apply_autostash import import _apply_autostash L45–45
+ _load_stash import import _load_stash L40–40
+ _stash_pop_programmatic import import _stash_pop_programmatic L40–40
+ _stash_push_programmatic import import _stash_push_programmatic L40–40
+ annotations import import annotations L27–27
+ contextlib import import contextlib L34–34
+ io import import io L29–29
+ json import import json L30–30
+ os import import os L31–31
+ pathlib import import pathlib L32–32
+ pytest import import pytest L36–36
+ read_current_branch import import read_current_branch L39–39
+ sys import import sys L33–33
+ runner variable variable runner L47–47
+ two_branch_repo function function two_branch_repo L76–93
~ muse/cli/commands/checkout.py .py 3 symbols added, 2 symbols modified
+ _apply_autostash function function _apply_autostash L93–126
+ _stash_pop_programmatic import import _stash_pop_programmatic L80–80
+ _stash_push_programmatic import import _stash_push_programmatic L80–80
~ run
~ muse/cli/commands/stash.py .py 2 symbols added
+ _stash_pop_programmatic function function _stash_pop_programmatic L507–551
+ _stash_push_programmatic function function _stash_push_programmatic L444–504
~ muse/cli/guard.py .py 1 symbol modified
← Older Oldest on task/checkout-autostash
All commits
Newer → Latest on task/checkout-autostash

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