Pull Requests with Rebase
We’re excited to roll out another way to integrate your pull requests in Azure Repos. Arriving in the Sprint 150 update is an option to rebase your pull request into the target branch. This lets you keep a linear commit history in your master branch, which many people think is an elegant way to visualize history.
Like tabs vs spaces, the way code gets integrated is the subject of heated debates on teams. Some people prefer merges, some people prefer rebase, and some people prefer a hybrid approach or even a “squash”. Azure Repos supports each of these scenarios:
Merge (no fast-forward)
This is the default integration strategy in Azure Repos, GitHub and most other Git providers. It emulates running
git merge pr from the master branch. All the individual commits in the pull request branch are preserved as-is, and a new merge commit is created to unite the master branch and the pull request branch.
This strategy is helpful because it illustrates exactly how a developer (or developers) worked on a pull request, including each individual commit along the way. It gives the most insight into how a branch evolves, but since it preserves every commit is may be very verbose.
Squashing will take the tree that’s produced in a merge and creates a single new commit with those repository contents. It emulates running
git merge pr --squash from the master branch. The resulting commit is not a merge commit; those individual commits that made up the pull request are discarded.
When this strategy is used, history is reminiscent of a centralized version control system. Each pull request becomes a single commit in master, and there are no merges, just a simple, straight, linear history. Individual commits are lost, which is best for teams that use “fix up” commits or do not carefully craft individual commits for review before pushing them.
Rebase will take each individual commit in the pull request and cherry-pick them onto the master branch. It emulates running
git rebase master on the pull reuqest branch, followed by
git merge pr --ff-only on the master branch.
When this strategy is used, history is straight and linear, like it is with the “squash” option, but each individual commit is retained. This is useful for teams that practice careful commit hygeine, where each individual commit stands on its own.
This strategy is the most exotic – it’s a mix of rebase and a merge. First, the commits in the pull request are rebased on top of the master branch. Then those rebased pull requests are merged into master branch. It emulates running
git rebase master on the pull request branch, followed by
git merge pr --no-ff on the master branch.
Some people think of this as the best of both worlds: individual commits are retained, so that you can see how the work evolved, but instead of just being rebased, a “merge bubble” is shown so that you can immediately see the work in each individual pull request.
Many teams want to choose a pull request integration strategy and standardize on it for the whole team. Regardless of whether you prefer the rebase approach or the merge approach, it’s often helpful to use same approach for each pull request.
Thankfully, you can configure a branch policy to enforce your preferred integration strategy (or strategies). Selecting only one will mean that pull requests into master will always use a single strategy.
And, of course, these branch policies that limit the merge types for a pull request are the perfect companion to the other branch policies. One that’s indispensible, of course, is the build validation branch policy, where you can set up an Azure Pipelines build for your master branch, and your build and tests must succeed before a pull request can be merged (or squashed, or rebased) into master.