"""ORCH-073 FR-4 — .gitattributes: CHANGELOG.md merge=union. Covers TC-11/TC-12 / AC-4. TC-11 asserts the repo-root .gitattributes declares the union driver (git check-attr). TC-12 proves, in a throwaway git repo, that two branches both editing '## [Unreleased]' merge WITHOUT a conflict and BOTH entries survive — exactly what stops auto_rebase_onto_main from rolling a branch back. """ import subprocess from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] def _git(cwd, *args, env=None): return subprocess.run( ["git", *args], cwd=str(cwd), capture_output=True, text=True, env=env, ) # --------------------------------------------------------------------------- # TC-11 (AC-4): the repo-root .gitattributes declares CHANGELOG.md merge=union. # --------------------------------------------------------------------------- def test_tc11_gitattributes_declares_union(): ga = REPO_ROOT / ".gitattributes" assert ga.is_file(), ".gitattributes must exist at the repo root" assert "CHANGELOG.md merge=union" in ga.read_text(encoding="utf-8") r = _git(REPO_ROOT, "check-attr", "merge", "CHANGELOG.md") assert r.returncode == 0, r.stderr # Output form: 'CHANGELOG.md: merge: union' assert "merge: union" in r.stdout, r.stdout # --------------------------------------------------------------------------- # TC-12 (AC-4): two Unreleased edits merge with no conflict; both kept. # --------------------------------------------------------------------------- def _init_repo(tmp_path): env = { "GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@t", "GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@t", "GIT_CONFIG_GLOBAL": "/dev/null", "GIT_CONFIG_SYSTEM": "/dev/null", "PATH": __import__("os").environ.get("PATH", ""), "HOME": str(tmp_path), } repo = tmp_path / "repo" repo.mkdir() assert _git(repo, "init", "-b", "main", env=env).returncode == 0 (repo / ".gitattributes").write_text("CHANGELOG.md merge=union\n", encoding="utf-8") base = ( "# Changelog\n\n## [Unreleased]\n\n### Common\n\n## [0.1.0]\n- initial\n" ) (repo / "CHANGELOG.md").write_text(base, encoding="utf-8") _git(repo, "add", ".", env=env) assert _git(repo, "commit", "-m", "base", env=env).returncode == 0 return repo, env def test_tc12_union_merge_keeps_both_entries(tmp_path): repo, env = _init_repo(tmp_path) # Branch A adds its Unreleased line. _git(repo, "checkout", "-b", "task-a", env=env) txt = (repo / "CHANGELOG.md").read_text(encoding="utf-8") (repo / "CHANGELOG.md").write_text( txt.replace("### Common\n", "### Common\n- ORCH-A: feature A\n"), encoding="utf-8" ) _git(repo, "commit", "-am", "task A changelog", env=env) # Branch B (from main) adds a DIFFERENT Unreleased line at the same spot. _git(repo, "checkout", "main", env=env) _git(repo, "checkout", "-b", "task-b", env=env) txt = (repo / "CHANGELOG.md").read_text(encoding="utf-8") (repo / "CHANGELOG.md").write_text( txt.replace("### Common\n", "### Common\n- ORCH-B: feature B\n"), encoding="utf-8" ) _git(repo, "commit", "-am", "task B changelog", env=env) # Merge A into B — union must avoid a conflict and keep BOTH lines. m = _git(repo, "merge", "--no-edit", "task-a", env=env) result = (repo / "CHANGELOG.md").read_text(encoding="utf-8") assert m.returncode == 0, f"union merge must not conflict: {m.stdout}\n{m.stderr}" assert "<<<<<<<" not in result and ">>>>>>>" not in result assert "ORCH-A: feature A" in result assert "ORCH-B: feature B" in result