# Server-Side Rebase (No Local Clone Required)

Complete workflow for rebasing a PR branch onto the latest base branch entirely through the API — no local git clone needed.

## 1. Check if PR Is Behind Base

```bash
OWNER="{owner}"
REPO="{repo}"
PR_INDEX="{index}"

# Get PR details
PR=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}")

BASE_BRANCH=$(echo "$PR" | jq -r '.base.ref')
HEAD_BRANCH=$(echo "$PR" | jq -r '.head.ref')
PR_MERGE_BASE=$(echo "$PR" | jq -r '.merge_base')

# Get current head of base branch
BASE_HEAD=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/branches/${BASE_BRANCH}" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq -r '.commit.id')

echo "PR base ref:     ${BASE_BRANCH}"
echo "PR head ref:     ${HEAD_BRANCH}"
echo "PR merge base:   ${PR_MERGE_BASE}"
echo "Current base HEAD: ${BASE_HEAD}"

if [ "$PR_MERGE_BASE" = "$BASE_HEAD" ]; then
  echo "PR is up to date with ${BASE_BRANCH}."
else
  echo "PR is behind ${BASE_BRANCH} — rebase needed."
fi
```

## 2. Check for Conflicts

```bash
MERGEABLE=$(echo "$PR" | jq -r '.mergeable')
echo "Mergeable: ${MERGEABLE}"
# true = no conflicts, false = conflicts exist, null = not yet computed
```

If `mergeable` is `null`, Forgejo hasn't computed the merge status yet. Fetch the PR again after a short wait:

```bash
if [ "$MERGEABLE" = "null" ]; then
  echo "Merge status not yet computed. Waiting..."
  sleep 5
  MERGEABLE=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}" \
    -H "Authorization: token ${FORGEJO_PAT}" | jq -r '.mergeable')
  echo "Mergeable: ${MERGEABLE}"
fi
```

## 3. Update PR Branch (Rebase)

The `style` parameter is a **query parameter** (not a JSON body field) per the Swagger spec.

```bash
RESULT=$(curl -s -w "\n%{http_code}" -X POST \
  "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}/update?style=rebase" \
  -H "Authorization: token ${FORGEJO_PAT}")

BODY=$(echo "$RESULT" | head -n -1)
STATUS=$(echo "$RESULT" | tail -n 1)

echo "HTTP ${STATUS}"
echo "$BODY" | jq .
```

Style options (passed as `?style=` query parameter):
- `rebase` — rebase the PR branch onto the base branch (rewrites commits)
- `merge` — merge the base branch into the PR branch (creates a merge commit, default if omitted)

## 4. Verify the Update Succeeded

```bash
# Get updated PR details
UPDATED_PR=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}")

NEW_MERGE_BASE=$(echo "$UPDATED_PR" | jq -r '.merge_base')
NEW_HEAD_SHA=$(echo "$UPDATED_PR" | jq -r '.head.sha')

echo "New merge base: ${NEW_MERGE_BASE}"
echo "New head SHA:   ${NEW_HEAD_SHA}"
echo "Base HEAD:      ${BASE_HEAD}"

if [ "$NEW_MERGE_BASE" = "$BASE_HEAD" ]; then
  echo "Rebase successful — PR is now up to date."
else
  echo "WARNING: Merge base hasn't updated yet. It may take a moment."
fi
```

## 5. Handle Failures

### Conflict During Rebase

If the rebase fails due to conflicts, the API returns HTTP 409:

```bash
if [ "$STATUS" = "409" ]; then
  echo "Rebase failed due to conflicts."
  echo "Conflict details: $(echo "$BODY" | jq -r '.message')"
  echo ""
  echo "Options:"
  echo "  1. Resolve conflicts locally and force-push"
  echo "  2. Use merge style instead of rebase"
  echo "  3. Ask the PR author to resolve conflicts"

  # Option 2: Fall back to merge style
  echo "Attempting merge-style update..."
  curl -s -X POST "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}/update?style=merge" \
    -H "Authorization: token ${FORGEJO_PAT}"
fi
```

