From 2e9dfa02147112f60d5415128a79b1310e4ad9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Fri, 3 Jul 2026 00:05:20 +0200 Subject: [PATCH] .forgejo/actions/rebase-pr: workaround stale `forge.event.pull_request.merge_base` value Unfortunately, Forgejo does not update the merge-base when PR branch is updated, the `forge.event.pull_request.merge_base` is stale value, from the time when PR was created. We cannot relly on it, so try to infer parent commmit of the PR from the shallow clone that we have. This can be reproduced by: 1. `git rebase X` where X is some commit on target branch, later known as merge-base 2. Create PR. Notice that `forge.event.pull_request.merge_base` == `X` 3. `git rebase X^` 4. Force push the reparented branch. Notice that forge.event.pull_request.merge_base == X. This is invalid! X not only is no longer a merge-base, but it is completelly unrelated commit, which exists only on `target` branch, not on PR branch. 5. ???? Additionally I can say that in fact Forgejo does update the merge-base value, but it looks like it does that AFTER the actions are run, or maybe concurrently and there is a race there. I've observed that on 2nd push of a PR branch, rebased on the same parent/merge-base, the reported value is actually correct, but we cannot rely on that, because on 1st run it's incorrect if PR branch was rebased on different parent. For more complex PR fallback to full unshallow and merge. While at it make git fetches verbose, so we know which commits are asked for. --- .forgejo/actions/rebase-pr/action.yml | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/.forgejo/actions/rebase-pr/action.yml b/.forgejo/actions/rebase-pr/action.yml index fa8c9e2a09..48e4b55f93 100644 --- a/.forgejo/actions/rebase-pr/action.yml +++ b/.forgejo/actions/rebase-pr/action.yml @@ -13,11 +13,28 @@ runs: if: ${{ forge.event_name == 'pull_request' }} working-directory: ${{ inputs.path }} run: | + set -x # Fetch all PR commits + merge-base commits=$(curl -s -o /dev/null -w '%header{X-Total-Count}' '${{ forge.api_url }}/repos/${{ forge.repository }}/pulls/${{ forge.event.pull_request.number }}/commits?limit=1&verification=false&files=false') - git fetch --deepen=$commits origin pull/${{ forge.event.pull_request.number }}/head + git fetch -vv --deepen=$commits origin pull/${{ forge.event.pull_request.number }}/head + + if [ "$(wc -l < .git/shallow)" -ne 1 ]; then + echo "Complex PR history detected, falling back to merge" + git fetch -vv --unshallow origin pull/${{ forge.event.pull_request.number }}/head + git fetch -vv origin ${{ forge.base_ref }} + git merge --no-edit origin/${{ forge.base_ref }} \ + || { echo "Does not merge cleanly with ${{ forge.base_ref }}"; exit 1; } + exit 0 + fi + + # Unfortunately, Forgejo does not update the merge-base when PR branch + # is updated, the `forge.event.pull_request.merge_base` is stale value, + # from the time when PR was created. We cannot rely on it, so try to + # infer parent commit of the PR from the shallow clone that we have. + base=$(cat .git/shallow) + # Fetch target branch - git fetch --depth=1 origin ${{ forge.base_ref }} + git fetch -vv --depth=1 origin ${{ forge.base_ref }} # Rebase PR commits onto the current state of target branch - git rebase --onto origin/${{ forge.base_ref }} ${{ forge.event.pull_request.merge_base }} \ - || { echo "::error::does not rebase cleanly onto ${{ forge.base_ref }}"; exit 1; } + git rebase --onto origin/${{ forge.base_ref }} $base \ + || { echo "Does not rebase cleanly onto ${{ forge.base_ref }}"; exit 1; }