How to rebase a GitHub pull request

I find it surprisingly confusing to work with GitHub pull requests while I’m primarily used to contribute to drupal.org and its own contribution mechanisms. So I thought I’d write a step-by-step for rebasing a PR and stop looking it up.

When do you need to rebase a PR?

Active projects can get tens of PRs everyday. This means your PR can go stale” quickly. In other words, it means it’s no longer up-to-date with the main line of development and needs to be updated before it can safely be reviewed/merged into the project.

This is similar to applying a patch if you will. If several PRs attempt to modify the same exact line(s) in the same file and one of them gets merged, then all other - unmerged - PRs will now have a merge conflict to resolve. This is not the only reason a merge can fail, but this is for sure the most common scenario you’ll be confronted to.

GitHub merge conflictGitHub merge conflict

Prepare your fork for rebasing

First, make sure you have two remotes for your repo: upstream and origin (your fork).

$ git remote -v
origin  git@github.com:anavarre/repo.git (fetch)
origin  git@github.com:anavarre/repo.git (push)
upstream  git@github.com:vendor/repo.git (fetch)
upstream  git@github.com:vendor/repo.git (push)

Next, you need to ensure your fork is synchronized with the upstream repo. This is very well detailed at https://help.github.com/articles/syncing-a-fork/

Here’s a practical example:

$ git fetch upstream
remote: Counting objects: 168, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 168 (delta 121), reused 121 (delta 121), pack-reused 45
Receiving objects: 100% (168/168), 23.25 KiB | 0 bytes/s, done.
Resolving deltas: 100% (122/122), completed with 62 local objects.
From github.com:vendor/repo
   4db2376..8ece95f  ID.60       -> upstream/ID.60
   95bae36..6945d98  ID.60.1      -> upstream/ID.60.1
   c5f9db4..072416c  fix-memcache   -> upstream/fix-memcache
 * [new branch]      cm-split  -> upstream/cm-split
   74ba779..8b02709  master     -> upstream/master
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge upstream/master
Updating 74ba779..8b02709
Fast-forward
 docroot/src/Endpoint/NewSite.php                        |  21 +-------
 docroot/src/Entity/Sitename.php                         |   5 --
 docroot/src/Repository/Stack.php                        |  27 +++++++---
 docroot/src/CustomService.php                           |  14 +++++-
$  git push
Counting objects: 123, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (80/80), done.
Writing objects: 100% (123/123), 15.49 KiB | 0 bytes/s, done.
Total 123 (delta 90), reused 69 (delta 42)
remote: Resolving deltas: 100% (90/90), completed with 48 local objects.
To git@github.com:anavarre/repo.git    74ba779..8b02709  master -> master

Rebase your pull request

So far, so good. Now, let’s get to the rebase. First things first, we need to checkout the branch from which we originally created the PR.

$ git checkout ID-1234

If we no longer have it locally, we can fetch it from our fork:

$ git fetch
$ git checkout ID-1234

Now, we can run the rebase on top of our development branch.

$ git rebase upstream/master
First, rewinding head to replay your work on top of it...
Applying: ID-1234 | Improve code quality
Using index info to reconstruct a base tree...
M   src/modules/version/D8.mustache
M   src/modules/version/WebConfig.php
tests/modules/D8-settings.inc
tests/stack/orchestration.inc
Falling back to patching base and 3-way merge...
Auto-merging src/modules/version/WebConfig.php
Auto-merging src/modules/version/D8.mustache

The last remaining step is to force push the changes. This will ensure your rebase does replace the outdated PR.

$ git push -f

From the GitHub interface, you will now notice a new reference to the rebase.

Successful GitHub rebaseSuccessful GitHub rebase

If you have a testbot, it’ll get triggered as if you were creating a new PR. The merge conflict message will go away if all is well.

All good!All good!

What if there are changes required in your PR?

A best practice is to have only one commit in your PR. You can absolutely make several commits, but be sure to squash your changes.

There are several ways to do this and the one that I like to use is when squashing the last N consecutive commits into a single commit. This works best when you work with feature branches and can’t commit to master. Here’s an example:

$ git reset --soft HEAD~5
$ git commit -m "ID-1234 | Improve code quality"

And, since we’ve pushed changes to the remote previously, we can simply do:

$ git push origin +ID-1234

The + signs basically acts as a force push to rewrite history.


Tags
Git

Date
February 4, 2017