Rebase your branches like you brush your teeth

Tomas Istenik
INLOOPX

--

There are many ways to employ git when working on your projects. This article explains why rebase should be a standard tool in every developer’s arsenal.

A single git commit should contain an isolated atomic change to the codebase - a single change should be present in a single commit, and a single commit should contain exactly one change. In short - the structure of the repo should be kept simple, and only contain relevant information. It only takes a few rules to ensure that you don’t go too far off the tracks and mess up your repository.

Develop (master) branch is to be used only for the finished features that are reviewed and approved. The branch is often connected to CI webhooks so anything that happens with it usually leaves a trace somewhere further down the pipeline. You can have multiple branches in the role of develop, especially if you are working on complex longtime features developed in parallel to develop. Similar rules to develop should apply to these branches.

Feature branches are there for the developers to work and play on. A feature branch should only address a single issue and is by default, owned by a single developer.

Note that the recommendations here are not set in stone, and might sometimes seem too strict. Feel free to adjust them as you see fit.

Feature branches only interact with develop - never other feature branches. Created on develop, rebased onto develop, merged into develop.

Ensuring the commits have reasonable size gives you some freedom over how to group stuff together. As mentioned earlier, a commit should contain a single isolated atomic change. In practice, it’s often good enough to contain several related changes dealing with a single issue. It’s up to you to choose how many commits are reasonable for the change you are working on, and how big they are, as long as each one contains independent changes.

Merging three feature branches into develop without using rebase or squashing (using explain-git-with-d3 by Wei Wang)

Rebase daily. If your feature branch takes longer, integrate the newest develop often in order to resolve conflicts quickly. The final merge will be easier to prepare.

Use rebase instead of merge to integrate newest develop into your feature branches. The structure becomes clearer and there is no benefit from untouched git history when it contains no relevant info (merge commits develop → your feature branch).

Do not touch feature branches that don’t belong to you without consent of their developers - since rebasing requires force pushing, your changes could be easily lost. Feature branches are there for development, and are therefore subject to having their history changed and overridden.

Merging the same three feature branches with each one rebased continuously (using explain-git-with-d3 by Wei Wang)

Do not worry about WIP (work in progress) commits. Sometimes you may need to juggle between multiple issues in several branches or just want to have your work backed up, and git stash simply doesn’t cut it. With rebase and squash, your history is not final, so you can easily remove them later.

Squash redundant commits after it becomes apparent they can not be isolated. A single feature or smaller change should usually have a single commit. Imagine trying to revert a feature and only hitting half of the changes because there is one more commit hiding somewhere.

Prepare your feature branches for code reviews. This rule sounds like a direct contradiction to the previous one, but if it makes sense to submit a pull request with multiple commits - each reviewable separately - you should consider doing so. It simplifies the review process, and therefore leads to better code. Changes based on code reviews should also have their own commits to simplify additional reviews.

Merging three feature branches that have been done as a single commit each (using explain-git-with-d3 by Wei Wang)

Also prepare your branches for the merge itself - by squashing code review changes and performing a final rebase. Merging a rebased branch actually only requires a fast-forward so that no actual merge commits are needed. We do, however, retain merge commits in our team for our CI to generate version increment numbers.

Remove unused branches. If a branch is in the repository, it is either being developed or is a backup of some important functionality. Name them accordingly and always remove branches that are no longer needed.

Consider your options and don’t worry about experimenting. If you are about to perform something risky, consider creating a backup branch, so you can roll back to it anytime with no effort. If you still get into trouble, there's always git reflog to help you undo almost everything. It's possible to use git commit --amend instead of having several WIP commits. For squashing, the most common way is to usegit rebase --interactive and edit each commit separately, but a single well-targeted git reset --soft with creating a new commit is also an alternative. Let git work for you, not the other way around.

--

--