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 #githook 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 #bash 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
}
}