diff --git a/server/local_git.py b/server/local_git.py index 3a34a1b8..d72310de 100644 --- a/server/local_git.py +++ b/server/local_git.py @@ -9,9 +9,16 @@ CREATE_NO_WINDOW = 0x08000000 # Patches are parsed and displayed by Amadeus, so their format must not follow -# the user's diff configuration: an external diff driver, forced color, or -# diff.noprefix/diff.mnemonicPrefix would change what the parser receives. -PATCH_FORMAT_ARGS = ("--no-ext-diff", "--no-color", "--src-prefix=a/", "--dst-prefix=b/") +# the user's diff configuration: an external diff driver, forced color, +# diff.noprefix/diff.mnemonicPrefix, or diff.submodule=log/diff would change +# what the parser receives. +PATCH_FORMAT_ARGS = ( + "--no-ext-diff", + "--no-color", + "--src-prefix=a/", + "--dst-prefix=b/", + "--submodule=short", +) async def run_git(cwd: str, args: list[str]) -> dict[str, Any]: diff --git a/tests/test_work_artifact_registry.py b/tests/test_work_artifact_registry.py index 8ef84c58..0359e026 100644 --- a/tests/test_work_artifact_registry.py +++ b/tests/test_work_artifact_registry.py @@ -308,6 +308,78 @@ async def run() -> None: asyncio.run(run()) +@pytest.mark.parametrize("submodule_format", ["log", "diff"]) +def test_attempt_patch_ignores_user_submodule_diff_format(submodule_format: str) -> None: + async def run() -> None: + with tempfile.TemporaryDirectory(prefix="work_artifact_submodule_") as temp: + library = Path(temp) / "library" + library.mkdir() + _git(library, "init") + _git(library, "config", "user.email", "amadeus-test@example.invalid") + _git(library, "config", "user.name", "Amadeus Test") + (library / "lib.txt").write_text("base\n", encoding="utf-8") + _git(library, "add", ".") + _git(library, "commit", "-m", "library baseline") + root = Path(temp) / "project" + root.mkdir() + _git(root, "init") + _git(root, "config", "user.email", "amadeus-test@example.invalid") + _git(root, "config", "user.name", "Amadeus Test") + (root / "app.txt").write_text("base\n", encoding="utf-8") + _git( + root, + "-c", + "protocol.file.allow=always", + "submodule", + "add", + str(library), + "vendor", + ) + _git(root, "add", ".") + _git(root, "commit", "-m", "baseline") + _git(root, "config", "diff.submodule", submodule_format) + + store = WorkLedgerStore(Path(temp) / "ledger.sqlite3") + project = store.create_or_get_project(root) + item = store.create_work_item(project.project_id, title="Update vendor") + attempt = store.create_attempt( + item.work_item_id, + provider="codex", + task="Update vendor", + ) + registry = WorkArtifactRegistry(store) + await registry.capture_baseline(attempt, item) + vendor = root / "vendor" + _git(vendor, "config", "user.email", "amadeus-test@example.invalid") + _git(vendor, "config", "user.name", "Amadeus Test") + (vendor / "lib.txt").write_text("base\nchanged\n", encoding="utf-8") + _git(vendor, "commit", "-am", "library change") + (root / "app.txt").write_text("base\nchanged\n", encoding="utf-8") + + current_attempt = store.get_attempt(attempt.attempt_id) + assert current_attempt is not None + delta = await registry.finalize_attempt(current_attempt, item) + store.close() + + assert "Submodule vendor" not in delta["patch"] + assert "lib.txt" not in delta["patch"] + parsed = parse_unified_diff( + delta["patch"], + changed_files=delta["changed_files"], + untracked=delta["untracked"], + ) + assert [(entry["path"], entry["additions"], entry["deletions"]) for entry in parsed["files"]] == [ + ("app.txt", 1, 0), + ("vendor", 1, 1), + ] + assert parsed["files"][0]["hunks"][0]["lines"] == [ + {"kind": "context", "oldLine": 1, "newLine": 1, "text": "base"}, + {"kind": "add", "newLine": 2, "text": "changed"}, + ] + + asyncio.run(run()) + + def test_non_git_workspace_registers_new_files_without_claiming_preexisting_changes() -> None: async def run() -> None: with tempfile.TemporaryDirectory(prefix="work_artifact_filesystem_") as temp: