1#!/usr/bin/env bash 2# git pre-commit hook that runs an clang-format stylecheck. 3# Features: 4# - abort commit when commit does not comply with the style guidelines 5# - create a patch of the proposed style changes 6# 7# modifications for clang-format by [email protected] 8# Link: https://github.com/githubbrowser/Pre-commit-hooks 9# Contact: David Martin, [email protected] 10 11# DESCR: Checking that VCS 'staged' source conforms to coding style 12 13################################################################## 14# SETTINGS 15# set path to clang-format binary 16CLANG_FORMAT="$(command -v clang-format)" 17 18# remove any older patches from previous commits. Set to true or false. 19# DELETE_OLD_PATCHES=false 20DELETE_OLD_PATCHES=false 21 22# only parse files with the extensions in FILE_EXTS. Set to true or false. 23# if false every changed file in the commit will be parsed with clang-format. 24# if true only files matching one of the extensions are parsed with clang-format. 25# PARSE_EXTS=true 26PARSE_EXTS=true 27 28# file types to parse. Only effective when PARSE_EXTS is true. 29# FILE_EXTS=".c .h .cpp .hpp" 30FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx" 31 32################################################################## 33# There should be no need to change anything below this line. 34 35# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac 36canonicalize_filename () { 37 local target_file=$1 38 local physical_directory="" 39 local result="" 40 41 # Need to restore the working directory after work. 42 pushd `pwd` > /dev/null 43 44 cd "$(dirname "$target_file")" 45 target_file=`basename $target_file` 46 47 # Iterate down a (possible) chain of symlinks 48 while [ -L "$target_file" ] 49 do 50 target_file=$(readlink "$target_file") 51 cd "$(dirname "$target_file")" 52 target_file=$(basename "$target_file") 53 done 54 55 # Compute the canonicalized name by finding the physical path 56 # for the directory we're in and appending the target file. 57 physical_directory=`pwd -P` 58 result="$physical_directory"/"$target_file" 59 60 # restore the working directory after work. 61 popd > /dev/null 62 63 echo "$result" 64} 65 66# exit on error 67set -e 68 69# check whether the given file matches any of the set extensions 70matches_extension() { 71 local filename=$(basename "$1") 72 local extension=".${filename##*.}" 73 local ext 74 75 for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done 76 77 return 1 78} 79 80# necessary check for initial commit 81if git rev-parse --verify HEAD >/dev/null 2>&1 ; then 82 against=HEAD 83else 84 # Initial commit: diff against an empty tree object 85 against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 86fi 87 88if [ ! -x "$CLANG_FORMAT" ] ; then 89 printf "Warning: clang-format executable not found.\n" 90 printf "Set the correct path in $(canonicalize_filename "$0").\n" 91 printf "Skipping clang-format style check test.\n" 92# exit 1 93 exit 0 94fi 95 96# create a random filename to store our generated patch 97prefix="pre-commit-clang-format" 98suffix="$(date +%s)" 99patch="/tmp/$prefix-$suffix.patch" 100 101# clean up any older clang-format patches 102$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch 103 104# create one patch containing all changes to the files 105git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; 106do 107 # ignore file if we do check for file extensions and the file 108 # does not match any of the extensions specified in $FILE_EXTS 109 if $PARSE_EXTS && ! matches_extension "$file"; then 110 continue; 111 fi 112 113 # clang-format our sourcefile, create a patch with diff and append it to our $patch 114 # The sed call is necessary to transform the patch from 115 # --- $file timestamp 116 # +++ - timestamp 117 # to both lines working on the same file and having a a/ and b/ prefix. 118 # Else it can not be applied with 'git apply'. 119 "$CLANG_FORMAT" -style=file "$file" | \ 120 diff -u "$file" - | \ 121 sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch" 122done 123 124# if no patch has been generated all is ok, clean up the file stub and exit 125if [ ! -s "$patch" ] ; then 126 printf "Files in this commit comply with the clang-format rules.\n" 127 rm -f "$patch" 128 exit 0 129fi 130 131# a patch has been created, notify the user and exit 132printf "\nThe following differences were found between the code to commit " 133printf "and the clang-format rules:\n\n" 134cat "$patch" 135 136printf "\nYou can apply these changes with:\n git apply $patch\n" 137printf "(may need to be called from the root directory of your repository)\n" 138 139# FIXME: clang-format is currently unusable, so don't abort the commit. 140# printf "Aborting commit. Apply changes and commit again or skip checking with" 141# printf " --no-verify (not recommended).\n" 142# exit 1 143 144exit 0 145