Git. A pre-push hook to protect remote branches, done right.

I was looking for a way to accomplish remote branches protection using a pre-push git hook. What I needed was basically a way to prevent people, and myself, from accidentally pushing to the master branch. I took the lazy approach went to Google for a quick solution which I could just copy and paste. And I got disappointed.

I noticed that most of the proposed solutions share one issue, which makes them unreliable in some scenarios. The most common way to implement such a hook is to use git rev-parse or git symbolic-ref to figure out the current local branch, the next step is to assume that the current branch will be pushed to a branch with the same name on the remote and prevent it if that’s the case. This, as mentioned, is not always the best approach.

The problem

Consider a repository where push.default has been set to matching, according to the git-config documentation the behavior of git push will be the following:

Push all branches having the same name on both ends.

This means that a git push done from a feature branch or bugfix branch will push all the other branches as well, including master and any branch we are trying to protect. Since the hook only checks the current branch name, it can easily be fooled with this setup. If there are unpublished commits on master they will just be pushed and the hook will not notice.

Another dangerous scenario is the use of git push --all this would also not be detected and write directly to master.

So there are cases in which looking at the command line or using git rev-parse or git symbolic-ref is just not good enough.

A different approach

This is why I came up with my own pre-push hook trying to address this kind of issues. Turns out that the pre-push hook receives from stdin a list of very useful values, again from the githooks documentation

local_ref  local_sha1  remote_ref  remote_sha1

Reading these values provided to us by git is a lot more reliable and secure, the check is done on remote_ref, which is compared against a list of protected remote refs. If a push is attempted to a protected remote ref then the hook returns error. That simple.

So that’s it, I thought it would be nice to share this. The python3 code for this hook can be found below. To use it, copy it inside .git/hooks/pre-push in your repository and remember to set it to executable. If the hook is not executable it will be skipped with no error.

Have a nice day!

Unique opportunity! Help a fellow grow his blog!

Hi there! If you’ve read this far maybe you think this was useful, or fun, or I don’t know what but for some reason You Got Here! Great! Please consider sharing this post with your network, I am trying to get The Code Butchery to grow so I can provide more content like this, will you help me in my journey? Thank you!

Share this

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.