Stephon Harris
May 16, 2018 · 3 min read

With the current development environment focusing on rapid development, it can be easy for developers, especially those newly introduced to the team. With changes flying in your git repository constantly, a developer can accidentally break your code by merging in code that does not have the latest changes incorporated in it. One way to prevent this damage is to not allow a push to your remote repository of a branch if it is not based on the latest changes in master. “How?” you ask. By introducing git hooks to your team. Git hooks are scripts or programs that Git triggers during a part of Git’s execution. As of this writing, the current git hooks are:

  • applypatch-msg
  • pre-applypatch
  • post-applypatch
  • pre-commit
  • prepare-commit-msg
  • commit-msg
  • post-commit
  • pre-rebase
  • post-checkout
  • post-merge
  • pre-receive
  • update
  • post-receive
  • post-update
  • pre-auto-gc
  • post-rewrite
  • pre-push

I’m going to use the pre-push hook to show you how to prevent a feature branch from being pushed to your remote if it’s not based on the latest commit in master.

pre-push script

Here’s what’s going on in the pre-push hook:

red=’\033[0;31m’green=’\033[0;32m’yellow=’\033[0;33m’no_color=’\033[0m’BRANCH=”$(git symbolic-ref HEAD 2>/dev/null)”

To print out the error messages with red, green, yellow, or the default color, I set the output color of echo with ANSI escape codes. The BRANCH variable is set to git symbolic-ref HEAD 2>/dev/null to capture which branch we are currently on in git.

If the git symbolic-ref command exited with a non-zero status that means that the HEAD reference is detached from a branch. You can commonly get in a detached head state by checking out a remote branch (like git checkout origin/branch) without first making a local copy. The script informs the user that they should ensure they are on a proper branch.

BRANCH=${BRANCH##refs/heads/}

We then use substring removal to get only the branch name from the previous command.

if git merge-base — is-ancestor refs/remotes/origin/master ${BRANCH};then...

Here is where the magic happens. Let’s walk through each part of this git command.

  • merge-base finds the common ancestor commit among commits.
  • --is-ancestor Check if the first reference(refs/remotes/origin/master) is an ancestor of the secondBRANCH, then exit with status 0 if true, or with status 1 if not.
  • refs/remotes/origin/master This is a reference to the latest commit on the tip of the remote origin/master branch.

The command checks if the current branch has master as an ancestor. If it does, it will continue with the push. If it does not it will exit the script and print a statement alerting the user that their branch is not based on the latest changes in master. Now all you have to do is save the script into your repo’s .git/hooks directory and make sure it has execute permission.

In a demanding, fast-paced development environment, it can be hard to keep up with the changes, literally. Checking your work is priceless and having tools to help you catch potential bugs can help you ship a better product to your users. I hope this git hook helps you and your team become more productive and prevents frantic fires of breaking code.

Full-stack Firefighter

Putting out fires, one line of code at a time.

Stephon Harris

Written by

Software developer. Turning my problems into community solutions.

Full-stack Firefighter

Putting out fires, one line of code at a time.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade