"""Tests that muse commit does NOT overwrite the working tree. Regression suite for the apply_manifest-in-commit bug: commit.py was calling apply_manifest() after writing the snapshot, which restored every tracked file from the object store — silently destroying any unstaged changes that existed on disk at commit time. Expected (Git-compatible) behaviour: commit writes to the object store and advances the branch ref; it never touches files in the working tree. """ from __future__ import annotations import pathlib import pytest from tests.cli_test_helper import CliRunner runner = CliRunner() def _run(repo: pathlib.Path, *args: str) -> None: r = runner.invoke(None, list(args), cwd=repo) assert r.exit_code == 0, f"muse {' '.join(args)} failed:\n{r.output}" # --------------------------------------------------------------------------- # Core regression: unstaged changes to a tracked file survive commit # --------------------------------------------------------------------------- class TestCommitPreservesWorkdir: def test_unstaged_changes_survive_commit(self, muse_repo: pathlib.Path) -> None: """Unstaged edits to a tracked file must not be overwritten by commit.""" f = muse_repo / "song.py" f.write_text("v1\n") _run(muse_repo, "code", "add", "song.py") _run(muse_repo, "commit", "-m", "add song") # Make a second change and stage it f.write_text("v2\n") _run(muse_repo, "code", "add", "song.py") # Make a THIRD change — intentionally NOT staged f.write_text("v3 — unstaged\n") # Commit captures v2 (staged); v3 on disk must survive _run(muse_repo, "commit", "-m", "commit v2") assert f.read_text() == "v3 — unstaged\n", ( "commit overwrote the working tree with the staged version" ) def test_unstaged_changes_to_multiple_files(self, muse_repo: pathlib.Path) -> None: """Multiple files with unstaged edits all survive commit.""" for name in ("a.py", "b.py", "c.py"): f = muse_repo / name f.write_text("initial\n") _run(muse_repo, "code", "add", ".") _run(muse_repo, "commit", "-m", "initial") # Stage updated versions for name in ("a.py", "b.py", "c.py"): (muse_repo / name).write_text("staged\n") _run(muse_repo, "code", "add", ".") # Write unstaged versions for name in ("a.py", "b.py", "c.py"): (muse_repo / name).write_text(f"unstaged {name}\n") _run(muse_repo, "commit", "-m", "commit staged versions") for name in ("a.py", "b.py", "c.py"): assert (muse_repo / name).read_text() == f"unstaged {name}\n", ( f"{name} was overwritten by commit" ) def test_untracked_files_survive_commit(self, muse_repo: pathlib.Path) -> None: """Untracked files must never be touched by commit (was already true).""" tracked = muse_repo / "tracked.py" tracked.write_text("tracked\n") _run(muse_repo, "code", "add", "tracked.py") _run(muse_repo, "commit", "-m", "add tracked") untracked = muse_repo / "notes.txt" untracked.write_text("my notes\n") tracked.write_text("tracked v2\n") _run(muse_repo, "code", "add", "tracked.py") _run(muse_repo, "commit", "-m", "update tracked") assert untracked.read_text() == "my notes\n" def test_new_untracked_file_not_deleted_by_commit(self, muse_repo: pathlib.Path) -> None: """A new file created after staging must not be deleted by commit.""" f = muse_repo / "old.py" f.write_text("old\n") _run(muse_repo, "code", "add", "old.py") _run(muse_repo, "commit", "-m", "add old") # Stage another file g = muse_repo / "new_staged.py" g.write_text("staged\n") _run(muse_repo, "code", "add", "new_staged.py") # Create a brand-new file AFTER staging — should survive commit extra = muse_repo / "created_after_stage.py" extra.write_text("i exist\n") _run(muse_repo, "commit", "-m", "commit new_staged") assert extra.exists(), "commit deleted a file that was never staged" assert extra.read_text() == "i exist\n" def test_staged_deletion_still_removes_file(self, muse_repo: pathlib.Path) -> None: """muse rm + commit must still delete the file from disk (regression guard).""" f = muse_repo / "gone.py" f.write_text("bye\n") _run(muse_repo, "code", "add", "gone.py") _run(muse_repo, "commit", "-m", "add gone") _run(muse_repo, "rm", "gone.py") _run(muse_repo, "commit", "-m", "delete gone") assert not f.exists(), "staged deletion did not remove the file" def test_working_tree_unchanged_after_first_commit(self, muse_repo: pathlib.Path) -> None: """On the very first commit, files that were staged are still on disk.""" f = muse_repo / "hello.py" f.write_text("hello\n") _run(muse_repo, "code", "add", "hello.py") _run(muse_repo, "commit", "-m", "first commit") assert f.read_text() == "hello\n"