1*7249d1a6SKrzysztof Kosiński#!/usr/bin/env bash 2*7249d1a6SKrzysztof Kosiński 3*7249d1a6SKrzysztof Kosiński# Git pre-commit hook to check staged Python files for formatting issues with 4*7249d1a6SKrzysztof Kosiński# yapf. 5*7249d1a6SKrzysztof Kosiński# 6*7249d1a6SKrzysztof Kosiński# INSTALLING: Copy this script into `.git/hooks/pre-commit`, and mark it as 7*7249d1a6SKrzysztof Kosiński# executable. 8*7249d1a6SKrzysztof Kosiński# 9*7249d1a6SKrzysztof Kosiński# This requires that yapf is installed and runnable in the environment running 10*7249d1a6SKrzysztof Kosiński# the pre-commit hook. 11*7249d1a6SKrzysztof Kosiński# 12*7249d1a6SKrzysztof Kosiński# When running, this first checks for unstaged changes to staged files, and if 13*7249d1a6SKrzysztof Kosiński# there are any, it will exit with an error. Files with unstaged changes will be 14*7249d1a6SKrzysztof Kosiński# printed. 15*7249d1a6SKrzysztof Kosiński# 16*7249d1a6SKrzysztof Kosiński# If all staged files have no unstaged changes, it will run yapf against them, 17*7249d1a6SKrzysztof Kosiński# leaving the formatting changes unstaged. Changed files will be printed. 18*7249d1a6SKrzysztof Kosiński# 19*7249d1a6SKrzysztof Kosiński# BUGS: This does not leave staged changes alone when used with the -a flag to 20*7249d1a6SKrzysztof Kosiński# git commit, due to the fact that git stages ALL unstaged files when that flag 21*7249d1a6SKrzysztof Kosiński# is used. 22*7249d1a6SKrzysztof Kosiński 23*7249d1a6SKrzysztof Kosiński# Find all staged Python files, and exit early if there aren't any. 24*7249d1a6SKrzysztof KosińskiPYTHON_FILES=() 25*7249d1a6SKrzysztof Kosińskiwhile IFS=$'\n' read -r line; do PYTHON_FILES+=("$line"); done \ 26*7249d1a6SKrzysztof Kosiński < <(git diff --name-only --cached --diff-filter=AM | grep --color=never '.py$') 27*7249d1a6SKrzysztof Kosińskiif [ ${#PYTHON_FILES[@]} -eq 0 ]; then 28*7249d1a6SKrzysztof Kosiński exit 0 29*7249d1a6SKrzysztof Kosińskifi 30*7249d1a6SKrzysztof Kosiński 31*7249d1a6SKrzysztof Kosiński########## PIP VERSION ############# 32*7249d1a6SKrzysztof Kosiński# Verify that yapf is installed; if not, warn and exit. 33*7249d1a6SKrzysztof Kosińskiif ! command -v yapf >/dev/null; then 34*7249d1a6SKrzysztof Kosiński echo 'yapf not on path; can not format. Please install yapf:' 35*7249d1a6SKrzysztof Kosiński echo ' pip install yapf' 36*7249d1a6SKrzysztof Kosiński exit 2 37*7249d1a6SKrzysztof Kosińskifi 38*7249d1a6SKrzysztof Kosiński######### END PIP VERSION ########## 39*7249d1a6SKrzysztof Kosiński 40*7249d1a6SKrzysztof Kosiński########## PIPENV VERSION ########## 41*7249d1a6SKrzysztof Kosiński# if ! pipenv run yapf --version 2>/dev/null 2>&1; then 42*7249d1a6SKrzysztof Kosiński# echo 'yapf not on path; can not format. Please install yapf:' 43*7249d1a6SKrzysztof Kosiński# echo ' pipenv install yapf' 44*7249d1a6SKrzysztof Kosiński# exit 2 45*7249d1a6SKrzysztof Kosiński# fi 46*7249d1a6SKrzysztof Kosiński###### END PIPENV VERSION ########## 47*7249d1a6SKrzysztof Kosiński 48*7249d1a6SKrzysztof Kosiński 49*7249d1a6SKrzysztof Kosiński# Check for unstaged changes to files in the index. 50*7249d1a6SKrzysztof KosińskiCHANGED_FILES=() 51*7249d1a6SKrzysztof Kosińskiwhile IFS=$'\n' read -r line; do CHANGED_FILES+=("$line"); done \ 52*7249d1a6SKrzysztof Kosiński < <(git diff --name-only "${PYTHON_FILES[@]}") 53*7249d1a6SKrzysztof Kosińskiif [ ${#CHANGED_FILES[@]} -gt 0 ]; then 54*7249d1a6SKrzysztof Kosiński echo 'You have unstaged changes to some files in your commit; skipping ' 55*7249d1a6SKrzysztof Kosiński echo 'auto-format. Please stage, stash, or revert these changes. You may ' 56*7249d1a6SKrzysztof Kosiński echo 'find `git stash -k` helpful here.' 57*7249d1a6SKrzysztof Kosiński echo 'Files with unstaged changes:' "${CHANGED_FILES[@]}" 58*7249d1a6SKrzysztof Kosiński exit 1 59*7249d1a6SKrzysztof Kosińskifi 60*7249d1a6SKrzysztof Kosiński 61*7249d1a6SKrzysztof Kosiński# Format all staged files, then exit with an error code if any have uncommitted 62*7249d1a6SKrzysztof Kosiński# changes. 63*7249d1a6SKrzysztof Kosińskiecho 'Formatting staged Python files . . .' 64*7249d1a6SKrzysztof Kosiński 65*7249d1a6SKrzysztof Kosiński########## PIP VERSION ############# 66*7249d1a6SKrzysztof Kosińskiyapf -i -r "${PYTHON_FILES[@]}" 67*7249d1a6SKrzysztof Kosiński######### END PIP VERSION ########## 68*7249d1a6SKrzysztof Kosiński 69*7249d1a6SKrzysztof Kosiński########## PIPENV VERSION ########## 70*7249d1a6SKrzysztof Kosiński# pipenv run yapf -i -r "${PYTHON_FILES[@]}" 71*7249d1a6SKrzysztof Kosiński###### END PIPENV VERSION ########## 72*7249d1a6SKrzysztof Kosiński 73*7249d1a6SKrzysztof Kosiński 74*7249d1a6SKrzysztof KosińskiCHANGED_FILES=() 75*7249d1a6SKrzysztof Kosińskiwhile IFS=$'\n' read -r line; do CHANGED_FILES+=("$line"); done \ 76*7249d1a6SKrzysztof Kosiński < <(git diff --name-only "${PYTHON_FILES[@]}") 77*7249d1a6SKrzysztof Kosińskiif [ ${#CHANGED_FILES[@]} -gt 0 ]; then 78*7249d1a6SKrzysztof Kosiński echo 'Reformatted staged files. Please review and stage the changes.' 79*7249d1a6SKrzysztof Kosiński echo 'Files updated: ' "${CHANGED_FILES[@]}" 80*7249d1a6SKrzysztof Kosiński exit 1 81*7249d1a6SKrzysztof Kosińskielse 82*7249d1a6SKrzysztof Kosiński exit 0 83*7249d1a6SKrzysztof Kosińskifi 84