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 master. I took the lazy approach went to Google for a quick solution which I could just copy and paste.

And I found a lot. However I noticed that most of them 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s