Skip to content

fix(pulls): merge-commit parent diff for merged PRs (collins-platform #12)#236

Merged
jacsamell merged 1 commit into
mainfrom
fix/merged-pr-diff-via-merge-commit
May 25, 2026
Merged

fix(pulls): merge-commit parent diff for merged PRs (collins-platform #12)#236
jacsamell merged 1 commit into
mainfrom
fix/merged-pr-diff-via-merge-commit

Conversation

@jacsamell
Copy link
Copy Markdown
Contributor

@jacsamell jacsamell commented May 25, 2026

Summary

Real-world hit: cube prv 12 on collins-platform PR #12 (123,773 additions, merged) bailed with "PR has no diff - nothing to review". The new local-git path in #234 returned empty for merged PRs.

Root cause

Three-dot origin/<base>...origin/<head> diffs from the merge-base. For OPEN PRs, merge-base = where head branched off base. For MERGED PRs, base has absorbed head → merge-base IS head itself → base...head is empty. The PR's actual changes are gone from three-dot's view.

GitHub's diff endpoint dodges this by diffing the merge commit's two parents: ^1 = base-before-merge, ^2 = PR head. cube does the same locally now.

Changes

  • fetch_pr requests mergeCommit alongside other PR fields
  • PRData.merge_commit_oid carries the SHA
  • has_changes() and get_diff() prefer git diff <merge>^1 <merge>^2 when the SHA is set. Fetches by SHA so this works even when the head branch was deleted post-merge.
  • Falls back to three-dot, then to API, then to conservative True
  • Cross-repo guard preserved

Smoke test

Against the real PR #12 (cwd = collins-platform clone):

  • has_changesTrue (was: False, silently bailed)
  • get_diff → 130k+ lines of actual PR diff (was: empty string)

Test plan

  • 2 new tests pin the merged-PR path
  • Full suite: 508/508
  • mypy clean

🤖 Generated with Claude Code

Summary

Problem: Merged PRs produced empty local diffs because the previous three-dot local-git diff (origin/...origin/) can compare from a merge-base equal to the head when the base absorbed the head.

Solution: When a PR has a merge commit, compute the diff between the merge commit’s two parents (git diff ^1 ^2) — matching GitHub’s behaviour — falling back to the existing three-dot local diff, the API diff, or a conservative True if resolution fails.

Changes

python/cube/github/pulls.py

  • Added merge_commit_oid: str to PRData.
  • fetch_pr() now requests mergeCommit via gh pr view and stores its oid.
  • has_changes() and get_diff() prefer a merge-commit parent diff when merge_commit_oid is available (non–cross-repo), with fallbacks to three-dot local diff → API diff → conservative True.
  • Added helper _try_local_diff_via_merge_commit() and clarified docs: three-dot logic targets OPEN PRs; merged-PR logic uses merge commit parents.

tests/cli/test_pulls_local_diff.py

  • Extended _gh_view_stub() to model merged PRs (state, merged_at, merge_commit_oid).
  • Added tests:
    • test_merged_pr_uses_merge_commit_parents_for_has_changes
    • test_merged_pr_get_diff_uses_merge_commit_parents

Results

  • Real-world smoke test (collins-platform PR #12) now returns ~130k+ lines and has_changes() = True (previously empty/False).
  • Test suite: 508/508 pass; mypy clean.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0782a00-5434-4d90-9a00-0f83986fba1b

📥 Commits

Reviewing files that changed from the base of the PR and between e844a38 and d717fce.

📒 Files selected for processing (2)
  • python/cube/github/pulls.py
  • tests/cli/test_pulls_local_diff.py

Walkthrough

This PR extends PR diff and emptiness detection to handle merged PRs by computing diffs via merge-commit parents. It adds merge_commit_oid to PRData, expands the GitHub API query to include mergeCommit, routes merged PRs through merge-commit–based emptiness and diff logic (using <merge>^1 and <merge>^2), and adds tests that assert the merge-commit parent path is used instead of the three-dot OPEN-PR path.

Changes

Merged PR Support

Layer / File(s) Summary
Data contract and PR metadata fetching
python/cube/github/pulls.py
PRData gains merge_commit_oid. fetch_pr() expands the gh pr view --json query to request mergeCommit, extracts its oid, and stores it in PRData.
Merged PR emptiness detection
python/cube/github/pulls.py
has_changes() adds a branch for merged PRs that delegates to a helper which resolves the merge commit and runs git diff between the merge commit's two parents to determine emptiness; returns None when resolution fails.
Merged PR diff retrieval
python/cube/github/pulls.py
get_diff() passes merge_commit_oid into _fetch_pr_diff(). _fetch_pr_diff() will attempt _try_local_diff_via_merge_commit() (which runs git show <merge> then git diff <merge>^1 <merge>^2) when not cross-repo and a merge OID is present, before falling back to the existing three-dot OPEN-PR local-git diff path. _try_local_git_diff() docstring clarified it targets OPEN PRs.
Testing infrastructure and merged PR tests
tests/cli/test_pulls_local_diff.py
_gh_view_stub test helper now accepts state, merged_at, and merge_commit_oid, conditionally emitting mergeCommit. Two tests assert has_changes() and get_diff() exercise the merge-commit parents diff path and avoid the three-dot path.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

  • aetheronhq/agent-cube#234: Both PRs modify python/cube/github/pulls.py around PRData.has_changes()/_fetch_pr_diff()/get_diff() diff computation flow—this PR extends merged-PR handling via merge-commit parents while that PR refactors diff fetching order.

Poem

A rabbit hops through GitHub diffs,
Now merged PRs get parent gifts—
Commit ^1 meets ^2,
Empty changes? We check true!
🐰 git diff for merged-PR bliss

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: handling merged PRs via merge-commit parent diffs, addressing a specific bug where merged PRs showed empty diffs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Real-world hit: collins-platform PR #12 (123,773 additions, merged).
After #234 landed, cube prv ran the local-git path but git diff
--quiet origin/main...origin/feat/... returned exit 0 (no diff),
so the empty-PR bail fired. False-empty.

Root cause: three-dot origin/<base>...origin/<head> diffs from the
merge-base. For OPEN PRs, merge-base = where head branched off
base. For MERGED PRs, base has absorbed head — so merge-base IS
head itself, and merge-base..head is empty.

GitHub's diff endpoint dodges this by diffing the merge commit's
two parents (^1 = base-before-merge, ^2 = head). cube does the
same locally now:
- fetch_pr requests mergeCommit alongside other PR fields
- PRData.merge_commit_oid carries the SHA
- has_changes() / get_diff() prefer git diff <merge>^1 <merge>^2
  when the SHA is set. Fetches by SHA so this works even when the
  head branch was deleted post-merge.
- Falls back to three-dot, then to API, then to conservative True.

Smoke test on the real PR:
  has_changes True (was: False, silently bailed)
  get_diff 130k+ lines (was: empty)

Tests: 2 new (16 total). Full suite: 508/508. mypy clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jacsamell jacsamell force-pushed the fix/merged-pr-diff-via-merge-commit branch from e844a38 to d717fce Compare May 25, 2026 20:09
@jacsamell jacsamell merged commit ba9fd5e into main May 25, 2026
1 of 2 checks passed
@jacsamell jacsamell deleted the fix/merged-pr-diff-via-merge-commit branch May 25, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant