Collect a keypress in a bash githook

After a flurry of accidental pushes direct to the main branch at work, I decided we need a technical solution. The obvious answer would be to use GitLab’s protected branches, but that’s not practical at our org. Next best: a to check the branch one is committing to, and have the user confirm or cancel as the case may be.

Enter githooks: we already have repo-based githooks set up so they’re propagated to every dev and regenerated at every checkout/clone. If you’re not familiar with the concept, “git hooks” are scripts which are called as part of various git workflows to modify or interrupt actions. In this case, it’s a pre-commit hook, which may block the commit if it exits with an error.

Collecting a keypress in is pretty trivial:

  read -p "Are you sure you want to commit to #develop? [y/N] " -n1 -r
  echo -e "\n"

The read command solicits user input; -n1 limits the input to one character and -r disallows backslash escapes. The echo -e "\n" jumps to the next line (read -n leaves the cursor at the end of the input) then prints another blank line just for kicks.

Next consideration: Git hooks are not normally called interactively; git itself pushes data to the hook via both cli parameters and stdin. We need to reattach the script’s stdin to the console:

  exec < /dev/tty

Tested on the correct branch, and this does the trick! Here’s the final script:

#! /bin/bash

# get the current branch name
BRANCH=`git branch --show-current`

# If it's #develop, we need to prompt the user
[[ "$BRANCH" == "develop" ]] && {
  cat <<EOT
*** Commit to #develop ***

You are about to commit a change directly to #develop. Are you sure this is 
what you want?

Consider:

    git switch -c feature/jira-123-new-branch-name

EOT

  # Reattach stdin to the tty
  exec < /dev/tty

  # Collect input
  read -p "Are you sure you want to commit to #develop? [y/N] " -n1 -r
  echo -e "\n"

  # Anything but the affirmative means stop
  [[ "$REPLY" == "Y" ]] || [[ "$REPLY" == "y" ]] || {
    echo "Aborting..."
    exit 1
  }
}

Author: Eddie Roosenmaallen

By day I'm the Release Manager at Distributive; I help build the Distributed Compute Protocol at https://dcp.cloud. In my off time I explore Linux, JavaScript and the web, and I try to share some of my knowledge and some of my humour online.

Leave a Reply

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