Safely Rebase a Development Branch onto Master with GitHub Actions

Safely Rebase a Development Branch onto Master with GitHub Actions

A hotfix merged into master does not automatically reach develop. You can use GitHub Actions to rebase develop whenever master changes, but the workflow must force-push because a rebase creates new commit IDs.

That makes this suitable only when your team allows develop to be rewritten. If several people push directly to develop, merging master into it is usually safer.

The workflow

Create .github/workflows/rebase-develop.yml:

name: Rebase develop onto master

on:
  push:
    branches:
      - master
  workflow_dispatch:

permissions:
  contents: write

concurrency:
  group: rebase-develop-onto-master
  cancel-in-progress: false

jobs:
  rebase:
    runs-on: ubuntu-24.04

    steps:
      - name: Check out develop
        uses: actions/checkout@v7
        with:
          ref: develop
          fetch-depth: 0

      - name: Rebase develop onto master
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

          git fetch origin master develop

          expected_develop="$(git rev-parse origin/develop)"

          git checkout -B develop origin/develop
          git rebase origin/master

          git push \
            --force-with-lease=refs/heads/develop:"$expected_develop" \
            origin HEAD:develop

The workflow fetches both branches, records the current develop commit, rebases onto master, and pushes only if the remote branch still points to the recorded commit.

That last condition matters. A normal force-push can overwrite work added while the workflow is running. The explicit lease makes the push fail instead.

What happens when there is a conflict?

The rebase command fails and the workflow stops. Nothing is pushed to GitHub.

Resolve the conflict locally:

git fetch origin
git checkout develop
git rebase origin/master

After resolving each conflict:

git add <resolved-files>
git rebase --continue

Then update the branch with a lease-protected push:

git push --force-with-lease origin develop

Do not add an automatic conflict resolution strategy. Git cannot decide which version preserves the intended behavior.

Check branch protection first

GitHub disables force-pushes to protected branches by default. If develop is protected, the repository rules must allow this workflow's actor to update it.

Do not weaken protection on master. The workflow only needs permission to update develop.

A limitation of GITHUB_TOKEN

actions/checkout uses the workflow's GITHUB_TOKEN by default. This is enough to push when the repository permissions allow it.

However, a push made with GITHUB_TOKEN normally does not start another workflow listening for push. If you expect a separate CI workflow to run after develop changes, use a narrowly scoped GitHub App token or trigger that workflow another way.

When a merge is safer

Rebasing changes the commit IDs on develop. Anyone with work based on its previous history must rebase or reset their local branch.

If develop is a shared integration branch, merge instead:

git checkout develop
git merge origin/master
git push origin develop

The history is less linear, but existing commits keep their IDs and the push does not need to be forced.

Use the automated rebase only when linear history is worth the cost of rewriting a shared branch.

Sources