From 92e0697b05f072fd5f9c7fb6b94df893fc9caa7d Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Wed, 11 Mar 2026 21:04:55 +0000 Subject: [PATCH] Add example --- README.md | 2 +- docs/advanced-python.md | 4 ++ examples/merge_file.py | 38 +++++++++++++++++++ reconcile-python/tests/test_reconcile.py | 47 ++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 examples/merge_file.py diff --git a/README.md b/README.md index 8ee4dc7..c25ef92 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ result = reconcile(parent, left, right) print(result["text"]) # "Hi beautiful world" ``` -See the [advanced Python examples](docs/advanced-python.md) for cursor tracking, change provenance, and compact diffs. +See the [merge-file example](examples/merge_file.py) for a file-merging CLI, or the [advanced examples document](docs/advanced-python.md) for cursor tracking, change provenance, and compact diffs. ## Motivation diff --git a/docs/advanced-python.md b/docs/advanced-python.md index 1e21907..ed2a8e9 100644 --- a/docs/advanced-python.md +++ b/docs/advanced-python.md @@ -86,3 +86,7 @@ assert reconstructed == changed ``` Diff entries are positive integers (retain N characters), negative integers (delete N characters), and strings (insert text). + +## File Merging Example + +For a complete file-merging CLI (a trivial `git merge-file`), see [`examples/merge_file.py`](../examples/merge_file.py). diff --git a/examples/merge_file.py b/examples/merge_file.py new file mode 100644 index 0000000..23e8555 --- /dev/null +++ b/examples/merge_file.py @@ -0,0 +1,38 @@ +"""Merge three versions of a file: mine, base, and theirs. + +A trivial version of git merge-file (https://git-scm.com/docs/git-merge-file). + +Run it with: + uv run --directory reconcile-python \ + python ../examples/merge_file.py my.txt base.txt their.txt [output.txt] +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +from reconcile_text import reconcile + + +def main() -> None: + args = sys.argv[1:] + + if len(args) < 3 or len(args) > 4: + print("Usage: merge_file.py [output]", file=sys.stderr) + sys.exit(1) + + mine = Path(args[0]).read_text() + base = Path(args[1]).read_text() + theirs = Path(args[2]).read_text() + + result = reconcile(base, mine, theirs) + + if len(args) == 4: + Path(args[3]).write_text(result["text"]) + else: + print(result["text"], end="") + + +if __name__ == "__main__": + main() diff --git a/reconcile-python/tests/test_reconcile.py b/reconcile-python/tests/test_reconcile.py index 0d3f964..196e808 100644 --- a/reconcile-python/tests/test_reconcile.py +++ b/reconcile-python/tests/test_reconcile.py @@ -1,11 +1,14 @@ from __future__ import annotations +import subprocess +import sys from pathlib import Path import pytest from reconcile_text import diff, reconcile, reconcile_with_history, undiff +EXAMPLES_DIR = Path(__file__).resolve().parent.parent.parent / "examples" RESOURCES_DIR = Path(__file__).resolve().parent.parent.parent / "tests" / "resources" FILES = ["pride_and_prejudice.txt", "room_with_a_view.txt", "blns.txt"] @@ -118,6 +121,50 @@ class TestUndiff: undiff("short", [100]) +class TestExamples: + def test_merge_file_stdout(self, tmp_path: Path) -> None: + (tmp_path / "base.txt").write_text("Hello world") + (tmp_path / "mine.txt").write_text("Hello beautiful world") + (tmp_path / "theirs.txt").write_text("Hi world") + + result = subprocess.run( + [ + sys.executable, + str(EXAMPLES_DIR / "merge_file.py"), + str(tmp_path / "mine.txt"), + str(tmp_path / "base.txt"), + str(tmp_path / "theirs.txt"), + ], + capture_output=True, + text=True, + check=True, + ) + + assert result.stdout == "Hi beautiful world" + + def test_merge_file_output_file(self, tmp_path: Path) -> None: + (tmp_path / "base.txt").write_text("Hello world") + (tmp_path / "mine.txt").write_text("Hello beautiful world") + (tmp_path / "theirs.txt").write_text("Hi world") + output = tmp_path / "output.txt" + + subprocess.run( + [ + sys.executable, + str(EXAMPLES_DIR / "merge_file.py"), + str(tmp_path / "mine.txt"), + str(tmp_path / "base.txt"), + str(tmp_path / "theirs.txt"), + str(output), + ], + capture_output=True, + text=True, + check=True, + ) + + assert output.read_text() == "Hi beautiful world" + + class TestDiffUndiffInverse: """Verify diff/undiff roundtrip across large real-world texts."""