Skip to content

Fork synchronization

Forks drift from upstream fast. This recipe uses bb repo view to discover the upstream clone URL and plain git for the fetch, rebase and push.

  • Your local clone is the fork, not the upstream.
  • origin points at your fork.
  • The upstream is reachable as a separate remote (we’ll add it if missing).
  • You have permission to push to your fork’s main (or whichever branch you want to keep in sync).
#!/bin/bash
# sync-fork.sh - Keep a fork's main branch in sync with its upstream.
set -euo pipefail
UPSTREAM_WORKSPACE="${UPSTREAM_WORKSPACE:?set UPSTREAM_WORKSPACE}"
UPSTREAM_REPO="${UPSTREAM_REPO:?set UPSTREAM_REPO}"
BRANCH="${BRANCH:-main}"
# 1. Look up the upstream clone URL via bb (HTTPS).
# Pipe --json to external `jq -r`: the built-in --jq JSON-encodes its
# output, so the URL would come back wrapped in literal double quotes.
upstream_url=$(bb repo view -w "$UPSTREAM_WORKSPACE" -r "$UPSTREAM_REPO" --json \
| jq -r '.links.clone[] | select(.name == "https") | .href')
if [ -z "$upstream_url" ]; then
echo "Could not resolve HTTPS clone URL for $UPSTREAM_WORKSPACE/$UPSTREAM_REPO" >&2
exit 1
fi
# 2. Add the upstream remote if it isn't there.
if ! git remote get-url upstream >/dev/null 2>&1; then
echo "Adding upstream remote: $upstream_url"
git remote add upstream "$upstream_url"
else
git remote set-url upstream "$upstream_url"
fi
# 3. Fetch upstream and your fork.
git fetch upstream "$BRANCH"
git fetch origin "$BRANCH"
# 4. Switch to the branch and rebase onto upstream.
git checkout "$BRANCH"
git rebase "upstream/$BRANCH"
# 5. Push the rebased branch back to your fork.
# Use --force-with-lease, NOT --force, so concurrent fork-side pushes aren't clobbered.
git push --force-with-lease origin "$BRANCH"
echo "Fork '$BRANCH' synced with upstream $UPSTREAM_WORKSPACE/$UPSTREAM_REPO"
Terminal window
UPSTREAM_WORKSPACE=acme \
UPSTREAM_REPO=widget \
BRANCH=main \
./sync-fork.sh

Recipe: sync, then open a pull request for a feature branch

Section titled “Recipe: sync, then open a pull request for a feature branch”

After syncing main, rebase your feature branch and open a pull request (PR) back to upstream.

bb pr create cannot do this. Its -s/--source and -d/--destination are both plain branch names resolved inside the -w/-r repository, so it has no way to name your fork as the source — pointing it at upstream either 404s or opens a same-repo PR in upstream. Use bb api to POST the cross-fork body yourself.

sync-and-pr.sh
#!/bin/bash
set -euo pipefail
UPSTREAM_WORKSPACE="${UPSTREAM_WORKSPACE:?set UPSTREAM_WORKSPACE}"
UPSTREAM_REPO="${UPSTREAM_REPO:?set UPSTREAM_REPO}"
FORK_WORKSPACE="${FORK_WORKSPACE:?set FORK_WORKSPACE}" # your workspace
FORK_REPO="${FORK_REPO:?set FORK_REPO}" # your fork's repo slug
FEATURE_BRANCH="${FEATURE_BRANCH:?set FEATURE_BRANCH}"
TITLE="${TITLE:-Update from $FEATURE_BRANCH}"
DESTINATION="${DESTINATION:-main}"
# 1. Sync main, then rebase the feature branch onto fresh main.
BRANCH=main ./sync-fork.sh
git checkout "$FEATURE_BRANCH"
git rebase main
git push --force-with-lease origin "$FEATURE_BRANCH"
# 2. Open a PR in the upstream repository, sourced from the fork's branch.
# Build the body with jq so a title containing quotes or backslashes
# can't break the JSON.
jq -n \
--arg title "$TITLE" \
--arg source "$FEATURE_BRANCH" \
--arg fork "$FORK_WORKSPACE/$FORK_REPO" \
--arg destination "$DESTINATION" \
'{
title: $title,
source: {
branch: { name: $source },
repository: { full_name: $fork }
},
destination: { branch: { name: $destination } }
}' \
| bb api POST "/repositories/$UPSTREAM_WORKSPACE/$UPSTREAM_REPO/pullrequests" --input -

The path is fully interpolated from shell variables, so bb api has no {workspace}/{repo} placeholders left to substitute. Don’t mix the two forms in one path.

Terminal window
UPSTREAM_WORKSPACE=acme UPSTREAM_REPO=widget \
FORK_WORKSPACE=myuser FORK_REPO=widget \
FEATURE_BRANCH=feat/login \
TITLE="Add login button" \
./sync-and-pr.sh

Cross-fork PRs require that the upstream repository accepts forks as sources and that your token can read it. If Bitbucket rejects the request, its message is printed to stderr as ✗ … and the CLI exits 1. Re-run with --json to get the same error as one compact line on stderr:

{"name":"APIError","code":2003,"message":"You do not have access to create a pull request","statusCode":403}
  • Repository Context — how bb resolves workspace/repo from your git remote
  • bb repo view — used here to fetch the upstream HTTPS clone URL
  • bb pr create — same-repository PRs, where the source branch lives in the target repository
  • Raw API accessbb api, used here for the cross-fork POST