Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Git pre-commit check to stop accidental commits to master and develop branches. There is also a variant with a core.whitespace check.
#!/bin/sh
# This gist contains pre-commit hooks to prevent you from commiting bad code or to the wrong branch.
# There are four variants that I have built:
# - pre-commit: stops commits to "master" and "develop" branches.
# - pre-commit-2: also includes a core.whitespace check.
# - pre-commit-3: the core.whitespace check and an EOF-newline-check.
# - pre-commit-4: only the core.whitespace check.
# - pre-commit-5: elixir formatting check.
# - pre-commit-6: prettier formatting check.
# Set desired version like this before installing:
# FILE=pre-commit
# Global installation instructions: (requires git 2.9 or later)
# NOTE: if you configure core.hooksPath, then git only runs the hooks from that directory (and ignores repo-specific hooks in .git/hooks/), but these pre-commit hooks contain a block at the end which executes a repo-specific pre-commit hook if it's present. It's a small hax that I think is pretty nice.
# mkdir $HOME/.githooks
# git config --global core.hooksPath $HOME/.githooks
# curl -fL -o $HOME/.githooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/${FILE:-pre-commit}
# chmod +x $HOME/.githooks/pre-commit
# Uninstall:
# rm $HOME/.githooks/pre-commit
# Install in current Git repo only:
# curl -fL https://gist.githubusercontent.com/stefansundin/9059706/raw/install-pre-commit.sh | sh -s ${FILE:-pre-commit}
# Uninstall:
# rm .git/hooks/pre-commit
GIT_DIR=`git rev-parse --git-common-dir 2> /dev/null`
echo
echo
if [ "$GIT_DIR" == "" ]; then
echo "This does not appear to be a git repo."
exit 1
fi
if [ -f "$GIT_DIR/hooks/pre-commit" ]; then
echo "There is already a pre-commit hook installed. Delete it first."
echo
echo " rm '$GIT_DIR/hooks/pre-commit'"
echo
exit 2
fi
FILE=${1:-pre-commit}
echo "Downloading $FILE hook from https://gist.github.com/stefansundin/9059706"
echo
curl -fL -o "$GIT_DIR/hooks/pre-commit" "https://gist.githubusercontent.com/stefansundin/9059706/raw/$FILE"
if [ ! -f "$GIT_DIR/hooks/pre-commit" ]; then
echo "Error downloading pre-commit script!"
exit 3
fi
chmod +x "$GIT_DIR/hooks/pre-commit"
echo
echo "You're all set! Happy hacking!"
echo "P.S. There is now a way to install this globally, see the instructions on the gist page."
exit 0
#!/bin/bash
# Stops accidental commits to master and develop. https://gist.github.com/stefansundin/9059706
# Install:
# cd path/to/git/repo
# curl -fL -o .git/hooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/pre-commit
# chmod +x .git/hooks/pre-commit
BRANCH=`git rev-parse --abbrev-ref HEAD`
if [[ "$BRANCH" == "master" || "$BRANCH" == "develop" ]]; then
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
echo "If so, commit with -n to bypass this pre-commit hook."
exit 1
fi
# This block allows for chaining pre-commit hooks if this hook is a global hook (via core.hooksPath) and there also exists a repo-specific pre-commit hook
if [[ -f ".git/hooks/pre-commit" ]]; then
type realpath >/dev/null 2>&1 || { echo >&2 "NOTE: the realpath binary is required to chain to the repo-specific pre-commit hook. Ignoring."; exit 0; }
if [[ "$(realpath "${BASH_SOURCE[0]}")" != "$(realpath ".git/hooks/pre-commit")" ]]; then
.git/hooks/pre-commit
exit $?
fi
fi
exit 0
#!/bin/bash
# Stops accidental commits to master and develop. https://gist.github.com/stefansundin/9059706
# Install:
# cd path/to/git/repo
# curl -fL -o .git/hooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/pre-commit-2
# chmod +x .git/hooks/pre-commit
BRANCH=`git rev-parse --abbrev-ref HEAD`
if [[ "$BRANCH" == "master" || "$BRANCH" == "develop" ]]; then
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
echo "If so, commit with -n to bypass this pre-commit hook."
exit 1
fi
if [ "`git diff --check --cached | wc -c`" -gt 0 ]; then
echo "Your spaces don't agree with your core.whitespace rules."
echo 'Please run `git diff --check HEAD` to see your errors.'
echo "You can commit with -n to bypass this pre-commit hook."
exit 2
fi
# This block allows for chaining pre-commit hooks if this hook is a global hook (via core.hooksPath) and there also exists a repo-specific pre-commit hook
if [[ -f ".git/hooks/pre-commit" ]]; then
type realpath >/dev/null 2>&1 || { echo >&2 "NOTE: the realpath binary is required to chain to the repo-specific pre-commit hook. Ignoring."; exit 0; }
if [[ "$(realpath "${BASH_SOURCE[0]}")" != "$(realpath ".git/hooks/pre-commit")" ]]; then
.git/hooks/pre-commit
exit $?
fi
fi
exit 0
#!/bin/bash
# Stops accidental commits to master and develop. https://gist.github.com/stefansundin/9059706
# Install:
# cd path/to/git/repo
# curl -fL -o .git/hooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/pre-commit-3
# chmod +x .git/hooks/pre-commit
BRANCH=`git rev-parse --abbrev-ref HEAD`
if [[ "$BRANCH" == "master" || "$BRANCH" == "develop" ]]; then
echo "You are on branch $BRANCH. Are you sure you want to commit to this branch?"
echo "If so, commit with -n to bypass this pre-commit hook."
exit 1
fi
if [ "`git diff --check --cached | wc -c`" -gt 0 ]; then
echo "Your spaces don't agree with your core.whitespace rules."
echo 'Please run `git diff --check HEAD` to see your errors.'
echo "You can commit with -n to bypass this pre-commit hook."
exit 2
fi
NOEOF=()
FILES=`git status --porcelain | grep "^M" | cut -b4-`
while read -r f; do
if [ "`tail -c 1 $f`" != "" ]; then
NOEOF+=("$f")
fi
done <<< "$FILES"
if [ ${#NOEOF[@]} -gt 0 ]; then
echo "No newlines at the end of these files:"
for f in "${NOEOF[@]}"; do
echo " $f"
done
echo
echo "To check your whole repository, run this:"
echo
echo ' git ls-tree -r -z --name-only HEAD | xargs -0 file | grep text | cut -d: -f1 | xargs -I {} bash -c '\''if [ -n "`tail -c 1 "{}"`" ]; then echo {}; fi'\'''
echo
echo "You can commit with -n to bypass this pre-commit hook."
exit 3
fi
# This block allows for chaining pre-commit hooks if this hook is a global hook (via core.hooksPath) and there also exists a repo-specific pre-commit hook
if [[ -f ".git/hooks/pre-commit" ]]; then
type realpath >/dev/null 2>&1 || { echo >&2 "NOTE: the realpath binary is required to chain to the repo-specific pre-commit hook. Ignoring."; exit 0; }
if [[ "$(realpath "${BASH_SOURCE[0]}")" != "$(realpath ".git/hooks/pre-commit")" ]]; then
.git/hooks/pre-commit
exit $?
fi
fi
exit 0
#!/bin/bash
# Checks yo whitespace on commit. https://gist.github.com/stefansundin/9059706
# Install:
# cd path/to/git/repo
# curl -fL -o .git/hooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/pre-commit-4
# chmod +x .git/hooks/pre-commit
if [ "`git diff --check --cached | wc -c`" -gt 0 ]; then
echo "Your spaces don't agree with your core.whitespace rules."
echo 'Please run `git diff --check HEAD` to see your errors.'
echo "You can commit with -n to bypass this pre-commit hook."
exit 2
fi
# This block allows for chaining pre-commit hooks if this hook is a global hook (via core.hooksPath) and there also exists a repo-specific pre-commit hook
if [[ -f ".git/hooks/pre-commit" ]]; then
type realpath >/dev/null 2>&1 || { echo >&2 "NOTE: the realpath binary is required to chain to the repo-specific pre-commit hook. Ignoring."; exit 0; }
if [[ "$(realpath "${BASH_SOURCE[0]}")" != "$(realpath ".git/hooks/pre-commit")" ]]; then
.git/hooks/pre-commit
exit $?
fi
fi
exit 0
#!/bin/bash
# Checks elixir formatting on commit. https://gist.github.com/stefansundin/9059706
# Install:
# cd path/to/git/repo
# curl -fL -o .git/hooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/pre-commit-5
# chmod +x .git/hooks/pre-commit
if [ "`git diff --check --cached | wc -c`" -gt 0 ]; then
echo "Your spaces don't agree with your core.whitespace rules."
echo 'Please run `git diff --check HEAD` to see your errors.'
echo "You can commit with -n to bypass this pre-commit hook."
exit 2
fi
mix format --check-formatted || {
echo "You can commit with -n to bypass this pre-commit hook."
exit 3
}
# This block allows for chaining pre-commit hooks if this hook is a global hook (via core.hooksPath) and there also exists a repo-specific pre-commit hook
if [[ -f ".git/hooks/pre-commit" ]]; then
type realpath >/dev/null 2>&1 || { echo >&2 "NOTE: the realpath binary is required to chain to the repo-specific pre-commit hook. Ignoring."; exit 0; }
if [[ "$(realpath "${BASH_SOURCE[0]}")" != "$(realpath ".git/hooks/pre-commit")" ]]; then
.git/hooks/pre-commit
exit $?
fi
fi
exit 0
#!/bin/bash
# Checks prettier formatting on commit. https://gist.github.com/stefansundin/9059706
# Install:
# cd path/to/git/repo
# curl -fL -o .git/hooks/pre-commit https://gist.githubusercontent.com/stefansundin/9059706/raw/pre-commit-6
# chmod +x .git/hooks/pre-commit
if [ "`git diff --check --cached | wc -c`" -gt 0 ]; then
echo "Your spaces don't agree with your core.whitespace rules."
echo 'Please run `git diff --check HEAD` to see your errors.'
echo "You can commit with -n to bypass this pre-commit hook."
exit 2
fi
# https://prettier.io/docs/en/precommit.html#option-5-bash-script
jsfiles=$(git diff --cached --name-only --diff-filter=ACM "*.js" "*.jsx" "*.tsx" | tr '\n' ' ')
[ -z "$jsfiles" ] && exit 0
jsfiles=$(echo "$jsfiles" | xargs ./node_modules/.bin/prettier -l)
[ -z "$jsfiles" ] && exit 0
echo "The following files do not conform to prettier formatting:"
echo "$jsfiles"
echo
echo "See the problem in each file with the following:"
echo "$jsfiles" | while read -r file; do
echo "prettier '$file' | diff '$file' -"
done
echo
echo "Apply changes with the following:"
echo "$jsfiles" | while read -r file; do
echo "prettier --write '$file'"
done
echo
echo "Don't forget to run 'git add' afterwards!"
echo "You can commit with -n to bypass this pre-commit hook."
exit 3
@mmcdaris

This comment has been minimized.

Copy link

mmcdaris commented Jan 27, 2016

Thanks! This is great 👍

@martin-klima

This comment has been minimized.

Copy link

martin-klima commented Jan 28, 2016

Thank you! Very useful.

@EerdeBruining

This comment has been minimized.

Copy link

EerdeBruining commented Apr 15, 2016

Thanks!

@satishksuryawanshi

This comment has been minimized.

Copy link

satishksuryawanshi commented Aug 12, 2016

Thanks a lot

@stefansundin

This comment has been minimized.

Copy link
Owner Author

stefansundin commented Apr 8, 2017

I just updated the instructions above to include instructions for a global installation using the core.hooksPath configuration option introduced in Git 2.9. Update to that and you no longer have to install this in every git repository, just do it once.

@lharzenetter

This comment has been minimized.

Copy link

lharzenetter commented Apr 26, 2018

Thank you for this @stefansundin !
Could you please tell me the license under which your code is available?

Cheers 🍻

@stefansundin

This comment has been minimized.

Copy link
Owner Author

stefansundin commented Apr 26, 2018

@lharzenetter Hmm.. I never thought about putting a license on this. But since you want one, maybe WTFPL will work? I'm a bit excited now because it's my first thing under that license. Let me know if another license would suit you better. Thanks!

@felipe1982

This comment has been minimized.

Copy link

felipe1982 commented Nov 7, 2019

For warnings and errors, it would be good to use echo with >&2 to send the messages to stderr.

@guptarohit

This comment has been minimized.

Copy link

guptarohit commented Nov 25, 2019

I wrote about how to automate code formatting tasks with the pre-commit framework.
Using pre-commit hooks to keep python code clean with black, flake8 and isort. ✌️
☮️ 🍰

@Elmessoudizakaria

This comment has been minimized.

Copy link

Elmessoudizakaria commented Dec 3, 2019

thanks a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.