### PR Already Up to Date

If the PR is already up to date, the API returns HTTP 422:

```bash
if [ "$STATUS" = "422" ]; then
  echo "PR is already up to date — no rebase needed."
fi
```

### Permission Denied

```bash
if [ "$STATUS" = "403" ]; then
  echo "Permission denied. You need write access to the head repository."
fi
```

## Complete Script with Error Handling

```bash
#!/bin/bash
set -euo pipefail

OWNER="${1:?Usage: $0 <owner> <repo> <pr_index>}"
REPO="${2:?}"
PR_INDEX="${3:?}"

echo "=== Server-Side Rebase for ${OWNER}/${REPO}#${PR_INDEX} ==="

# 1. Get PR details
PR=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}")

BASE_BRANCH=$(echo "$PR" | jq -r '.base.ref')
HEAD_BRANCH=$(echo "$PR" | jq -r '.head.ref')
PR_MERGE_BASE=$(echo "$PR" | jq -r '.merge_base')
MERGEABLE=$(echo "$PR" | jq -r '.mergeable')
PR_STATE=$(echo "$PR" | jq -r '.state')

if [ "$PR_STATE" != "open" ]; then
  echo "PR is ${PR_STATE}. Nothing to do."
  exit 0
fi

echo "Base: ${BASE_BRANCH}, Head: ${HEAD_BRANCH}"
echo "Mergeable: ${MERGEABLE}"

# 2. Get current base branch head
BASE_HEAD=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/branches/${BASE_BRANCH}" \
  -H "Authorization: token ${FORGEJO_PAT}" | jq -r '.commit.id')

if [ "$PR_MERGE_BASE" = "$BASE_HEAD" ]; then
  echo "PR is already up to date with ${BASE_BRANCH}."
  exit 0
fi

echo "PR is behind ${BASE_BRANCH}. Rebasing..."

# 3. Check for conflicts
if [ "$MERGEABLE" = "false" ]; then
  echo "WARNING: PR has conflicts. Rebase may fail."
fi

# 4. Attempt rebase
RESULT=$(curl -s -w "\n%{http_code}" -X POST \
  "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}/update?style=rebase" \
  -H "Authorization: token ${FORGEJO_PAT}")

BODY=$(echo "$RESULT" | head -n -1)
STATUS=$(echo "$RESULT" | tail -n 1)

case "$STATUS" in
  200)
    echo "Rebase successful."
    ;;
  409)
    echo "Rebase failed due to conflicts."
    echo "$(echo "$BODY" | jq -r '.message // .')"
    echo ""
    echo "Falling back to merge-style update..."
    FALLBACK=$(curl -s -w "\n%{http_code}" -X POST \
      "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}/update?style=merge" \
      -H "Authorization: token ${FORGEJO_PAT}")
    FB_STATUS=$(echo "$FALLBACK" | tail -n 1)
    if [ "$FB_STATUS" = "200" ]; then
      echo "Merge-style update successful."
    else
      echo "Merge-style update also failed (HTTP ${FB_STATUS})."
      echo "Manual conflict resolution required."
      exit 1
    fi
    ;;
  422)
    echo "PR is already up to date."
    ;;
  403)
    echo "Permission denied. Write access to the head repository is required."
    exit 1
    ;;
  404)
    echo "PR not found."
    exit 1
    ;;
  *)
    echo "Unexpected response (HTTP ${STATUS}):"
    echo "$BODY" | jq . 2>/dev/null || echo "$BODY"
    exit 1
    ;;
esac

# 5. Verify
echo ""
echo "=== Verification ==="
UPDATED=$(curl -s "${FORGEJO_URL}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_INDEX}" \
  -H "Authorization: token ${FORGEJO_PAT}")
echo "New head SHA:   $(echo "$UPDATED" | jq -r '.head.sha')"
echo "Merge base:     $(echo "$UPDATED" | jq -r '.merge_base')"
echo "Mergeable:      $(echo "$UPDATED" | jq -r '.mergeable')"
echo "Commits:        $(echo "$UPDATED" | jq -r '.commits')"
```
