GitHub recently announced a new way to update branches in pull requests. Instead of requiring stale branches to be updated via a merge, they now offer the option to update via a rebase!
But what exactly does this mean? It's easiest to explain with an example. Imagine an active git repository with a main branch that already has a few commits on it.
A developer comes along and decides to add code to the repository, so they create a new branch for their feature and make commits there. At the same time, a second developer pushes code to the main branch. The result is two divergent branches.
Eventually, the feature branch developer will want to reunite their code with the main branch, but this is tricky to do since the branches have diverged. The feature branch is stale because it's missing the latest commits from the main branch, and the developer must decide how to combine the commits from each of the branches before the branches can merge. Luckily, the developer has a few options.
Option 1: Do nothing
The developer can choose to not update the feature branch. Instead, they delay the resolution of code differences between the two branches until the reunification step. There are a few ways to reunite the two branches, but for the sake of simplicity, let's consider only the merge-and-commit for now.
When the feature branch is merged, a new commit is created on the main branch. If the divergent branches modified mutually exclusive blobs of code, then this merge is relatively painless. If not, the developer must resolve all merge conflicts that arise at the time that the new commit is created.
All told, this method is simple and popular. However, if a codebase has CI/CD tools enabled, those tools will not run on the combined code until after the merge commit. This means that the main branch can be polluted with commits that fail CI tests, which may be undesirable.
Option 2: Update via merge
Alternatively, the developer can choose to update their feature branch via a merge. This means that they pull all commits from the main branch and apply the missing ones as a single "squashed" commit to the feature branch.
When the merge commit is applied to the feature branch, merge conflicts may arise and will need to be resolved. Any CI/CD tools will run on the combined code that now sits on the feature branch, and the developer can have higher confidence that their code changes will not break the main branch when the feature branch is eventually merged in.
The downside to these kinds of updates is that the commit history becomes difficult to follow. Arrows point to and from various branches, so when several developers work on the same codebase, the history can look like something straight out of a horror film.
Option 3: Update via rebase
And so finally, GitHub introduced the update via rebase. With this option, the commits from the feature branch are pulled and applied on top of the latest commit of the main branch. Think of it as a literal tree branch that gets sawed off the trunk and then glued back on higher up on the tree.
Note that the new commits are not 100% identical to the old commits. They have different parents, so the code will be slightly different from the original feature branch. But the commit history will be easy to read!
Of course, this method also has drawbacks. For one, pressing the button to "update with rebase" on GitHub will only modify the remote copy of the repository, and because a rebase changes the parent lineage instead of creating new commits, the developer will need to sync their local copy of the repository by using a hard reset instead of the usual git pull
. For another, rebase conflicts can be just as much of a nightmare as merge conflicts. Make sure to be comfortable with rebases before choosing this option.
Conclusion
I'm excited about this announcement, because it's the way I've been coding all along! I suspect many other developers do it too if GitHub thought it deserved a dedicated UI button. So yay! Happy rebasing!