diff --git a/pr_split/cli.py b/pr_split/cli.py index 7121b06..ad119a6 100644 --- a/pr_split/cli.py +++ b/pr_split/cli.py @@ -64,6 +64,7 @@ from .graph import PlanDAG from .plan_store import load_plan, plan_exists, save_plan from .planner import plan_split, validate_coverage, validate_plan +from .planner.chunker import recompute_estimated_loc from .schemas import ( BranchRecord, GitState, @@ -638,7 +639,10 @@ def _interactive_edit(groups: list[Group], parsed_diff: ParsedDiff) -> list[Grou if hunk_index < 0: console.print("[red]Hunk index must be non-negative.[/red]") continue - _move_assignment(groups, parsed_diff, file_path, hunk_index, from_id, to_id) + if _move_assignment(groups, parsed_diff, file_path, hunk_index, from_id, to_id): + # Keep the per-group LOC estimates in step with the new + # assignments; validate_plan and the PR bodies read them. + recompute_estimated_loc(groups, parsed_diff) else: console.print( "[yellow]Unknown command. Type 'done' to proceed or 'abort' to cancel.[/yellow]" diff --git a/tests/test_cli_coverage.py b/tests/test_cli_coverage.py index 8d05885..fb0c090 100644 --- a/tests/test_cli_coverage.py +++ b/tests/test_cli_coverage.py @@ -17,6 +17,7 @@ from pr_split.cli import ( _build_pr_body, _handle_loc_bound_warnings, + _interactive_edit, _move_assignment, _present_plan, _resolve_fork_ref, @@ -26,6 +27,7 @@ app, ) from pr_split.constants import AssignmentType +from pr_split.diff_ops.parser import parse_diff from pr_split.exceptions import PRSplitError from pr_split.schemas import Group, GroupAssignment from pr_split.types_defs import ForkPRInfo @@ -537,3 +539,48 @@ def test_status_no_plan(self, mock_pe: MagicMock) -> None: def test_clean_no_plan(self, mock_pe: MagicMock) -> None: result = runner.invoke(app, ["clean"]) assert result.exit_code == 0 + + +TWO_HUNK_DIFF = """\ +diff --git a/a.py b/a.py +--- a/a.py ++++ b/a.py +@@ -1,3 +1,4 @@ + x ++one + y + z +@@ -20,3 +21,5 @@ + p ++two ++three + q + r +""" + + +class TestInteractiveEditRecomputesLoc: + @patch("pr_split.cli.typer.prompt") + def test_move_updates_estimated_loc_of_both_groups(self, mock_prompt: MagicMock) -> None: + parsed = parse_diff(TWO_HUNK_DIFF) + g1 = _group("pr-1", "src", files=["a.py"], added=3, removed=0) + g2 = _group("pr-2", "dst", added=0, removed=0) + mock_prompt.side_effect = ["move a.py:1 pr-1 pr-2", "done"] + + result = _interactive_edit([g1, g2], parsed) + + assert result == [g1, g2] + assert (g1.estimated_added, g1.estimated_removed, g1.estimated_loc) == (1, 0, 1) + assert (g2.estimated_added, g2.estimated_removed, g2.estimated_loc) == (2, 0, 2) + + @patch("pr_split.cli.typer.prompt") + def test_failed_move_leaves_estimates_alone(self, mock_prompt: MagicMock) -> None: + parsed = parse_diff(TWO_HUNK_DIFF) + g1 = _group("pr-1", "src", files=["a.py"], added=3, removed=0) + g2 = _group("pr-2", "dst", added=0, removed=0) + mock_prompt.side_effect = ["move a.py:7 pr-1 pr-2", "done"] + + _interactive_edit([g1, g2], parsed) + + assert g1.estimated_loc == 3 + assert g2.estimated_loc == 0