|
3 | 3 | # Updates PR stack after merging a PR |
4 | 4 | # |
5 | 5 | # Required environment variables (squash-merge mode): |
6 | | -# SQUASH_COMMIT - The hash of the squash commit that was merged |
| 6 | +# SQUASH_COMMIT - The merged PR's merge_commit_sha: the squash commit, or the |
| 7 | +# last copied commit of a rebase merge |
7 | 8 | # MERGED_BRANCH - The name of the branch that was merged and will be deleted |
8 | 9 | # TARGET_BRANCH - The name of the branch that the PR was merged into |
9 | 10 | # PR_NUMBER - The number of the PR that was merged |
@@ -99,58 +100,6 @@ has_squash_commit() { |
99 | 100 | && git merge-base --is-ancestor SQUASH_COMMIT "$BRANCH" |
100 | 101 | } |
101 | 102 |
|
102 | | -# Args: a commit sha. Echoes the numbers of the pull requests that introduced |
103 | | -# the commit to the repository, one per line. |
104 | | -commit_pull_numbers() { |
105 | | - gh api "repos/{owner}/{repo}/commits/$1/pulls" --jq '.[].number' \ |
106 | | - || { echo "❌ Could not list the pull requests that introduced commit $1" >&2; return 1; } |
107 | | -} |
108 | | - |
109 | | -# Args: the merge commit sha, the merged PR's number. The association is |
110 | | -# computed asynchronously, some time after the merge. The merge commit always |
111 | | -# belongs to the merged PR, so once it shows up the index has caught up with |
112 | | -# this merge; until then, an empty answer for any commit of the merge means |
113 | | -# nothing. Exits if the association never appears. |
114 | | -wait_for_pull_association() { |
115 | | - local MERGE_SHA="$1" PR_NUMBER="$2" |
116 | | - local NUMBERS |
117 | | - for _ in $(seq 1 24); do |
118 | | - NUMBERS=$(commit_pull_numbers "$MERGE_SHA") || exit 1 |
119 | | - if grep -qx "$PR_NUMBER" <<<"$NUMBERS"; then |
120 | | - return 0 |
121 | | - fi |
122 | | - sleep "${ASSOCIATION_POLL_SECONDS:-5}" |
123 | | - done |
124 | | - echo "❌ GitHub never associated $MERGE_SHA with PR #$PR_NUMBER; cannot tell a squash from a rebase" >&2 |
125 | | - exit 1 |
126 | | -} |
127 | | - |
128 | | -# Args: the merged PR's number. The event payload does not say which merge |
129 | | -# method was used (GitHub records it nowhere), but GitHub associates every |
130 | | -# trunk commit with the PR that introduced it. A squash introduces a single |
131 | | -# commit, so the commit below SQUASH_COMMIT belongs to an older PR or to |
132 | | -# none; a rebase introduces a copy of each PR commit, so with two or more |
133 | | -# commits the one below SQUASH_COMMIT still belongs to this PR. A |
134 | | -# single-commit PR merges identically under rebase and squash and correctly |
135 | | -# reads as a squash here. |
136 | | -is_rebase_merge() { |
137 | | - local PR_NUMBER="$1" |
138 | | - local MERGE_SHA PARENT_SHA NUMBERS |
139 | | - MERGE_SHA=$(git rev-parse SQUASH_COMMIT) |
140 | | - PARENT_SHA=$(git rev-parse SQUASH_COMMIT~) |
141 | | - |
142 | | - NUMBERS=$(commit_pull_numbers "$PARENT_SHA") || exit 1 |
143 | | - if [[ -z "$NUMBERS" ]]; then |
144 | | - # Ambiguous: "no PR introduced this commit" (a squash on top of a |
145 | | - # direct push) and "not indexed yet" (a rebase copy) both come back |
146 | | - # empty. Wait until the index has caught up with this merge, then ask |
147 | | - # again; this time empty really means no PR. |
148 | | - wait_for_pull_association "$MERGE_SHA" "$PR_NUMBER" |
149 | | - NUMBERS=$(commit_pull_numbers "$PARENT_SHA") || exit 1 |
150 | | - fi |
151 | | - grep -qx "$PR_NUMBER" <<<"$NUMBERS" |
152 | | -} |
153 | | - |
154 | 103 | # Echoes "<number> <head branch>" for each open PR based on the merged branch. |
155 | 104 | # try, not run: callers run this in a command substitution, where a die would |
156 | 105 | # only leave the subshell, so they capture the output and die themselves. An |
@@ -425,19 +374,11 @@ main() { |
425 | 374 | return 0 |
426 | 375 | fi |
427 | 376 |
|
428 | | - # Rebase merges are not supported: the copies on the target are new |
429 | | - # commits, so a child retargeted as-is would show its parent's changes in |
430 | | - # its diff, and the squash sequence can raise spurious conflicts against |
431 | | - # the intermediate copies. Tell the children and leave everything alone. |
432 | | - if is_rebase_merge "$PR_NUMBER"; then |
433 | | - echo "⚠️ '$MERGED_BRANCH' looks rebase-merged; rebase merges are not supported, leaving the stack alone" |
434 | | - CHILDREN=$(list_child_prs) || die "could not list the PRs based on $MERGED_BRANCH" |
435 | | - while read -r NUMBER BRANCH; do |
436 | | - [[ -n "$BRANCH" ]] || continue |
437 | | - run gh pr comment "$NUMBER" --body "ℹ️ The base branch \`$MERGED_BRANCH\` of this PR was merged with \"Rebase and merge\", which autorestack does not support. Update this PR manually. \`$MERGED_BRANCH\` was kept so this PR stays open." |
438 | | - done <<<"$CHILDREN" |
439 | | - return 0 |
440 | | - fi |
| 377 | + # A squash merge and a rebase merge both land the branch's content as new |
| 378 | + # commits without merging its history, and both take the path below. |
| 379 | + # SQUASH_COMMIT (the PR's merge_commit_sha) is the squash commit or the |
| 380 | + # last rebased copy; either way its tree carries the merged branch's full |
| 381 | + # content, which is all the single-merge re-parent reads from it. |
441 | 382 |
|
442 | 383 | # Find all PRs directly targeting the merged PR's head |
443 | 384 | CHILDREN=$(list_child_prs) || die "could not list the PRs based on $MERGED_BRANCH" |
|
0 commit comments