Skip to main content

Rebasing a local branch onto a remote branch

· 8 min read
Serhii Hrekov
software engineer, creator, artist, programmer, projects founder

Rebasing a local branch onto a remote branch is a common workflow for keeping your feature branch up-to-date with the main development branch (like main or master) and maintaining a clean, linear commit history.

The process involves a few key steps: fetching the latest changes, checking out your local branch, performing the rebase, handling conflicts, and then force-pushing your changes if the branch was already shared.


The Problem

Imagine this scenario: you create a new feature branch, say feature/new-api, from main. While you're working on it and making new commits, other developers are also pushing their changes to main. Your feature/new-api branch now has diverged from main, and its commits are based on an older version of the main branch.

To integrate the new changes from main into your feature branch, you have two options: git merge or git rebase. While merge is safe and preserves history, it creates a "merge commit" which can clutter the history. Rebasing, on the other hand, rewrites the history of your feature branch so that it appears as if you created it from the very latest commit on main.


The Workflow

Here's the step-by-step guide to rebase your local branch onto a remote branch. We'll use a feature/new-api branch and rebase it onto the remote main branch, assuming your remote is named origin.

1. Fetch the Latest Changes

Before doing anything, you need to make sure your local repository's knowledge of the remote branches is up-to-date. This command downloads all the latest commits from the remote repository (1).

git fetch origin

This updates all your remote-tracking branches (like origin/main or origin/feature/new-api) but doesn't change any of your local branches.

2. Checkout Your Local Branch

Switch to the branch you want to rebase.

git checkout feature/new-api

3. Perform the Rebase

Now, run the rebase command. You'll be rebasing your current branch (feature/new-api) onto the remote-tracking branch (origin/main).

git rebase origin/main

Git will now take all of your commits on feature/new-api that are not on origin/main and "replay" them one by one on top of the latest commit of origin/main.


4. Resolve Conflicts (If Any)

This is the most critical part. It's very likely that changes you made in your branch will conflict with changes that were merged into main. Git will pause the rebase process and tell you which files have conflicts.

You'll see output like this:

Applying: Your commit message
Using index info to revise base tree...
M path/to/conflicting_file.py
Falling back to patching base and 3-way merge...
Auto-merging path/to/conflicting_file.py
CONFLICT (content): Merge conflict in path/to/conflicting_file.py
error: Failed to merge in the changes.
Patch failed at 0001 Your commit message
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, then write the result to the index with 'git add .'
'git rebase --continue'.
You can instead skip this commit: 'git rebase --skip'
To abort and get back to the state before the rebase, run 'git rebase --abort'
Could not apply 1a2b3c4... Your commit message
  1. Open the conflicting files in your code editor.
  2. Manually resolve the conflicts. Git marks the conflict sections with <<<<<<<, =======, and >>>>>>>.
  3. Add the resolved files to the staging area.
git add path/to/conflicting_file.py
  1. Continue the rebase.
git rebase --continue

Git will continue replaying the next commit. You must repeat this process for every commit that has a conflict.

If at any point you want to give up and return your branch to its state before the rebase, you can run:

git rebase --abort

5. Push Your Changes

Once the rebase is complete and all conflicts are resolved, you have a new, linear commit history.

If this is a brand new branch that you haven't pushed yet, a regular push will work.

git push -u origin feature/new-api

However, if you've already pushed your branch to the remote repository, a regular git push will fail because the remote history doesn't match your new, rewritten local history.

You'll get an error like "Updates were rejected because the tip of your current branch is behind its remote counterpart."

To fix this, you must force-push your changes. This tells Git to overwrite the remote branch with your local history (2).

git push --force-with-lease origin feature/new-api

--force-with-lease is the safest way to force-push. It will only overwrite the remote branch if nobody else has pushed new commits since your last pull. If someone else did, the push will fail, preventing you from accidentally overwriting their work.


Best Practices

  • Don't Rebase Public Branches: Never rebase a branch that is shared and actively being used by other developers. Rebasing rewrites history, which will cause major issues for anyone who has pulled the original version of that branch (3). Only rebase your own private feature branches.
  • Rebase Frequently: The more often you rebase, the smaller the changes you'll be incorporating, and the fewer conflicts you'll have to resolve.
  • Interactive Rebase for Cleanup: You can use git rebase -i to clean up your commit history before rebasing onto a remote branch. This allows you to squash multiple small commits into one, reorder commits, or edit commit messages. This is a great way to present a clean, concise history for code review.

Sources

  1. Git Fetch
  2. Git Push
  3. The Golden Rule of Rebasing