gabriel / muse public
feat patch task/mv-command #1 / 1
gabriel · 167 days ago · Apr 14, 2026 · Diff

feat: implement muse mv — tracked file move with atomic staging

Moves SOURCE to DEST on disk and stages the rename: source gets a 'D' tombstone, destination gets an 'A' entry with the same object_id (no re-hash needed). Staged-only files (never committed) skip the tombstone and just relocate their stage entry. Handles move-into-directory, --force, --dry-run, --json. 29 TDD tests: unit helpers, integration, security (path traversal rejected), stress (200-file repo).

sha256:48c1970ec4e6fd39d6f7bb20570c2d06aadcd4f04a2fc3b1236a5aa1424a6927 sha
+85 ~108 symbols
sha256:98c05dfa21a8b0f668674e85e7bdc939da261bddf7f73049135f3478542d472d snapshot
+85
symbols added
~108
symbols modified
0
dead code introduced
Semantic Changes 193 symbols
~ muse/cli/commands/mv.py .py 28 symbols added
+ _MvResultJson class class _MvResultJson L75–80
+ _get_source_object_id function function _get_source_object_id L142–160
+ _head_manifest function function _head_manifest L88–101
+ _resolve_dest function function _resolve_dest L163–213
+ _resolve_path function function _resolve_path L104–139
+ ExitCode import import ExitCode L55–55
+ Manifest import import Manifest L57–57
+ StagedFileMap import import StagedFileMap L65–65
+ TypedDict import import TypedDict L53–53
+ _json import import _json L49–49
+ annotations import import annotations L46–46
+ argparse import import argparse L48–48
+ assert_write_inside_repo import import assert_write_inside_repo L64–64
+ get_head_commit_id import import get_head_commit_id L57–57
+ logging import import logging L50–50
+ make_entry import import make_entry L65–65
+ pathlib import import pathlib L51–51
+ read_commit import import read_commit L57–57
+ read_current_branch import import read_current_branch L57–57
+ read_snapshot import import read_snapshot L57–57
+ read_stage import import read_stage L65–65
+ require_repo import import require_repo L56–56
+ sanitize_display import import sanitize_display L64–64
+ sys import import sys L52–52
+ write_stage import import write_stage L65–65
+ logger variable variable logger L67–67
+ register function function register L221–264
+ run function function run L267–436
~ tests/test_cmd_mv.py .py 56 symbols added
+ _REPO_ID variable variable _REPO_ID L31–31
+ _commit_files function function _commit_files L61–91
+ _counter variable variable _counter L43–43
+ _env function function _env L57–58
+ _init_repo function function _init_repo L46–54
+ _invoke function function _invoke L94–96
+ _sha function function _sha L39–40
+ CliRunner import import CliRunner L21–21
+ CommitRecord import import CommitRecord L25–25
+ Manifest import import Manifest L26–26
+ SnapshotRecord import import SnapshotRecord L25–25
+ StagedFileMap import import StagedFileMap L27–27
+ annotations import import annotations L13–13
+ compute_commit_id import import compute_commit_id L24–24
+ compute_snapshot_id import import compute_snapshot_id L24–24
+ datetime import import datetime L15–15
+ hashlib import import hashlib L16–16
+ json import import json L17–17
+ make_entry import import make_entry L27–27
+ pathlib import import pathlib L18–18
+ pytest import import pytest L20–20
+ read_stage import import read_stage L27–27
+ write_commit import import write_commit L25–25
+ write_object import import write_object L23–23
+ write_snapshot import import write_snapshot L25–25
+ write_stage import import write_stage L27–27
+ runner variable variable runner L29–29
+ test_get_source_object_id_from_head function function test_get_source_object_id_from_head L133–141
+ test_get_source_object_id_prefers_stage function function test_get_source_object_id_prefers_stage L144–153
+ test_mv_across_subdirectories function function test_mv_across_subdirectories L409–421
+ test_mv_dest_already_tracked_exits_nonzero function function test_mv_dest_already_tracked_exits_nonzero L300–305
+ test_mv_dest_exists_on_disk_exits_nonzero function function test_mv_dest_exists_on_disk_exits_nonzero L308–314
+ test_mv_dest_path_traversal_rejected function function test_mv_dest_path_traversal_rejected L437–442
+ test_mv_dry_run_json function function test_mv_dry_run_json L265–273
+ test_mv_dry_run_no_disk_change function function test_mv_dry_run_no_disk_change L245–252
+ test_mv_dry_run_no_stage_change function function test_mv_dry_run_no_stage_change L255–262
+ test_mv_exit_code_zero_on_success function function test_mv_exit_code_zero_on_success L205–209
+ test_mv_force_allows_tracked_dest function function test_mv_force_allows_tracked_dest L322–330
+ test_mv_force_allows_untracked_dest_on_disk function function test_mv_force_allows_untracked_dest_on_disk L333–341
+ test_mv_help function function test_mv_help L104–108
+ test_mv_into_directory_preserves_object_id function function test_mv_into_directory_preserves_object_id L364–372
+ test_mv_into_existing_directory function function test_mv_into_existing_directory L349–361
+ test_mv_json_output_structure function function test_mv_json_output_structure L225–237
+ test_mv_object_id_preserved function function test_mv_object_id_preserved L193–202
+ test_mv_outside_repo_source_rejected function function test_mv_outside_repo_source_rejected L445–452
+ test_mv_prints_rename_line function function test_mv_prints_rename_line L212–217
+ test_mv_renames_file_on_disk function function test_mv_renames_file_on_disk L161–169
+ test_mv_source_not_on_disk_exits_nonzero function function test_mv_source_not_on_disk_exits_nonzero L290–297
+ test_mv_source_not_tracked_exits_nonzero function function test_mv_source_not_tracked_exits_nonzero L281–287
+ test_mv_source_path_traversal_rejected function function test_mv_source_path_traversal_rejected L429–434
+ test_mv_staged_only_source_updates_stage function function test_mv_staged_only_source_updates_stage L380–401
+ test_mv_stages_dest_as_added function function test_mv_stages_dest_as_ L182–190
+ test_mv_stages_source_as_deleted function function test_mv_stages_source_as_deleted L172–179
+ test_mv_stress_200_files_move_half function function test_mv_stress_200_files_move_half L460–477
+ test_resolve_path_rejects_traversal function function test_resolve_path_rejects_traversal L124–130
+ test_resolve_source_returns_relative_posix function function test_resolve_source_returns_relative_posix L116–121
← Older Oldest on task/mv-command
All commits
Newer → Latest on task/mv-command

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