Git’s force push feature is the source of a lot of controversy and errors. It’s widely considered to be dangerous and left for use only in extreme situations. I use it every single day.
The atlassian guide on git offers the following advice.
Same as the above command, but force the push even if it results in a non-fast-forward merge. Do not use the –force flag unless you’re absolutely sure you know what you’re doing.
sensible.io has a blog post dedicated to the subject with the final words
Use force push only as a last resort when everything else fails. Things might get ugly for you and for your repository.
In November 2013 the developers of Jenkins had a huge problem at their hands when one of the developers accidentally force pushed more than 150 repositories.
Ground rules
As warned above force push is quite a powerful feature which will cause issues when used incorrectly. Therefore we need to establish some ground rules before doing any force pushing.
1 Force pushing to the master or development branches should be avoid at all costs. If it is absolutely necessary it should be coordinated with all developers in the team. If possible force pushing should be disabled for these branches.
2 Work should not be done in master or development, but in feature branches. Features should be small enough that only one developer work in the each branch or in a fork of the main repository.
3 Commits are considered immutable only after they have been merged into master or development. Before that they maybe be reordered, squashed and deleted at will.
With these grounds rules established we can now look at a few scenarios when it’s useful to use force push.
Cleaning up the intention of a feature branch
Let’s say I am working on implementing a method in a JavaScript project. My initial implementation looks like this
I make a commit and submit a PR. My git log now looks like this
The PR is reviewed and luckily someone finds my spelling mistake. So I fix it.
It was also pointed out that I didn’t wrap my code in an anonymous self executing function so I fix that too.
Lastly someone points out that this function would fit better in math.js
so I move it there.
This is my final implementation
If we now merge this PR we will merge 4 commits out of which 3 are just fixing issues in the first one. Instead of merging the PR I use interactive rebase to squash all commits into a single commit which reflects the work done in this feature branch.
My final git log is
I try to push my changes, but I can’t. My push is being rejected by the remote.
Here force push comes to the rescue, the PR is merged and the git log of the repository is kept unpolluted by useless commits.
Forgetting that one thing
Again let’s say I am implementing some feature with the following code
1
2
3
4
var implodeUniverse = function () {
universe.implode();
universe = null;
};
I commit my changes and end up with a git log like the following.
Just as I push my changes to the remote I realise that I forgot to check the answer before imploding the universe. I have to modify my code a bit.
1
2
3
4
5
6
7
8
var implodeUniverse = function (answer) {
if (answer == 42) {
return;
}
universe.implode();
universe = null;
};
At this point I could make another commit and then push it. A commit message like “Add check of answer before imploding universe” would have made sense here, however the answer check is really something that should have been in the original commit. It is not deserving of its very own commit. A better solution to this problem is:
Now I can open my pull request and maintain a clean git log without surplus commits.
Merging a diverged branch
In some cases your branch might diverge from the branch it was forked off of. When this happens GitHub shows the following UI:
These are the steps that GitHub suggests to resolve this issue
The problem with this solution is that you will unfortunately create a merge commit which looks like this
Commits like these will pollute your commit log and make it harder to read. They should be avoided when they are not the result of a PR or syncing develop/master with each other. Instead we can use a combination of rebase and force push to solve the problem.
Conclusion
Force push is an extremely powerful ally when utilised correctly. It helps a team keep the git log clean from surplus and misleading commits by instead making the intent of each feature branch and each commit clearer. However a good branching model and workflow is required to support heavy usage of force push. The fact that commits are not considered immutable and sacred until they have been merged into a stable branch is very important if force push is to be used heavily. The forking model used for open source projects on GitHub works very well with this paradigm of force pushing. As a policy I always use rebase and force push to clean-up my pull request just before being merged into the forked repository.
Log comparison
The following is a comparison between using and not using the techniques I’ve showed in this post.
Without force push
With force push