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.
Prepare your fork for rebasing
First, make sure you have two remotes for your repo:
origin (your fork).
$ git remote -v origin [email protected]:anavarre/repo.git (fetch) origin [email protected]:anavarre/repo.git (push) upstream [email protected]:vendor/repo.git (fetch) upstream [email protected]: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 [email protected]: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.
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.
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
+ signs basically acts as a force push to rewrite history.