Git workflows revisited
Photo by Yancy Min on Unsplash
Git’s strength is being a tool that can support a wide range of development workflows. It’s therefore not surprising that the developer community is divided on how it should be used. The main division lies between the merge and the rebase workflows.
In the context of this article I’m making a few assumptions. Firstly, I’m assuming that work is done in feature branches (typically) by a single developer. After the work is done, a pull request is created, reviewed and the work is then somehow added to the main development branch. This article also doesn’t discuss how git branching should be used (e.g. one master or multiple environment branches).
This article is a part of my software development foundations series, be sure to check my previous article discussing daily scrum meetings if you missed it.
That said, lets dive in.
The “merge workflow” simply means that the feature branch is merged back to the development branch after the work is done. This actually has a few slightly different meanings or variations. One decision is if a merge commit should always be created (by disabling fast-forward merges). Another variation concerns how the feature branch should be kept up to date. Alternatives include merging the development branch or rebasing. In GitHub terms the merge workflow is done with “Merge pull request” or “Squash and merge” options.
Feature branch merged back to the master branch using GitHub’s default option
The pull request can be squashed either manually with the git client or with GitHub’s “Squash and merge” option to combine several commits of the feature into one. This can make the version history cleaner.
Feature branch squashed and merged to the master branch using GitHub’s “Squash and merge”
Some prefer to squash the commits manually before pushing the pull request, then merging it back with a normal merge commit.
Feature branch squashed locally and then merged back normally.
The “rebase workflow” means that the feature branch is rebased on top of the development branch after the work is done. The benefit is that a merge commit is not created. In GitHub terms the rebase workflow is done with “Rebase and merge”.
The same feature branch rebased and merged to the master branch
Both workflows actually (may) allow rebasing of the feature branch. The rebase can be non-interactive to update the feature branch, or interactive to squash commits or to remove unnecessary “fix typo” commits polluting the history. I recommend using the “local rebase” (possibly interactive) to make the history cleaner in any workflow, even if it technically rewrites history.
Any git workflow is a team policy, and both main workflows and their different variations have their own proponents, cons and pros. The merge workflow is probably more common.
The benefits of the merge workflow include simplicity, as practically every developer understands merging but possibly not rebasing. It also retains more history even when local rebasing is used. Additionally, it’s easy to revert a merge commit to revert a group of commits related to a specific feature. The downsides include making the version history more difficult to understand. Not only is the git log full of merge commits, a visual graph representation of the commit history can be difficult to decipher.
The benefits of the rebase workflow include a clean, linear history devoid of merge commits and distracting branching. The downsides include more rewriting of history, which can sometimes (but rarely) lose some important piece of context. Rebasing is also more difficult for developers to understand. Additionally, it’s more difficult to revert a feature, as there is no single merge commit to revert.
My own preference is a combination of the two workflows. I like to use the rebase workflow if the feature branch only contains a single commit. Otherwise I use the merge flow and always create a merge commit. This policy tries to retain the positives of the two different workflows by making the history relatively clean but also making it easy to revert multi-commit features. The downside is with the tooling — GitHub and others don’t support this out of the box. This means that the developers need to remember to use the correct merging strategy when finishing the pull request.
Whatever workflow your team chooses, the important thing is to keep your workflow consistent and clearly documented.