.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.
This commit is contained in:
Kacper Michajłow
2026-07-03 00:05:20 +02:00
committed by Timo Rothenpieler
parent aafb5c655e
commit 2e9dfa0214
+21 -4
View File
@@ -13,11 +13,28 @@ runs:
if: ${{ forge.event_name == 'pull_request' }} if: ${{ forge.event_name == 'pull_request' }}
working-directory: ${{ inputs.path }} working-directory: ${{ inputs.path }}
run: | run: |
set -x
# Fetch all PR commits + merge-base # 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') 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 # 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 # Rebase PR commits onto the current state of target branch
git rebase --onto origin/${{ forge.base_ref }} ${{ forge.event.pull_request.merge_base }} \ git rebase --onto origin/${{ forge.base_ref }} $base \
|| { echo "::error::does not rebase cleanly onto ${{ forge.base_ref }}"; exit 1; } || { echo "Does not rebase cleanly onto ${{ forge.base_ref }}"; exit 1; }