gabriel / muse public
feat BREAKING main #33 / 55
AI Agent gabriel · 136 days ago · Apr 26, 2026 · Diff

feat: implement muse code migrate — full layout and commit-ID migration

Rewrites all commits using the correct 7-field compute_commit_id formula (repo_id, parents, snapshot_id, message, committed_at, author, signer_public_key).

- Migrates flat objects/commits/snapshots to sha256/ canonical paths - Normalises bare hex refs to sha256: prefix; applies id_map to stale refs - Renames created_on_branch -> branch field; bumps format_version to 8 - Normalises bare base64 signatures to ed25519: prefix - Migrates UUID repo IDs to deterministic sha256: IDs via blob_id() - Updates reflogs with new commit IDs - MigrateResult tracks 14 counters; CLI --json exposes all fields - 92 tests, all passing - Fix _verify_commit_id in store.py: was calling compute_commit_id with wrong 4-field signature; restored correct 7-field call

sha256:b00766e16667508c8dc748f304d2e17f4bd9a3cbefbb1a039b829c4a1fca75f3 sha
+141 ~28 −79 symbols
sha256:b5464b5da9ba00775e9d248ca5aa3f764bc17d3d79f06388459d1744ede6ec3a snapshot
+141
symbols added
~28
symbols modified
−79
symbols removed
0
dead code introduced
Semantic Changes 248 symbols
~ muse/cli/commands/migrate.py .py 1 symbol modified
~ run
~ muse/core/migrate.py .py 14 symbols added, 19 symbols removed, 8 symbols modified
_RawCommitMap variable variable _RawCommitMap L112–112
_normalise_key_id function function _normalise_key_id L82–97
_read_raw_commit function function _read_raw_commit L77–79
_rewrite_commit function function _rewrite_commit L167–281
CommitRecord import import CommitRecord L43–43
DEFAULT_HASH_ALGO import import DEFAULT_HASH_ALGO L40–40
DEFAULT_SIGN_ALGO import import DEFAULT_SIGN_ALGO L40–40
MsgpackDict import import MsgpackDict L40–40
TYPE_CHECKING import import TYPE_CHECKING L35–35
_commits_dir import import _commits_dir L39–39
_merge_state_path import import _merge_state_path L39–39
_muse_dir import import _muse_dir L39–39
_objects_dir import import _objects_dir L39–39
_rebase_merge_dir import import _rebase_merge_dir L39–39
commit_path import import commit_path L43–43
decode_pubkey import import decode_pubkey L40–40
public_key_fingerprint import import public_key_fingerprint L40–40
read_repo_id import import read_repo_id L41–41
write_commit import import write_commit L43–43
+ _canonical_commit_path function function _canonical_commit_path L179–180
+ _committed_at_iso function function _committed_at_iso L79–83
+ _is_flat_commit_path function function _is_flat_commit_path L190–196
+ _make_canonical_dict function function _make_canonical_dict L133–176
+ _migrate_reflogs function function _migrate_reflogs L310–328
+ _migrate_refs function function _migrate_refs L259–273
+ _migrate_remote_refs function function _migrate_remote_refs L276–292
+ _migrate_repo_id function function _migrate_repo_id L295–307
+ _migrate_snapshots function function _migrate_snapshots L231–249
+ _normalise_ref_value function function _normalise_ref_value L252–256
+ _write_raw_commit function function _write_raw_commit L183–187
+ blob_id import import blob_id L37–37
+ datetime import import datetime L29–29
+ json import import json L30–30
~ muse/core/store.py .py 1 symbol modified
~ tests/test_code_migrate.py .py 127 symbols added, 60 symbols removed, 18 symbols modified
TestBranchHeads class class TestBranchHeads L536–569
test_all_branch_heads_updated method method test_all_branch_heads_updated L537–552
test_branch_heads_point_to_v2_ids method method test_branch_heads_point_to_v2_ids L554–569
test_dry_run_makes_no_writes method method test_dry_run_makes_no_writes L270–281
test_dry_run_reports_old_to_new_mapping method method test_dry_run_reports_old_to_new_mapping L283–302
test_dry_run_reports_summary_counts method method test_dry_run_reports_summary_counts L304–317
TestFixture class class TestFixture L217–236
test_v1_commit_id_differs_from_v2 method method test_v1_commit_id_differs_from_v2 L218–228
test_v1_raw_write_is_unreadable_by_read_commit method method test_v1_raw_write_is_unreadable_by_read_commit L230–236
test_running_twice_same_state method method test_running_twice_same_state L647–666
test_json_commits_signed_zero_when_unsigned method method test_json_commits_signed_zero_when_unsigned L845–850
test_json_dry_run_flag_is_true method method test_json_dry_run_flag_is_true L828–836
test_json_live_run_dry_run_is_false method method test_json_live_run_dry_run_is_false L838–843
test_json_output_has_required_keys method method test_json_output_has_required_keys L817–826
TestLegacyFields class class TestLegacyFields L759–808
test_format_version_in_raw_dict_ignored method method test_format_version_in_raw_dict_ignored L760–774
test_old_branch_key_in_raw_dict_handled method method test_old_branch_key_in_raw_dict_handled L776–808
TestLinearChain class class TestLinearChain L372–425
_build_chain method method _build_chain L373–384
test_chain_all_readable_after_migrate method method test_chain_all_readable_after_migrate L386–399
test_chain_old_ids_all_deleted method method test_chain_old_ids_all_deleted L401–408
test_chain_parent_ids_consistent method method test_chain_parent_ids_consistent L410–425
TestMergeCommit class class TestMergeCommit L433–528
test_merge_commit_both_parents_cascaded method method test_merge_commit_both_parents_cascaded L434–498
test_merge_commit_old_file_deleted method method test_merge_commit_old_file_deleted L500–528
_write_legacy_blob method method _write_legacy_blob L675–683
test_blob_already_at_canonical_not_duplicated method method test_blob_already_at_canonical_not_duplicated L720–736
test_legacy_blob_moved_to_canonical_path method method test_legacy_blob_moved_to_canonical_path L685–701
test_legacy_dir_removed_when_empty method method test_legacy_dir_removed_when_empty L703–718
test_aborts_when_merge_state_exists method method test_aborts_when_merge_state_exists L245–253
test_aborts_when_rebase_in_progress method method test_aborts_when_rebase_in_progress L255–261
TestResigning class class TestResigning L863–958
test_dry_run_skips_signing method method test_dry_run_skips_signing L946–958
test_signed_migrate_all_commits_signed method method test_signed_migrate_all_commits_signed L897–912
test_signed_migrate_produces_valid_signatures method method test_signed_migrate_produces_valid_signatures L864–895
test_signed_migrate_signer_public_key_bound_in_commit_id method method test_signed_migrate_signer_public_key_bound_in_commit_id L914–944
_fake_pubkey method method _fake_pubkey L578–580
_fake_sig_bytes method method _fake_sig_bytes L582–583
test_bare_base64_sig_normalised method method test_bare_base64_sig_normalised L585–605
test_empty_sig_not_modified method method test_empty_sig_not_ L627–638
TestSingleRootCommit class class TestSingleRootCommit L325–364
test_already_v2_commit_unchanged method method test_already_v2_commit_unchanged L354–364
test_old_commit_file_deleted method method test_old_commit_file_deleted L343–352
test_rewrites_v1_commit_with_v2_id method method test_rewrites_v1_commit_with_v2_id L326–341
_AUTHOR variable variable _AUTHOR L48–48
_Environ variable variable _Environ L71–71
_REPO_ID variable variable _REPO_ID L47–47
_env function function _env L74–75
_generate_test_key function function _generate_test_key L858–860
_invoke function function _invoke L202–209
_v1_commit_id function function _v1_commit_id L123–138
_v2_commit function function _v2_commit L88–120
_write_v1_commit_raw function function _write_v1_commit_raw L141–193
cli variable variable cli L44–44
CliRunner import import CliRunner L30–30
MsgpackDict import import MsgpackDict L31–31
_merge_state_path import import _merge_state_path L32–32
read_commit import import read_commit L34–34
split_id import import split_id L31–31
runner variable variable runner L45–45
+ TestBranchFieldMigration class class TestBranchFieldMigration L710–782
+ _repo_with_old_branch_key method method _repo_with_old_branch_key L711–724
+ test_branch_value_preserved_after_rename method method test_branch_value_preserved_after_rename L736–743
+ test_canonical_branch_key_unchanged method method test_canonical_branch_key_unchanged L745–758
+ test_created_on_branch_key_renamed_to_branch method method test_created_on_branch_key_renamed_to_branch L726–734
+ test_dry_run_does_not_rename_branch_field method method test_dry_run_does_not_rename_branch_field L774–782
+ test_result_branch_fields_renamed_count method method test_result_branch_fields_renamed_count L760–772
+ TestCommitIdRecomputation class class TestCommitIdRecomputation L839–1026
+ _legacy_root method method _legacy_root L840–848
+ test_branch_head_updated_to_new_id method method test_branch_head_updated_to_new_id L892–898
+ test_commits_rewritten_count method method test_commits_rewritten_count L974–986
+ test_correct_id_commit_not_in_id_map method method test_correct_id_commit_not_in_id_map L962–972
+ test_dry_run_does_not_rewrite_commits method method test_dry_run_does_not_rewrite_commits L1007–1017
+ test_dry_run_id_map_is_populated method method test_dry_run_id_map_is_populated L1019–1026
+ test_id_map_contains_old_to_new_mapping method method test_id_map_contains_old_to_new_mapping L883–890
+ test_linear_chain_all_old_files_deleted method method test_linear_chain_all_old_files_deleted L920–936
+ test_linear_chain_parent_ids_cascade method method test_linear_chain_parent_ids_cascade L900–918
+ test_merge_commit_both_parents_cascaded method method test_merge_commit_both_parents_cascaded L938–960
+ test_multiple_branch_heads_all_updated method method test_multiple_branch_heads_all_updated L988–1005
+ test_new_commit_id_matches_current_formula method method test_new_commit_id_matches_current_formula L871–881
+ test_old_commit_file_deleted_after_rewrite method method test_old_commit_file_deleted_after_rewrite L860–869
+ test_single_commit_wrong_id_rewritten method method test_single_commit_wrong_id_rewritten L850–858
+ TestCommitPathMigration class class TestCommitPathMigration L347–412
+ test_dry_run_does_not_relocate_flat_commit method method test_dry_run_does_not_relocate_flat_commit L402–412
+ test_flat_commit_relocated_to_sha256_subdir method method test_flat_commit_relocated_to_sha256_subdir L348–361
+ test_flat_commit_removed_after_relocation method method test_flat_commit_removed_after_relocation L363–374
+ test_flat_commit_with_correct_id_not_rewritten method method test_flat_commit_with_correct_id_not_rewritten L376–388
+ test_result_commits_relocated_counted method method test_result_commits_relocated_counted L390–400
+ test_dry_run_does_not_update_repo_json method method test_dry_run_does_not_update_repo_json L1211–1218
+ test_dry_run_flag_set_in_result method method test_dry_run_flag_set_in_result L1177–1180
+ test_dry_run_makes_zero_writes_to_commits method method test_dry_run_makes_zero_writes_to_commits L1198–1209
+ test_dry_run_makes_zero_writes_to_objects method method test_dry_run_makes_zero_writes_to_objects L1187–1196
+ test_dry_run_reports_correct_blobs_to_migrate method method test_dry_run_reports_correct_blobs_to_migrate L1233–1240
+ test_dry_run_reports_full_id_map method method test_dry_run_reports_full_id_map L1220–1231
+ test_live_run_flag_false_in_result method method test_live_run_flag_false_in_result L1182–1185
+ TestFormatVersionMigration class class TestFormatVersionMigration L789–832
+ test_current_format_version_unchanged method method test_current_format_version_unchanged L805–818
+ test_old_format_version_bumped_to_8 method method test_old_format_version_bumped_to_8 L790–803
+ test_result_format_versions_bumped_count method method test_result_format_versions_bumped_count L820–832
+ test_running_twice_same_store_state method method test_running_twice_same_store_state L1303–1317
+ test_second_run_noop_for_repo_id method method test_second_run_noop_for_repo_id L1292–1301
+ test_second_run_reports_zero_blobs_migrated method method test_second_run_reports_zero_blobs_migrated L1261–1268
+ test_second_run_reports_zero_commits_rewritten method method test_second_run_reports_zero_commits_rewritten L1248–1259
+ test_second_run_reports_zero_refs_updated method method test_second_run_reports_zero_refs_updated L1279–1290
+ test_second_run_reports_zero_snapshots_relocated method method test_second_run_reports_zero_snapshots_relocated L1270–1277
+ _run method method _run L1409–1418
+ test_json_commits_rewritten_count method method test_json_commits_rewritten_count L1440–1450
+ test_json_dry_run_flag_true_with_flag method method test_json_dry_run_flag_true_with_flag L1430–1433
+ test_json_has_required_keys method method test_json_has_required_keys L1420–1428
+ test_json_id_map_is_dict_of_prefixed_ids method method test_json_id_map_is_dict_of_prefixed_ids L1452–1464
+ test_json_live_run_dry_run_false method method test_json_live_run_dry_run_false L1435–1438
+ test_json_merge_in_progress_exits_nonzero method method test_json_merge_in_progress_exits_nonzero L1466–1476
+ TestMixedState class class TestMixedState L1324–1376
+ test_mix_of_bare_and_prefixed_refs method method test_mix_of_bare_and_prefixed_refs L1353–1367
+ test_mix_of_flat_and_canonical_objects method method test_mix_of_flat_and_canonical_objects L1325–1334
+ test_mix_of_flat_and_canonical_snapshots method method test_mix_of_flat_and_canonical_snapshots L1369–1376
+ test_mix_of_legacy_and_canonical_commit_ids method method test_mix_of_legacy_and_canonical_commit_ids L1336–1351
+ test_canonical_object_not_duplicated method method test_canonical_object_not_duplicated L292–302
+ test_dry_run_does_not_move_flat_objects method method test_dry_run_does_not_move_flat_objects L321–331
+ test_flat_dir_removed_after_move method method test_flat_dir_removed_after_move L260–269
+ test_flat_object_moved_to_sha256_subdir method method test_flat_object_moved_to_sha256_subdir L248–258
+ test_flat_object_not_present_after_move method method test_flat_object_not_present_after_move L282–290
+ test_multiple_flat_objects_all_moved method method test_multiple_flat_objects_all_moved L271–280
+ test_result_blobs_migrated_count method method test_result_blobs_migrated_count L304–311
+ test_result_legacy_dirs_removed_count method method test_result_legacy_dirs_removed_count L313–319
+ test_clean_repo_proceeds_without_error method method test_clean_repo_proceeds_without_error L1398–1401
+ test_merge_in_progress_raises method method test_merge_in_progress_raises L1384–1389
+ test_rebase_in_progress_raises method method test_rebase_in_progress_raises L1391–1396
+ TestRefFileMigration class class TestRefFileMigration L487–551
+ _repo_with_bare_ref method method _repo_with_bare_ref L488–496
+ test_all_branch_refs_updated method method test_all_branch_refs_updated L517–530
+ test_already_prefixed_ref_unchanged method method test_already_prefixed_ref_unchanged L505–515
+ test_bare_hex_ref_gets_sha256_prefix method method test_bare_hex_ref_gets_sha256_prefix L498–503
+ test_dry_run_does_not_update_bare_ref method method test_dry_run_does_not_update_bare_ref L545–551
+ test_result_refs_updated_count method method test_result_refs_updated_count L532–543
+ TestReflogMigration class class TestReflogMigration L1101–1169
+ _write_reflog method method _write_reflog L1102–1114
+ test_missing_reflog_does_not_abort method method test_missing_reflog_does_not_abort L1145–1156
+ test_reflog_ids_updated_when_in_id_map method method test_reflog_ids_updated_when_in_id_map L1116–1130
+ test_reflog_with_no_stale_ids_unchanged method method test_reflog_with_no_stale_ids_unchanged L1132–1143
+ test_result_reflogs_updated_count method method test_result_reflogs_updated_count L1158–1169
+ TestRemoteRefMigration class class TestRemoteRefMigration L558–643
+ test_already_prefixed_remote_ref_unchanged method method test_already_prefixed_remote_ref_unchanged L573–584
+ test_bare_hex_remote_ref_gets_prefix method method test_bare_hex_remote_ref_gets_prefix L559–571
+ test_dry_run_does_not_update_remote_ref method method test_dry_run_does_not_update_remote_ref L631–643
+ test_multiple_remotes_all_updated method method test_multiple_remotes_all_updated L586–601
+ test_result_remote_refs_updated_count method method test_result_remote_refs_updated_count L617–629
+ test_stale_remote_ref_updated_after_id_recompute method method test_stale_remote_ref_updated_after_id_recompute L603–615
+ TestRepoIdMigration class class TestRepoIdMigration L650–703
+ _repo_with_uuid method method _repo_with_uuid L651–657
+ test_dry_run_does_not_update_repo_id method method test_dry_run_does_not_update_repo_id L697–703
+ test_migrated_repo_id_is_deterministic method method test_migrated_repo_id_is_deterministic L667–673
+ test_result_repo_id_updated_flag_false_for_valid method method test_result_repo_id_updated_flag_false_for_valid L690–695
+ test_result_repo_id_updated_flag_true_for_uuid method method test_result_repo_id_updated_flag_true_for_uuid L683–688
+ test_uuid_repo_id_replaced_with_sha256_id method method test_uuid_repo_id_replaced_with_sha256_id L659–665
+ test_valid_sha256_repo_id_unchanged method method test_valid_sha256_repo_id_unchanged L675–681
+ _bare_sig method method _bare_sig L1034–1036
+ _prefixed_sig method method _prefixed_sig L1038–1039
+ test_bare_base64_sig_gets_ed25519_prefix method method test_bare_base64_sig_gets_ed25519_prefix L1041–1053
+ test_empty_sig_unchanged method method test_empty_sig_unchanged L1069–1080
+ test_result_signatures_normalised_count method method test_result_signatures_normalised_count L1082–1094
+ TestSnapshotPathMigration class class TestSnapshotPathMigration L419–480
+ test_canonical_snapshot_not_duplicated method method test_canonical_snapshot_not_duplicated L453–461
+ test_dry_run_does_not_move_flat_snapshot method method test_dry_run_does_not_move_flat_snapshot L472–480
+ test_flat_snapshot_moved_to_sha256_subdir method method test_flat_snapshot_moved_to_sha256_subdir L420–430
+ test_flat_snapshot_removed_after_move method method test_flat_snapshot_removed_after_move L432–440
+ test_multiple_flat_snapshots_all_moved method method test_multiple_flat_snapshots_all_moved L442–451
+ test_result_snapshots_relocated_counted method method test_result_snapshots_relocated_counted L463–470
+ _REPO_ID_SHA variable variable _REPO_ID_SHA L60–60
+ _REPO_ID_UUID variable variable _REPO_ID_UUID L59–59
+ _canonical_id function function _canonical_id L84–94
+ _object_canonical function function _object_canonical L147–154
+ _object_flat function function _object_flat L137–144
+ _raw_commit_dict function function _raw_commit_dict L157–198
+ _read_raw_commit function function _read_raw_commit L238–240
+ _read_ref function function _read_ref L228–230
+ _read_remote_ref function function _read_remote_ref L233–235
+ _set_remote_ref function function _set_remote_ref L220–225
+ _snap_flat function function _snap_flat L123–134
+ _v0_id function function _v0_id L67–81
+ _write_commit_raw function function _write_commit_raw L201–210
+ MigrateResult import import MigrateResult L41–41
+ base64 import import base64 L31–31
+ hashlib import import hashlib L33–33
+ long_id import import long_id L40–40
+ migrate import import migrate L41–41
+ write_branch_ref import import write_branch_ref L43–43
~ _snap

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