From 68d1d3b4aff40df4cd35f33cf2ecad7d9b9a56ca Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 31 Mar 2010 17:39:00 -0400 Subject: [PATCH 01/57] Prototype pre-commit and commit-msg hook scripts These hooks perform some basic commit-time checks to keep history clean. --- commit-msg | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pre-commit | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100755 commit-msg create mode 100755 pre-commit diff --git a/commit-msg b/commit-msg new file mode 100755 index 000000000..80f497197 --- /dev/null +++ b/commit-msg @@ -0,0 +1,57 @@ +#!/bin/sh +# +# Copy or link this file as ".git/hooks/pre-commit". + +die() { + echo 'commit-msg hook failure' 1>&2 + echo '-----------------------' 1>&2 + echo '' 1>&2 + echo "$@" 1>&2 + exit 1 +} + +#----------------------------------------------------------------------------- +# Check the commit message layout with a simple state machine. + +msg_first() { + len=$(echo -n "$line" | wc -c) + if test $len -eq 0; then + # not yet first line + return + elif test $len -lt 8; then + die 'The first line must be at least 8 characters: +-------- +'"$line"' +-------- +' + elif test $len -gt 78; then + die 'The first line may be at most 78 characters: +------------------------------------------------------------------------------ +'"$line"' +------------------------------------------------------------------------------ +' + else + # first line okay + state=second + fi +} + +msg_second() { + if test "x$line" != "x"; then + die 'The second line must be empty: +'"$line" + else + state=rest + fi +} + +msg_rest() { + false +} + +# Pipe commit message into the state machine. +state=first +cat "$1" | grep -v '^#' | +while read line; do + msg_$state || break +done diff --git a/pre-commit b/pre-commit new file mode 100755 index 000000000..442fe9a9f --- /dev/null +++ b/pre-commit @@ -0,0 +1,43 @@ +#!/bin/sh +# +# Copy or link this file as ".git/hooks/pre-commit". + +die() { + echo 'pre-commit hook failure' 1>&2 + echo '-----------------------' 1>&2 + echo '' 1>&2 + echo "$@" 1>&2 + exit 1 +} + +#----------------------------------------------------------------------------- +# Check for committer identity. +git config --get user.name > /dev/null && +git config --get user.email > /dev/null || +die 'Identity not configured! Use the commands + + git config --global user.name '\''Your Name'\'' + git config --global user.email '\''you@yourdomain.com'\'' + +to introduce yourself to Git before committing.' + +#----------------------------------------------------------------------------- +# Check content that will be added by this commit. + +if git rev-parse --verify -q HEAD > /dev/null; then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# Disallow non-ascii file names. The printable range starts at the +# space character and ends with tilde. +if test "$(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0')"; then + die 'Non-ascii file names may not be added: +'"$(git diff --cached --name-only --diff-filter=A $against)" +fi + +# Builtin whitespace checks. +bad=$(git diff-index --check --cached $against --) || die "$bad" From 8f96d67507bba38e884aa6d4643d2af0147c2b65 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 1 Apr 2010 15:33:56 -0400 Subject: [PATCH 02/57] Teach Git to ignore sample hooks --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a5a385464 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.sample From 2b3a3cd7a6fc5f217e9ee628ca5e348165be002a Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 20 Apr 2010 11:28:25 -0400 Subject: [PATCH 03/57] pre-commit: Validate user.name and user.email --- pre-commit | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pre-commit b/pre-commit index 442fe9a9f..c10687572 100755 --- a/pre-commit +++ b/pre-commit @@ -12,15 +12,25 @@ die() { #----------------------------------------------------------------------------- # Check for committer identity. -git config --get user.name > /dev/null && -git config --get user.email > /dev/null || -die 'Identity not configured! Use the commands +advice=' +Use the commands git config --global user.name '\''Your Name'\'' git config --global user.email '\''you@yourdomain.com'\'' to introduce yourself to Git before committing.' +# Ensure name and email are available. +git config --get user.name > /dev/null && +git config --get user.email > /dev/null || +die 'Identity not configured!' "$advice" + +# Validate the name and email. +git config --get user.name | grep ' ' > /dev/null || +die 'Set user.name to your Real Name (with a space), not a userid.' "$advice" +git config --get user.email | grep '^[^@]*@[^@]*$' > /dev/null || +die 'Set user.email to an email address (userid@validdomain.com).' "$advice" + #----------------------------------------------------------------------------- # Check content that will be added by this commit. From 5395049e7f82a5f92e4b08ec1a939af239bd2fcd Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 22 Apr 2010 11:42:14 -0400 Subject: [PATCH 04/57] commit-msg: Preserve bad message for user to fix Store a backup copy of the input message. Remove the backup only on success. On failure, tell the user how to use and edit the message without having to enter it from scratch. --- commit-msg | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/commit-msg b/commit-msg index 80f497197..e47ec0ff0 100755 --- a/commit-msg +++ b/commit-msg @@ -2,11 +2,18 @@ # # Copy or link this file as ".git/hooks/pre-commit". +# Prepare a backup message without comments. +commit_msg="$GIT_DIR/COMMIT_MSG" +grep -v '^#' "$1" > "$commit_msg" + die() { echo 'commit-msg hook failure' 1>&2 echo '-----------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 + echo 'To continue editing, run the command + git commit -e -F '"$commit_msg"' +(assuming your working directory is at the top).' 1>&2 exit 1 } @@ -51,7 +58,8 @@ msg_rest() { # Pipe commit message into the state machine. state=first -cat "$1" | grep -v '^#' | +cat "$commit_msg" | while read line; do msg_$state || break -done +done && +rm -f "$commit_msg" From f226aad5a424de464def3eaf4202d55271e1337f Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 23 Apr 2010 15:49:01 -0400 Subject: [PATCH 05/57] pre-commit: Reject leading TABs Our style guidelines do not permit indentation by TABs. --- pre-commit | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pre-commit b/pre-commit index c10687572..858842ea0 100755 --- a/pre-commit +++ b/pre-commit @@ -51,3 +51,27 @@ fi # Builtin whitespace checks. bad=$(git diff-index --check --cached $against --) || die "$bad" + +# Reject leading TABs. +check_tab() { + git diff-index -p --cached $against -- "$1" | + grep '^+ ' > /dev/null && + echo " $1" +} +bad=$(git diff-index --name-only --cached $against -- | +while read file; do + case "$file" in + *.c) check_tab "$file" ;; + *.h) check_tab "$file" ;; + *.cxx) check_tab "$file" ;; + *.txx) check_tab "$file" ;; + *.hxx) check_tab "$file" ;; + *.htm) check_tab "$file" ;; + *.html) check_tab "$file" ;; + *.txt) check_tab "$file" ;; + *.cmake) check_tab "$file" ;; + esac +done) +test -z "$bad" || die 'Leading TABs added in +'"$bad"' +Convert them to spaces (2 per TAB) before commit.' From 0df83855da1e4dcf59bdfe22a939d3f1ddf2a02a Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 27 Apr 2010 13:55:06 -0400 Subject: [PATCH 06/57] pre-commit: Workaround shell syntax limitations Commit "Reject leading TABs" added use of shell syntax of the form $(case "..." in a) ... ;; esac) Some shell implementations fail to recognize that the ')' in the case label is not the end of the '$(' expression. Work around the problem by moving the case block into a separate function outside the '$()' expression. --- pre-commit | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pre-commit b/pre-commit index 858842ea0..14e03ab59 100755 --- a/pre-commit +++ b/pre-commit @@ -58,19 +58,22 @@ check_tab() { grep '^+ ' > /dev/null && echo " $1" } +check_file() { + case "$1" in + *.c) check_tab "$1" ;; + *.h) check_tab "$1" ;; + *.cxx) check_tab "$1" ;; + *.txx) check_tab "$1" ;; + *.hxx) check_tab "$1" ;; + *.htm) check_tab "$1" ;; + *.html) check_tab "$1" ;; + *.txt) check_tab "$1" ;; + *.cmake) check_tab "$1" ;; + esac +} bad=$(git diff-index --name-only --cached $against -- | while read file; do - case "$file" in - *.c) check_tab "$file" ;; - *.h) check_tab "$file" ;; - *.cxx) check_tab "$file" ;; - *.txx) check_tab "$file" ;; - *.hxx) check_tab "$file" ;; - *.htm) check_tab "$file" ;; - *.html) check_tab "$file" ;; - *.txt) check_tab "$file" ;; - *.cmake) check_tab "$file" ;; - esac + check_file "$file" done) test -z "$bad" || die 'Leading TABs added in '"$bad"' From 7cd6238240d440f0a69a0f4e8eecc3b2a23a1973 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 28 Apr 2010 13:35:19 -0400 Subject: [PATCH 07/57] commit-msg: Exclude diff during "git commit -v" Stop processing the commit message text at a "diff --git" line. Such lines occur when the user commits with the "-v" option. We should not check the patch content; it will be stripped by Git anyway. --- commit-msg | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/commit-msg b/commit-msg index e47ec0ff0..3953b37f2 100755 --- a/commit-msg +++ b/commit-msg @@ -2,9 +2,11 @@ # # Copy or link this file as ".git/hooks/pre-commit". -# Prepare a backup message without comments. +# Prepare a copy of the message: +# - strip comment lines +# - stop at "diff --git" (git commit -v) commit_msg="$GIT_DIR/COMMIT_MSG" -grep -v '^#' "$1" > "$commit_msg" +sed -n -e '/^#/d' -e '/^diff --git/q' -e 'p;d' "$1" > "$commit_msg" die() { echo 'commit-msg hook failure' 1>&2 From 7827726e88c2a4cacdda5cd09e6907925cd30f2f Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 20 May 2010 11:42:57 -0400 Subject: [PATCH 08/57] pre-commit: Check file modes Check new files and files whose mode changes to verify that each file mode matches the content of the file. The mode of a file must be executable if and only if the file looks executable (name ends in a Windows executable extension or content starts with a shebang line). --- pre-commit | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pre-commit b/pre-commit index 14e03ab59..00dc8486e 100755 --- a/pre-commit +++ b/pre-commit @@ -49,9 +49,11 @@ if test "$(git diff --cached --name-only --diff-filter=A -z $against | '"$(git diff --cached --name-only --diff-filter=A $against)" fi +#----------------------------------------------------------------------------- # Builtin whitespace checks. bad=$(git diff-index --check --cached $against --) || die "$bad" +#----------------------------------------------------------------------------- # Reject leading TABs. check_tab() { git diff-index -p --cached $against -- "$1" | @@ -78,3 +80,40 @@ done) test -z "$bad" || die 'Leading TABs added in '"$bad"' Convert them to spaces (2 per TAB) before commit.' + +#----------------------------------------------------------------------------- +# Check file modes. +looks_executable() { + case "$1" in + *.bat) return 0 ;; + *.cmd) return 0 ;; + *.exe) return 0 ;; + *.com) return 0 ;; + esac + git cat-file blob "$2" | head -1 | grep "^#!/" > /dev/null +} +mode_not_exe () { + echo "The file '$file' has looks executable but does not have an executable mode." +} +mode_bad_exe () { + echo "The file '$file' has executable mode but does not look executable." +} +mode_non_file () { + echo "The path '$file' has a non-file mode." +} +check_mode() { + case "$dst_mode" in + 100755) looks_executable "$file" "$dst_obj" || mode_bad_exe ;; + 100644) looks_executable "$file" "$dst_obj" && mode_not_exe ;; + 160000) ;; + *) mode_non_file ;; + esac +} +bad=$(git diff-index --cached $against -- | +sed -n '/^:[^:]/ {s/^://;p;}' | +while read src_mode dst_mode src_obj dst_obj status file; do + if test "$src_mode" != "$dst_mode" -a "$dst_mode" != "000000"; then + check_mode + fi +done) +test -z "$bad" || die "$bad" From 92c7517435970a4e1abc37292e03ea18302d523d Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 7 Jun 2010 13:27:19 -0400 Subject: [PATCH 09/57] pre-commit: Rename looks_executable -> mode_looks_exe Keep all mode-related check helpers in the "mode_" namespace. --- pre-commit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pre-commit b/pre-commit index 00dc8486e..79b27a029 100755 --- a/pre-commit +++ b/pre-commit @@ -83,7 +83,7 @@ Convert them to spaces (2 per TAB) before commit.' #----------------------------------------------------------------------------- # Check file modes. -looks_executable() { +mode_looks_exe() { case "$1" in *.bat) return 0 ;; *.cmd) return 0 ;; @@ -103,8 +103,8 @@ mode_non_file () { } check_mode() { case "$dst_mode" in - 100755) looks_executable "$file" "$dst_obj" || mode_bad_exe ;; - 100644) looks_executable "$file" "$dst_obj" && mode_not_exe ;; + 100755) mode_looks_exe "$file" "$dst_obj" || mode_bad_exe ;; + 100644) mode_looks_exe "$file" "$dst_obj" && mode_not_exe ;; 160000) ;; *) mode_non_file ;; esac From a54ae582f7de41e8a0f54dff5b260dab94aaf9ef Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 7 Jun 2010 13:56:33 -0400 Subject: [PATCH 10/57] pre-commit: Check file sizes Check blob and tree object sizes to prevent large objects from entering the repository. The default limit is 1024 KiB, but it can be set with git config hooks.MaxObjectKiB $KiB locally, or disabled by using 0 KiB. --- pre-commit | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pre-commit b/pre-commit index 79b27a029..69b84b933 100755 --- a/pre-commit +++ b/pre-commit @@ -10,6 +10,8 @@ die() { exit 1 } +zero='0000000000000000000000000000000000000000' + #----------------------------------------------------------------------------- # Check for committer identity. advice=' @@ -82,7 +84,7 @@ test -z "$bad" || die 'Leading TABs added in Convert them to spaces (2 per TAB) before commit.' #----------------------------------------------------------------------------- -# Check file modes. +# Check file modes and sizes. mode_looks_exe() { case "$1" in *.bat) return 0 ;; @@ -109,11 +111,27 @@ check_mode() { *) mode_non_file ;; esac } + +size_max_KiB=$(git config hooks.MaxObjectKiB) +test -n "$size_max_KiB" || size_max_KiB=1024 +size_too_large() { + echo "The path '$file' has size $file_KiB KiB, greater than the maximum $size_max_KiB KiB." + echo 'Run "git config hooks.MaxObjectKiB $KiB" to set local limit, 0 to disable.' +} +check_size() { + if test "$size_max_KiB" -gt "0" \ + -a "$dst_obj" != "$zero" -a "$dst_mode" != '160000'; then + file_KiB=$(expr '(' $(git cat-file -s "$dst_obj") + 1023 ')' / 1024) + test "$file_KiB" -le "$size_max_KiB" || size_too_large + fi +} + bad=$(git diff-index --cached $against -- | sed -n '/^:[^:]/ {s/^://;p;}' | while read src_mode dst_mode src_obj dst_obj status file; do if test "$src_mode" != "$dst_mode" -a "$dst_mode" != "000000"; then check_mode + check_size fi done) test -z "$bad" || die "$bad" From 1387c7efa00ce996107885e8d806faf50648e04a Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 7 Jun 2010 14:07:38 -0400 Subject: [PATCH 11/57] pre-commit: Check file size always Fix logic to call check_size for all updated index entries, not just those whose mode changed. --- pre-commit | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pre-commit b/pre-commit index 69b84b933..75c7f829d 100755 --- a/pre-commit +++ b/pre-commit @@ -119,8 +119,7 @@ size_too_large() { echo 'Run "git config hooks.MaxObjectKiB $KiB" to set local limit, 0 to disable.' } check_size() { - if test "$size_max_KiB" -gt "0" \ - -a "$dst_obj" != "$zero" -a "$dst_mode" != '160000'; then + if test "$size_max_KiB" -gt "0" -a "$dst_obj" != "$zero"; then file_KiB=$(expr '(' $(git cat-file -s "$dst_obj") + 1023 ')' / 1024) test "$file_KiB" -le "$size_max_KiB" || size_too_large fi @@ -131,6 +130,8 @@ sed -n '/^:[^:]/ {s/^://;p;}' | while read src_mode dst_mode src_obj dst_obj status file; do if test "$src_mode" != "$dst_mode" -a "$dst_mode" != "000000"; then check_mode + fi + if test "$dst_mode" != "160000" -a "$dst_mode" != '000000'; then check_size fi done) From 6c7e3f42ba6eb3e415a93b4e6d0494d66474b213 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 16 Jun 2010 15:53:39 -0400 Subject: [PATCH 12/57] commit-msg: Reject leading or trailing space on first line --- commit-msg | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commit-msg b/commit-msg index 3953b37f2..e3b51de51 100755 --- a/commit-msg +++ b/commit-msg @@ -38,6 +38,10 @@ msg_first() { ------------------------------------------------------------------------------ '"$line"' ------------------------------------------------------------------------------ +' + elif echo "$line" | grep "^[ ]\|[ ]$" >/dev/null 2>&1; then + die 'The first line may not have leading or trailing space: +['"$line"'] ' else # first line okay @@ -61,7 +65,7 @@ msg_rest() { # Pipe commit message into the state machine. state=first cat "$commit_msg" | -while read line; do +while IFS='' read line; do msg_$state || break done && rm -f "$commit_msg" From 8653d286060b71b74b5a89ec7075617a76f81c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Tue, 3 Aug 2010 11:00:14 -0400 Subject: [PATCH 13/57] Reference bash explicitly in shebang lines On Solaris, where /bin/sh is actually sh, not bash, some expressions like $() are not supported. Git's own scripts on this machine are configured to use "#!/bin/bash". Change our shebang line to #!/usr/bin/env bash which should work almost everywhere. --- commit-msg | 2 +- pre-commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commit-msg b/commit-msg index e3b51de51..715837c4a 100755 --- a/commit-msg +++ b/commit-msg @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # # Copy or link this file as ".git/hooks/pre-commit". diff --git a/pre-commit b/pre-commit index 75c7f829d..928938200 100755 --- a/pre-commit +++ b/pre-commit @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # # Copy or link this file as ".git/hooks/pre-commit". From 48e72dd1cd9ebefae0006cba0fafd6a09928bc8c Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 10 Aug 2010 17:30:06 -0400 Subject: [PATCH 14/57] pre-commit: Check submodules staged with other changes Since Git does not automatically update submodule checkouts when checking out a new version in the work tree, it is common to have locally modified submodule references. Therefore it is easy to stage such modifications with other changes by accident, especially with commands like "git add -u" or "git commit -a". The result is almost always wrong if the submodule change is not intended. Prevent such mistakes by requiring an extra step to commit submodule link updates with other changes. When this case is detected, print a message describing the situation and provide cut-and-paste instructions to proceed. --- pre-commit | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/pre-commit b/pre-commit index 928938200..2399f53c9 100755 --- a/pre-commit +++ b/pre-commit @@ -125,8 +125,61 @@ check_size() { fi } -bad=$(git diff-index --cached $against -- | -sed -n '/^:[^:]/ {s/^://;p;}' | +short_commit() { + git rev-parse --short "$1" 2>/dev/null || echo "$1" +} + +lookup_config_module_update() { + # Format is "aaaaaa..bbbbbb" for update aaaaaa => bbbbbb. + # Convert to regex "^aaaaaa[a-z0-9]* bbbbbb[a-z0-9]*$". + sha1ex='[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]*' + regex='^\('"$sha1ex"'\)\.\.\('"$sha1ex"'\)$' + git config "hooks.$1.update" | + sed -n "/$regex/ {s/$regex/"'^\1[a-z0-9]* \2[a-z0-9]*$/;p;}' | + grep '.' # Return false if result is empty. +} + +check_module() { + # Allow module-only commits without extra work. + test -z "$diffs_normal" && return + + # Check if module update is allowed with other changes. + allow=$(lookup_config_module_update "$file") || allow='none' + if echo "$src_obj $dst_obj" | grep "$allow" > /dev/null; then + return + fi + src_short=$(short_commit "$src_obj") + dst_short=$(short_commit "$dst_obj") + echo 'A submodule link is staged for commit (among other changes): + + "'"$file"'" '"$src_short => $dst_short"' + +This may occur by accident so we require an extra step to commit. +If you intend to include this change in your commit, run + + git config "hooks.'"$file"'.update" '"$src_short..$dst_short"' + +to explicitly allow the change and try the commit again. Otherwise run + + git reset HEAD -- "'"$file"'" + +to unstage the change. Furthermore, if you did not intend to modify +the submodule at all, also run + + git submodule update -- "'"$file"'" + +to checkout the current version of the submodule in your work tree. +Test your changes again to see if they still work with the module. +Finally, try the commit again. +' +} + +diffs=$(git diff-index --cached $against -- | + sed -n '/^:[^:]/ {s/^://;p;}') +diffs_normal=$(echo "$diffs" | grep -v '^...... 160000') +diffs_module=$(echo "$diffs" | grep '^...... 160000') +bad=$( +test -n "$diffs_normal" && echo "$diffs_normal" | while read src_mode dst_mode src_obj dst_obj status file; do if test "$src_mode" != "$dst_mode" -a "$dst_mode" != "000000"; then check_mode @@ -134,5 +187,10 @@ while read src_mode dst_mode src_obj dst_obj status file; do if test "$dst_mode" != "160000" -a "$dst_mode" != '000000'; then check_size fi -done) +done +test -n "$diffs_module" && echo "$diffs_module" | +while read src_mode dst_mode src_obj dst_obj status file; do + check_module +done +) test -z "$bad" || die "$bad" From 0c6925a7fc869b32ceb07d043de79f3b4247d0ad Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 25 Aug 2010 11:50:53 -0400 Subject: [PATCH 15/57] pre-commit: Allow .gitattributes to limit file size Check for a 'hooks.MaxObjectKiB' attribute to set the limit of specific files or patterns. --- pre-commit | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/pre-commit b/pre-commit index 2399f53c9..fafc3e2cc 100755 --- a/pre-commit +++ b/pre-commit @@ -114,14 +114,49 @@ check_mode() { size_max_KiB=$(git config hooks.MaxObjectKiB) test -n "$size_max_KiB" || size_max_KiB=1024 +size_too_large_once="" +size_too_large_once() { + test -z "$size_too_large_once" || return ; size_too_large_once=done + echo 'At least one file is staged for commit with size larger than its limit. +We prefer to keep large files out of the main source tree, especially +binary files that do not compress well. This hook disallows large files +by default but can be configured. A limit for specific files or patterns +may be set in ".gitattributes" with the "hooks.MaxObjectKiB" attribute. +For example, the line + + *.c hooks.MaxObjectKiB=2048 + +sets a limit of 2048 KiB for C source files. See "git help attributes" +for details on the .gitattributes format. If no attribute has been set +for a given file then its size is limited by the local default. Run + + git config hooks.MaxObjectKiB $KiB + +to set the local default limit (0 to disable). +' +} size_too_large() { - echo "The path '$file' has size $file_KiB KiB, greater than the maximum $size_max_KiB KiB." - echo 'Run "git config hooks.MaxObjectKiB $KiB" to set local limit, 0 to disable.' + size_too_large_once + echo "The path '$file' has size $file_KiB KiB, greater than allowed $max_KiB KiB." +} +size_validate_max_KiB() { + test "$max_KiB" -ge "0" 2>/dev/null && return 0 + echo "The path '$file' has invalid attribute \"hooks-MaxObjectKiB=$max_KiB\"." + return 1 } check_size() { - if test "$size_max_KiB" -gt "0" -a "$dst_obj" != "$zero"; then + test "$dst_obj" != "$zero" || return + max_KiB=$(git check-attr hooks.MaxObjectKiB -- "$file" | + sed 's/^[^:]*: hooks.MaxObjectKiB: //') + case "$max_KiB" in + 'unset') return ;; # No maximum for this object. + 'set') max_KiB="$size_max_KiB" ;; # Use local default. + 'unspecified') max_KiB="$size_max_KiB" ;; # Use local default. + *) size_validate_max_KiB || return ;; + esac + if test "$max_KiB" -gt "0"; then file_KiB=$(expr '(' $(git cat-file -s "$dst_obj") + 1023 ')' / 1024) - test "$file_KiB" -le "$size_max_KiB" || size_too_large + test "$file_KiB" -le "$max_KiB" || size_too_large fi } From aad245bee7d3570056922e1fa8c5e183a3b943f9 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Aug 2010 16:41:32 -0400 Subject: [PATCH 16/57] Remove out-dated instructions --- commit-msg | 2 -- pre-commit | 2 -- 2 files changed, 4 deletions(-) diff --git a/commit-msg b/commit-msg index 715837c4a..c2d7de190 100755 --- a/commit-msg +++ b/commit-msg @@ -1,6 +1,4 @@ #!/usr/bin/env bash -# -# Copy or link this file as ".git/hooks/pre-commit". # Prepare a copy of the message: # - strip comment lines diff --git a/pre-commit b/pre-commit index fafc3e2cc..9264eacbc 100755 --- a/pre-commit +++ b/pre-commit @@ -1,6 +1,4 @@ #!/usr/bin/env bash -# -# Copy or link this file as ".git/hooks/pre-commit". die() { echo 'pre-commit hook failure' 1>&2 From 19ee713db129db2d2704d1b7ee4bde24e8ce91ad Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Aug 2010 16:43:57 -0400 Subject: [PATCH 17/57] Add Apache 2.0 LICENSE and copyright NOTICE --- LICENSE | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++ NOTICE | 5 ++ commit-msg | 15 ++++ pre-commit | 15 ++++ 4 files changed, 237 insertions(+) create mode 100644 LICENSE create mode 100644 NOTICE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..6371f079a --- /dev/null +++ b/NOTICE @@ -0,0 +1,5 @@ +Kitware Local Git Hooks +Copyright 2010 Kitware, Inc. + +This product includes software developed at Kitware, Inc. +(http://www.kitware.com/). diff --git a/commit-msg b/commit-msg index c2d7de190..9b881b540 100755 --- a/commit-msg +++ b/commit-msg @@ -1,4 +1,19 @@ #!/usr/bin/env bash +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= # Prepare a copy of the message: # - strip comment lines diff --git a/pre-commit b/pre-commit index 9264eacbc..76a308199 100755 --- a/pre-commit +++ b/pre-commit @@ -1,4 +1,19 @@ #!/usr/bin/env bash +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= die() { echo 'pre-commit hook failure' 1>&2 From e368fa1290fb1eeb6a0a343f0f73356f852e93f7 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 30 Aug 2010 10:02:42 -0400 Subject: [PATCH 18/57] pre-commit: Approximate Git 1.7.2 tab-in-indent check Check for leading TABs in files marked with the whitespace=tab-in-indent attribute instead of hard-coding a list of file extensions. --- pre-commit | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/pre-commit b/pre-commit index 76a308199..afaba6cc7 100755 --- a/pre-commit +++ b/pre-commit @@ -68,33 +68,30 @@ fi # Builtin whitespace checks. bad=$(git diff-index --check --cached $against --) || die "$bad" -#----------------------------------------------------------------------------- -# Reject leading TABs. +# Approximate whitespace=tab-in-indent check with Git < 1.7.2. check_tab() { - git diff-index -p --cached $against -- "$1" | - grep '^+ ' > /dev/null && - echo " $1" + lines=$(git diff-index -p --cached $against -- "$1" | + grep '^+ ') && + echo "$lines" | + while read line; do + echo "$1: tab in indent." && + echo "$line" + done } -check_file() { - case "$1" in - *.c) check_tab "$1" ;; - *.h) check_tab "$1" ;; - *.cxx) check_tab "$1" ;; - *.txx) check_tab "$1" ;; - *.hxx) check_tab "$1" ;; - *.htm) check_tab "$1" ;; - *.html) check_tab "$1" ;; - *.txt) check_tab "$1" ;; - *.cmake) check_tab "$1" ;; +approx_tab_in_indent() { + ws=$(git check-attr whitespace -- "$file" | + sed 's/^[^:]*: whitespace: //') + case ",$ws," in + *,tab-in-indent,*) check_tab "$1" ;; esac } -bad=$(git diff-index --name-only --cached $against -- | -while read file; do - check_file "$file" -done) -test -z "$bad" || die 'Leading TABs added in -'"$bad"' -Convert them to spaces (2 per TAB) before commit.' +if git --version | grep " \(1\.[0-6]\|1\.7\.[01]\)" >/dev/null; then + bad=$(git diff-index --name-only --cached $against -- | + while read file; do + approx_tab_in_indent "$file" + done) + test -z "$bad" || die "$bad" +fi #----------------------------------------------------------------------------- # Check file modes and sizes. From 4c5c09042a5aa6139307c9b2e4fc5b0059118866 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Aug 2010 16:47:00 -0400 Subject: [PATCH 19/57] Add commit-msg hook from Gerrit Code Review Currently the hook is unused but later we will invoke it from the main commit-msg hook. --- NOTICE | 3 ++ gerrit/commit-msg | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100755 gerrit/commit-msg diff --git a/NOTICE b/NOTICE index 6371f079a..cd8dcca71 100644 --- a/NOTICE +++ b/NOTICE @@ -3,3 +3,6 @@ Copyright 2010 Kitware, Inc. This product includes software developed at Kitware, Inc. (http://www.kitware.com/). + +Portions of this software were developed as part of Gerrit Code Review +(http://code.google.com/p/gerrit/) by The Android Open Source Project. diff --git a/gerrit/commit-msg b/gerrit/commit-msg new file mode 100755 index 000000000..0c50f69fd --- /dev/null +++ b/gerrit/commit-msg @@ -0,0 +1,104 @@ +#!/bin/sh +# From Gerrit Code Review 2.1.5 +# +# Part of Gerrit Code Review (http://code.google.com/p/gerrit/) +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +CHANGE_ID_AFTER="Bug|Issue" +MSG="$1" + +# Check for, and add if missing, a unique Change-Id +# +add_ChangeId() { + clean_message=$(sed -e ' + /^diff --git a\/.*/{ + s/// + q + } + /^Signed-off-by:/d + /^#/d + ' "$MSG" | git stripspace) + if test -z "$clean_message" + then + return + fi + + if grep -i '^Change-Id:' "$MSG" >/dev/null + then + return + fi + + id=$(_gen_ChangeId) + perl -e ' + $MSG = shift; + $id = shift; + $CHANGE_ID_AFTER = shift; + + undef $/; + open(I, $MSG); $_ = ; close I; + s|^diff --git a/.*||ms; + s|^#.*$||mg; + exit unless $_; + + @message = split /\n/; + $haveFooter = 0; + $startFooter = @message; + for($line = @message - 1; $line >= 0; $line--) { + $_ = $message[$line]; + + if (/^[a-zA-Z0-9-]+:/ && !m,^[a-z0-9-]+://,) { + $haveFooter++; + next; + } + next if /^[ []/; + $startFooter = $line if ($haveFooter && /^\r?$/); + last; + } + + @footer = @message[$startFooter+1..@message]; + @message = @message[0..$startFooter]; + push(@footer, "") unless @footer; + + for ($line = 0; $line < @footer; $line++) { + $_ = $footer[$line]; + next if /^($CHANGE_ID_AFTER):/i; + last; + } + splice(@footer, $line, 0, "Change-Id: I$id"); + + $_ = join("\n", @message, @footer); + open(O, ">$MSG"); print O; close O; + ' "$MSG" "$id" "$CHANGE_ID_AFTER" +} +_gen_ChangeIdInput() { + echo "tree $(git write-tree)" + if parent=$(git rev-parse HEAD^0 2>/dev/null) + then + echo "parent $parent" + fi + echo "author $(git var GIT_AUTHOR_IDENT)" + echo "committer $(git var GIT_COMMITTER_IDENT)" + echo + printf '%s' "$clean_message" +} +_gen_ChangeId() { + _gen_ChangeIdInput | + git hash-object -t commit --stdin +} + + +add_ChangeId From 5450a1ecb6c95dfbc87ca8f0bf76d9dfe54a7130 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Aug 2010 16:52:02 -0400 Subject: [PATCH 20/57] gerrit/commit-msg: Reference bash in shebang line This script uses bashisms like $() so we need a real bash. --- gerrit/commit-msg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gerrit/commit-msg b/gerrit/commit-msg index 0c50f69fd..336a738c5 100755 --- a/gerrit/commit-msg +++ b/gerrit/commit-msg @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # From Gerrit Code Review 2.1.5 # # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) From 592711ab847bb1c18b10d694d6e5929d1754cece Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 30 Aug 2010 13:00:11 -0400 Subject: [PATCH 21/57] gerrit/commit-msg: Report added Change-Id --- gerrit/commit-msg | 1 + 1 file changed, 1 insertion(+) diff --git a/gerrit/commit-msg b/gerrit/commit-msg index 336a738c5..d843020f9 100755 --- a/gerrit/commit-msg +++ b/gerrit/commit-msg @@ -83,6 +83,7 @@ add_ChangeId() { $_ = join("\n", @message, @footer); open(O, ">$MSG"); print O; close O; ' "$MSG" "$id" "$CHANGE_ID_AFTER" + echo "Added Change-Id: I$id" } _gen_ChangeIdInput() { echo "tree $(git write-tree)" From 7cd9751649d0bb93bdc7b88f96b840877c202b11 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 30 Aug 2010 13:26:19 -0400 Subject: [PATCH 22/57] commit-msg: Simplify newlines in die() calls --- commit-msg | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/commit-msg b/commit-msg index 9b881b540..19d2d4cbe 100755 --- a/commit-msg +++ b/commit-msg @@ -26,7 +26,8 @@ die() { echo '-----------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 - echo 'To continue editing, run the command + echo ' +To continue editing, run the command git commit -e -F '"$commit_msg"' (assuming your working directory is at the top).' 1>&2 exit 1 @@ -44,18 +45,15 @@ msg_first() { die 'The first line must be at least 8 characters: -------- '"$line"' --------- -' +--------' elif test $len -gt 78; then die 'The first line may be at most 78 characters: ------------------------------------------------------------------------------ '"$line"' ------------------------------------------------------------------------------- -' +------------------------------------------------------------------------------' elif echo "$line" | grep "^[ ]\|[ ]$" >/dev/null 2>&1; then die 'The first line may not have leading or trailing space: -['"$line"'] -' +['"$line"']' else # first line okay state=second From cb038d0b57fa6d131089066b71a30ad284df3c3c Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 30 Aug 2010 13:27:14 -0400 Subject: [PATCH 23/57] commit-msg: Invoke gerrit/commit-msg if hooks.GerritId=true Add a Gerrit Change-Id header if hooks.GerritId is true. Print help if the option is not set and a remote URL looks like Gerrit. --- commit-msg | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/commit-msg b/commit-msg index 19d2d4cbe..37fa32be5 100755 --- a/commit-msg +++ b/commit-msg @@ -79,4 +79,44 @@ cat "$commit_msg" | while IFS='' read line; do msg_$state || break done && -rm -f "$commit_msg" +rm -f "$commit_msg" || exit 1 + +#----------------------------------------------------------------------------- +# Optionally run Gerrit's commit-msg hook to add a Change-Id line. + +gerrit_advice() { + gerrits=$(git config -l |grep '^remote\.[^=]\+url=.*review.*$') + test "x$gerrits" != "x" || return + + echo 'Some config values look like Gerrit Code Review URLs:' + echo '' + echo "$gerrits" | sed 's/^/ /' + echo ' +This hook can automatically add a "Change-Id" footer to commit messages +to make interaction with Gerrit easier. To enable this feature, run + + git config hooks.GerritId true + +Then run "git commit --amend" to fix this commit. Otherwise, run + + git config hooks.GerritId false + +to disable the feature and this message.' +} + +gerrit_error() { + die 'non-bool config value hooks.GerritId = '"$hooks_GerritId" +} + +gerrit_hook() { + "$GIT_DIR/hooks/gerrit/commit-msg" "$@" || + die 'gerrit/commit-msg failed' +} + +hooks_GerritId=$(git config --get hooks.GerritId) +case "$hooks_GerritId" in + 'true') gerrit_hook "$@" ;; + 'false') ;; + '') gerrit_advice ;; + *) gerrit_error ;; +esac From 8edae204704e83968f31ed4f45c579adf646dc8b Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 31 Aug 2010 15:21:15 -0400 Subject: [PATCH 24/57] commit-msg: Fix silent failure when no remote looks like Gerrit --- commit-msg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commit-msg b/commit-msg index 37fa32be5..60ddeee1a 100755 --- a/commit-msg +++ b/commit-msg @@ -86,7 +86,7 @@ rm -f "$commit_msg" || exit 1 gerrit_advice() { gerrits=$(git config -l |grep '^remote\.[^=]\+url=.*review.*$') - test "x$gerrits" != "x" || return + test "x$gerrits" != "x" || return 0 echo 'Some config values look like Gerrit Code Review URLs:' echo '' From f83a32feb68c5b6ee2e98f54f016daed86a7fd8f Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 14 Sep 2010 14:59:45 -0400 Subject: [PATCH 25/57] commit-msg: Allow long merge commit subject lines Merge commits can have long subject lines because they name other branches. Accept such messages as a special case for merge commits. --- commit-msg | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/commit-msg b/commit-msg index 60ddeee1a..92a95cd02 100755 --- a/commit-msg +++ b/commit-msg @@ -36,6 +36,11 @@ To continue editing, run the command #----------------------------------------------------------------------------- # Check the commit message layout with a simple state machine. +msg_is_merge() { + test -f "$GIT_DIR/MERGE_HEAD" && + echo "$line" | grep "^Merge " >/dev/null 2>&1 +} + msg_first() { len=$(echo -n "$line" | wc -c) if test $len -eq 0; then @@ -46,7 +51,7 @@ msg_first() { -------- '"$line"' --------' - elif test $len -gt 78; then + elif test $len -gt 78 && ! msg_is_merge; then die 'The first line may be at most 78 characters: ------------------------------------------------------------------------------ '"$line"' From 19f86ec15cc36f3edd30210724b0d11b080d9961 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 15 Sep 2010 09:22:03 -0400 Subject: [PATCH 26/57] pre-commit: Allow submodule check to be disabled If hooks.submodule is 'false' disable all checks. If an individual hooks..update is 'true' then accept any update for that module. Leave these options out of the hints printed. Developers that know what they are doing will be able to find them by reading the hook source. --- pre-commit | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pre-commit b/pre-commit index afaba6cc7..bee9c9027 100755 --- a/pre-commit +++ b/pre-commit @@ -175,16 +175,24 @@ short_commit() { } lookup_config_module_update() { + update=$(git config "hooks.$1.update") + + # Special-case "true" to accept any update. + test "{$update}" = "{true}" && echo '.' && return + # Format is "aaaaaa..bbbbbb" for update aaaaaa => bbbbbb. # Convert to regex "^aaaaaa[a-z0-9]* bbbbbb[a-z0-9]*$". sha1ex='[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]*' regex='^\('"$sha1ex"'\)\.\.\('"$sha1ex"'\)$' - git config "hooks.$1.update" | + echo "$update" | sed -n "/$regex/ {s/$regex/"'^\1[a-z0-9]* \2[a-z0-9]*$/;p;}' | grep '.' # Return false if result is empty. } check_module() { + enabled=$(git config --get --bool hooks.submodule) || enabled=true + test "$enabled" = "false" && return + # Allow module-only commits without extra work. test -z "$diffs_normal" && return From d5bb076453719934cd9eda65e72d337acb4fe5bd Mon Sep 17 00:00:00 2001 From: "Matt McCormick (thewtex)" Date: Thu, 9 Sep 2010 11:45:14 -0500 Subject: [PATCH 27/57] pre-commit: Apply uncrustify and KWStyle check for modified files. uncrustify (uncrustify.sourceforge.net) is applied to changed files prior to commit. This feature is off by default. To enable this behavior, set git config hooks.uncrustify true By default, the behavior of git-mergetool is used to review the changes uncrustify makes before they are added to the commit. For more information on this behavior, see git help mergetool KWStyle is run on the changed C++ files and the commit is aborted if the files do not pass the test. A file similar to the original is saved with a '*.kws' extension so that line numbers referenced in the error message can be examined. The test is off by default. To enable this behavoir, set git config hooks.KWStyle true Project specific uncrustify and KWStyle configuration files are set with 'git config'. For example, git config hooks.uncrustify.conf path/to/uncrustify.conf git config hooks.KWStyle.conf path/to/KWStyle.conf git config hooks.KWStyle.overwriteRulesConf path/to/overwrite.conf # optional If the appropriate values have not been set, die() is called. An optional KWStyle overwrite rules file can also been configured. The files on which to run the style checks must also be identified in the repository's '.gitattributes'. For example, *.h hooks.style *.cpp hooks.style Or, to only enable a subset of style hooks, *.h hooks.style=KWStyle *.cpp hooks.style=KWStyle,uncrustify Change-Id: Ia6b2d4136af3002eb0ec5d36f03c50df928917f4 --- pre-commit | 6 ++ pre-commit-style | 252 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 pre-commit-style diff --git a/pre-commit b/pre-commit index bee9c9027..15a0151b0 100755 --- a/pre-commit +++ b/pre-commit @@ -247,3 +247,9 @@ while read src_mode dst_mode src_obj dst_obj status file; do done ) test -z "$bad" || die "$bad" + +#----------------------------------------------------------------------------- +# Style hooks. +. "$GIT_DIR/hooks/pre-commit-style" + +# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/pre-commit-style b/pre-commit-style new file mode 100644 index 000000000..15af958fd --- /dev/null +++ b/pre-commit-style @@ -0,0 +1,252 @@ +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +# Run uncrustify and KWStyle pre-commit hooks. +# +# 'git config' is used to enable the hooks and set their configuration files. +# The repository .gitattributes must also enable the hooks on the targeted +# files. + +do_KWStyle=$(git config --bool hooks.KWStyle) || do_KWStyle=false + +do_uncrustify=$(git config --bool hooks.uncrustify) || do_uncrustify=false + +#----------------------------------------------------------------------------- +# Check if we want to run the style on a given file. Uses git attributes. If +# the hook.style attribute is set, then all styles are executed. If the +# hook.style attribute is set to a value, only the values given are executed. +# Also, do not run the style check if there are unstaged changes in the file. +# The first positional parameter is the file to check. +# The second positional parameter is the style to check. +# Returns 0 for execute, 1 for don't execute. +run_style_on_file() { + # Do not run on submodule changes. + if git diff-index --cached $against -- "$1" | grep -q '^:...... 160000'; then + return 1 + fi + if ! git diff-files --quiet -- "$1"; then + # A way to always allow skipping. + skip_unstaged=$(git config --bool hooks.styleSkipUnstaged) || + skip_unstaged=false + file_sha=$(git diff-index --cached --abbrev=7 $against -- "$1" | \ + awk '{print substr($3,1,9) substr($4,1,7)}') + if file_skip_unstaged=$(git config "hooks.$1.styleSkipUnstaged"); then + if test ",$file_skip_unstaged," = ",$file_sha," -o \ + ",$file_skip_unstaged," = ",true,"; then + skip_unstaged=true + fi + fi + + if $skip_unstaged; then + echo "The file '$1' contains unstaged stages. Skipping style \ +check '$2'." + else + die "Style check '$2' cannot run on '$1' with unstaged stages. + +Allow skipping the style check for this commit with + + git config \"hooks.$1.styleSkipUnstaged\" $file_sha" + fi + return 1 + fi + style=$(git check-attr hooks.style -- "$1" | + sed 's/^[^:]*: hooks.style: //') + case "$style" in + 'unset') return 1 ;; + 'set') return 0 ;; + 'unspecified') return 1 ;; + *) echo ",$style," | grep -iq ",$2," && return 0 ;; + esac + return 1 +} + +#----------------------------------------------------------------------------- +# KWStyle. +check_for_KWStyle() { + KWStyle_path=$(git config hooks.KWStyle.path) || + KWStyle_path=$(which KWStyle) || + die "KWStyle executable was not found. + +Please install KWStyle or set the executable location with + + git config hooks.KWStyle.path /path/to/KWStyle + +See http://public.kitware.com/KWStyle/" + + KWStyle_conf=$(git config hooks.KWStyle.conf) + if ! test -f "$KWStyle_conf"; then + die "The file '$KWStyle_conf' does not exist. + +Please run + + git config hooks.KWStyle.conf path/to/KWStyle.conf.xml" + fi + KWStyle_overWriteRulesConf=$(git config hooks.KWStyle.overwriteRulesConf) + if test $? -eq 0 && ! test -f "$KWStyle_overWriteRulesConf"; then + die "The hooks.KWStyle.overwriteRulesConf file '$KWStyle_overWriteRulesConf' does not exist." + fi +} + +run_KWStyle_on_file() { + if test -z "$KWStyle_overWriteRulesConf"; then + "$KWStyle_path" -v -xml "$KWStyle_conf" "$1" + else + "$KWStyle_path" -v -xml "$KWStyle_conf" -o "$KWStyle_overWriteRulesConf" "$1" + fi + + if test $? -ne 0; then + cp -- "$1"{,.kws} + die "KWStyle check failed. + +Line numbers in the errors shown refer to the file: +${1}.kws" + fi +} + +run_KWStyle() { + git diff-index --cached --diff-filter=ACMR --name-only $against -- | + while read f; do + if run_style_on_file "$f" KWStyle; then + run_KWStyle_on_file "$f" + fi + done +} + +#----------------------------------------------------------------------------- +# uncrustify. +check_for_uncrustify() { + uncrustify_path=$(git config hooks.uncrustify.path) || + uncrustify_path=$(which uncrustify) || + die "uncrustify executable was not found. + +Please install uncrustify or set the executable location with + + git config hooks.uncrustify.path /path/to/uncrustify + + See http://uncrustify.sourceforge.net/" + + uncrustify_conf=$(git config hooks.uncrustify.conf) + if ! test -f "$uncrustify_conf"; then + die "The file '$uncrustify_conf' does not exist. + +Please run + + git config hooks.uncrustify.conf path/to/uncrustify.conf" + fi +} + +run_uncrustify_on_file() { + MERGED="$1" + if run_style_on_file "$MERGED" uncrustify; then + ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" + BACKUP="./$MERGED.BACKUP.$ext" + LOCAL="./$MERGED.STAGED.$ext" + REMOTE="./$MERGED.UNCRUSTIFY.$ext" + NEW_MERGED="./$MERGED.NEW.$ext" + OLD_MERGED="$MERGED" + + mv -- "$MERGED" "$BACKUP" + # We temporarily change MERGED because the file might already be open, and + # the text editor may complain. + MERGED="$NEW_MERGED" + cp -- "$BACKUP" "$MERGED" + cp -- "$BACKUP" "$LOCAL" + + if ! "$uncrustify_path" -c "$uncrustify_conf" -f "$LOCAL" \ + -o "$REMOTE" 2> /dev/null; then + mv -- "$BACKUP" "$OLD_MERGED" + + if test "$merge_keep_temporaries" = "false"; then + rm -f -- "$LOCAL" "$REMOTE" "$BACKUP" + fi + + die "error when running uncrustify on $OLD_MERGED" + fi + + if test $(git hash-object -- "$LOCAL") != $(git hash-object -- "$REMOTE") && + ! run_merge_tool "$merge_tool" "false" + +For more information, see + + git help mergetool" + merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)" + merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" + git diff-index --cached --diff-filter=ACMR --name-only $against -- | + while read MERGED; do + run_uncrustify_on_file "$MERGED" + done # end for changed files +} + +# Do not run during merge commits for now. +if test -f "$GIT_DIR/MERGE_HEAD"; then + : +elif $do_uncrustify; then + # We use git-mergetool settings to review the uncrustify changes. + TOOL_MODE=merge + . "$(git --exec-path)/git-mergetool--lib" + # Redefine check_unchanged because we do not need to check if the merge was + # successful. + check_unchanged() { + status=0 + } + check_for_uncrustify + run_uncrustify +# do_uncrustify will run KWStyle on the files incrementally so excessive +# uncrustify merges do not have to occur. +elif $do_KWStyle; then + check_for_KWStyle + run_KWStyle +fi + +# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : From b32ecce759213ca7e8214d0ad014660f9c834bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Wed, 27 Oct 2010 17:02:19 +0200 Subject: [PATCH 28/57] make the check for KWStyle executable non blocking At this time, a contributor won't be able to commit a change if KWStyle is not installed on the computer. KWStyle is not a very common program, and so is likely to have to be installed by hand. This increase the work needed to be able to contribute to ITK. With this change a warning is displayed if KWStyle is not installed but the commit is not blocked. Change-Id: I0719ee5ac6e048120504bbdc4dc022043c0f0ded --- pre-commit-style | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/pre-commit-style b/pre-commit-style index 15af958fd..7fc51c87b 100644 --- a/pre-commit-style +++ b/pre-commit-style @@ -77,15 +77,20 @@ Allow skipping the style check for this commit with # KWStyle. check_for_KWStyle() { KWStyle_path=$(git config hooks.KWStyle.path) || - KWStyle_path=$(which KWStyle) || - die "KWStyle executable was not found. + KWStyle_path=$(which KWStyle) + if [ $? != 0 ] ; then + echo "KWStyle executable was not found. + + No style verification will be performed with KWStyle! Please install KWStyle or set the executable location with git config hooks.KWStyle.path /path/to/KWStyle -See http://public.kitware.com/KWStyle/" - +See http://public.kitware.com/KWStyle/ +" >&2 + return 1 + fi KWStyle_conf=$(git config hooks.KWStyle.conf) if ! test -f "$KWStyle_conf"; then die "The file '$KWStyle_conf' does not exist. @@ -201,7 +206,10 @@ run_uncrustify_on_file() { fi # end if run uncrustify on file - if $do_KWStyle && run_style_on_file "$MERGED" KWStyle; then + if $do_KWStyle && + $have_KWStyle && + run_style_on_file "$MERGED" KWStyle + then run_KWStyle_on_file "$MERGED" else return 0 @@ -210,6 +218,11 @@ run_uncrustify_on_file() { run_uncrustify() { $do_KWStyle && check_for_KWStyle + if test $?; then + have_KWStyle=false + else + have_KWStyle=true + fi merge_tool=$(get_merge_tool "$merge_tool") || die "Merge tool not configured. @@ -245,8 +258,9 @@ elif $do_uncrustify; then # do_uncrustify will run KWStyle on the files incrementally so excessive # uncrustify merges do not have to occur. elif $do_KWStyle; then - check_for_KWStyle - run_KWStyle + if check_for_KWStyle; then + run_KWStyle + fi fi # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : From dc31be5bda3817384e0208b38a905ab8a070196a Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 16 Nov 2010 17:40:16 -0500 Subject: [PATCH 29/57] Chain to script configured by "hooks.chain-" Chain the pre-commit, commit-msg, and prepare-commit-msg hooks to locally configured scripts. Interpret relative paths with respect to the working directory where the hooks run (top of work tree). This allows project setup scripts to add project-specific checks for each of these hooks. --- commit-msg | 5 +++++ hooks-chain.bash | 31 +++++++++++++++++++++++++++++++ pre-commit | 5 +++++ prepare-commit-msg | 21 +++++++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 hooks-chain.bash create mode 100755 prepare-commit-msg diff --git a/commit-msg b/commit-msg index 92a95cd02..7856f1ef5 100755 --- a/commit-msg +++ b/commit-msg @@ -125,3 +125,8 @@ case "$hooks_GerritId" in '') gerrit_advice ;; *) gerrit_error ;; esac + +#----------------------------------------------------------------------------- +# Chain to project-specific hook. +. "$GIT_DIR/hooks/hooks-chain.bash" +hooks_chain commit-msg "$@" diff --git a/hooks-chain.bash b/hooks-chain.bash new file mode 100644 index 000000000..63c1f4019 --- /dev/null +++ b/hooks-chain.bash @@ -0,0 +1,31 @@ +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +hooks_chain() { + hook="$1" ; shift + chain=$(git config --get hooks.chain-$hook) || return 0 + case "$chain" in + '/'*) prefix="" ;; + '[A-Za-z]:/'*) prefix="" ;; + '.'*) prefix="" ;; + *) prefix="./" ;; + esac + if test -x "$prefix$chain" ; then + exec "$prefix$chain" "$@" + fi +} + +# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/pre-commit b/pre-commit index 15a0151b0..0f710552c 100755 --- a/pre-commit +++ b/pre-commit @@ -252,4 +252,9 @@ test -z "$bad" || die "$bad" # Style hooks. . "$GIT_DIR/hooks/pre-commit-style" +#----------------------------------------------------------------------------- +# Chain to project-specific hook. +. "$GIT_DIR/hooks/hooks-chain.bash" +hooks_chain pre-commit "$@" + # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/prepare-commit-msg b/prepare-commit-msg new file mode 100755 index 000000000..505b522d5 --- /dev/null +++ b/prepare-commit-msg @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +#----------------------------------------------------------------------------- +# Chain to project-specific hook. +. "$GIT_DIR/hooks/hooks-chain.bash" +hooks_chain prepare-commit-msg "$@" From 125100babc6a0c1f25cb88b7da66c1bbb2a62fdf Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 17 Nov 2010 17:54:50 -0500 Subject: [PATCH 30/57] commit-msg: Allow long first lines starting in "Revert " Revert commits always have longer first lines than the commit they revert. Do not reject those that happen to go over the threshold. It is much simpler if the "git revert" command creates the commit without error. --- commit-msg | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commit-msg b/commit-msg index 7856f1ef5..5d18c5ae3 100755 --- a/commit-msg +++ b/commit-msg @@ -41,6 +41,10 @@ msg_is_merge() { echo "$line" | grep "^Merge " >/dev/null 2>&1 } +msg_is_revert() { + echo "$line" | grep "^Revert " >/dev/null 2>&1 +} + msg_first() { len=$(echo -n "$line" | wc -c) if test $len -eq 0; then @@ -51,7 +55,7 @@ msg_first() { -------- '"$line"' --------' - elif test $len -gt 78 && ! msg_is_merge; then + elif test $len -gt 78 && ! msg_is_merge && ! msg_is_revert; then die 'The first line may be at most 78 characters: ------------------------------------------------------------------------------ '"$line"' From f560224524868b4b8ed39fb7e68e3f4474a658ef Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 30 Dec 2010 09:08:10 -0500 Subject: [PATCH 31/57] pre-commit: Generalize custom whitespace check Lookup the whitespace attribute for each modified file and dispatch custom checks as necessary. Currently the only custom check is the approximate tab-in-indent check for Git < 1.7.2. --- pre-commit | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/pre-commit b/pre-commit index 0f710552c..8ecbaab5c 100755 --- a/pre-commit +++ b/pre-commit @@ -69,6 +69,8 @@ fi bad=$(git diff-index --check --cached $against --) || die "$bad" # Approximate whitespace=tab-in-indent check with Git < 1.7.2. +git --version | grep -q " \(1\.[0-6]\|1\.7\.[01]\)" && +approx_tab_in_indent=true || approx_tab_in_indent=false check_tab() { lines=$(git diff-index -p --cached $against -- "$1" | grep '^+ ') && @@ -78,20 +80,22 @@ check_tab() { echo "$line" done } -approx_tab_in_indent() { + +# Custom whitespace checks. +check_whitespace() { ws=$(git check-attr whitespace -- "$file" | sed 's/^[^:]*: whitespace: //') - case ",$ws," in - *,tab-in-indent,*) check_tab "$1" ;; - esac + if $approx_tab_in_indent; then + case ",$ws," in + *,tab-in-indent,*) check_tab "$1" ;; + esac + fi } -if git --version | grep " \(1\.[0-6]\|1\.7\.[01]\)" >/dev/null; then - bad=$(git diff-index --name-only --cached $against -- | - while read file; do - approx_tab_in_indent "$file" - done) - test -z "$bad" || die "$bad" -fi +bad=$(git diff-index --name-only --cached $against -- | +while read file; do + check_whitespace "$file" +done) +test -z "$bad" || die "$bad" #----------------------------------------------------------------------------- # Check file modes and sizes. From c3d0b77f8db7073b32f06ef958a7f92867991e73 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 30 Dec 2010 09:11:38 -0500 Subject: [PATCH 32/57] pre-commit: Add whitespace check "no-lf-at-eol" --- pre-commit | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pre-commit b/pre-commit index 8ecbaab5c..24937727c 100755 --- a/pre-commit +++ b/pre-commit @@ -81,6 +81,15 @@ check_tab() { done } +# Reject addition of a line without a newline at end-of-file. +check_no_lf_at_eof() { + lines=$(git diff-index -p --cached $against -- "$1" | tail -2) + if echo "$lines" | head -1 | grep -q '^+' && + echo "$lines" | tail -1 | grep -q '^\\ No newline'; then + echo "$1: No newline at end of file" + fi +} + # Custom whitespace checks. check_whitespace() { ws=$(git check-attr whitespace -- "$file" | @@ -90,6 +99,9 @@ check_whitespace() { *,tab-in-indent,*) check_tab "$1" ;; esac fi + case ",$ws," in + *,no-lf-at-eof,*) check_no_lf_at_eof "$1" ;; + esac } bad=$(git diff-index --name-only --cached $against -- | while read file; do From 6d1fe685cb08cba7f1b32e721cb474025bad2e03 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 30 Dec 2010 12:13:20 -0500 Subject: [PATCH 33/57] commit-msg: Allow Change-Id only in footer Gerrit recognizes the Change-Id line only if it appears in the footer. --- commit-msg | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/commit-msg b/commit-msg index 5d18c5ae3..c4d8f06d3 100755 --- a/commit-msg +++ b/commit-msg @@ -79,7 +79,15 @@ msg_second() { } msg_rest() { - false + if echo "$line" | grep -q "^Change-Id:"; then + state=gerrit + fi +} + +msg_gerrit() { + test "x$line" = "x" && return + echo "$line" | grep -q '^[A-Za-z0-9-][A-Za-z0-9-]*:' && return + die 'The Change-Id line must appear in a footer at the bottom.' } # Pipe commit message into the state machine. From b22ff892df4bbb55d9ef23f99d2e4b144d0ba607 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 11 Jan 2011 18:01:50 -0500 Subject: [PATCH 34/57] commit-msg: Do not advise use of COMMIT_MSG after removal Once our temporary COMMIT_MSG file has been removed do not advise the author to use the file to continue editing the message. --- commit-msg | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/commit-msg b/commit-msg index c4d8f06d3..b9c528b2e 100755 --- a/commit-msg +++ b/commit-msg @@ -21,15 +21,17 @@ commit_msg="$GIT_DIR/COMMIT_MSG" sed -n -e '/^#/d' -e '/^diff --git/q' -e 'p;d' "$1" > "$commit_msg" +die_advice=' +To continue editing, run the command + git commit -e -F '"$commit_msg"' +(assuming your working directory is at the top).' + die() { echo 'commit-msg hook failure' 1>&2 echo '-----------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 - echo ' -To continue editing, run the command - git commit -e -F '"$commit_msg"' -(assuming your working directory is at the top).' 1>&2 + test -n "$die_advice" && echo "$die_advice" 1>&2 exit 1 } @@ -97,6 +99,7 @@ while IFS='' read line; do msg_$state || break done && rm -f "$commit_msg" || exit 1 +die_advice='' # No more temporary message file. #----------------------------------------------------------------------------- # Optionally run Gerrit's commit-msg hook to add a Change-Id line. From 5d4166083669297e2930ad6d23150e17edcb4eac Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 13 Jan 2011 15:33:33 -0500 Subject: [PATCH 35/57] Update copyright notices for year 2011 --- NOTICE | 2 +- commit-msg | 2 +- hooks-chain.bash | 2 +- pre-commit | 2 +- pre-commit-style | 2 +- prepare-commit-msg | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NOTICE b/NOTICE index cd8dcca71..1b9d3a599 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Kitware Local Git Hooks -Copyright 2010 Kitware, Inc. +Copyright 2010-2011 Kitware, Inc. This product includes software developed at Kitware, Inc. (http://www.kitware.com/). diff --git a/commit-msg b/commit-msg index b9c528b2e..414285f66 100755 --- a/commit-msg +++ b/commit-msg @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2010 Kitware, Inc. +# Copyright 2010-2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/hooks-chain.bash b/hooks-chain.bash index 63c1f4019..a4c7544bd 100644 --- a/hooks-chain.bash +++ b/hooks-chain.bash @@ -1,5 +1,5 @@ #============================================================================= -# Copyright 2010 Kitware, Inc. +# Copyright 2010-2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pre-commit b/pre-commit index 24937727c..d6aedb252 100755 --- a/pre-commit +++ b/pre-commit @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2010 Kitware, Inc. +# Copyright 2010-2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pre-commit-style b/pre-commit-style index 7fc51c87b..74d2caa34 100644 --- a/pre-commit-style +++ b/pre-commit-style @@ -1,5 +1,5 @@ #============================================================================= -# Copyright 2010 Kitware, Inc. +# Copyright 2010-2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/prepare-commit-msg b/prepare-commit-msg index 505b522d5..b791fbfb5 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2010 Kitware, Inc. +# Copyright 2010-2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From f635fab3cc88bbba7fc4b58e8def08a630346237 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 26 Jan 2011 13:47:38 -0500 Subject: [PATCH 36/57] pre-commit: Allow merged submodule updates If "git merge" brings in a submodule update then allow it without requiring the extra step added in commit 48e72dd1 (Check submodules staged with other changes, 2010-08-10). --- pre-commit | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pre-commit b/pre-commit index d6aedb252..3b9010899 100755 --- a/pre-commit +++ b/pre-commit @@ -56,6 +56,9 @@ else against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi +# Merge ("git commit" after "git merge" with conflicts or --no-commit) +merge_head=$(git rev-parse -q --verify MERGE_HEAD) || merge_head='' + # Disallow non-ascii file names. The printable range starts at the # space character and ends with tilde. if test "$(git diff --cached --name-only --diff-filter=A -z $against | @@ -209,6 +212,11 @@ check_module() { enabled=$(git config --get --bool hooks.submodule) || enabled=true test "$enabled" = "false" && return + # Allow merged submodule updates. + test -n "$merge_head" && + merge_obj=$(git rev-parse -q --verify "$merge_head:$file") && + test "$merge_obj" = "$dst_obj" && return + # Allow module-only commits without extra work. test -z "$diffs_normal" && return From bc6d41f4d2c9b0c4645aaf6be4d92429bf52fab6 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 26 Jan 2011 13:56:06 -0500 Subject: [PATCH 37/57] pre-commit: Disallow submodule rewind Reject commits that rewind a submodule relative to any parent (HEAD or MERGE_HEAD). --- pre-commit | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/pre-commit b/pre-commit index 3b9010899..f46cf5d78 100755 --- a/pre-commit +++ b/pre-commit @@ -249,6 +249,31 @@ to checkout the current version of the submodule in your work tree. Test your changes again to see if they still work with the module. Finally, try the commit again. ' + return 1 +} + +check_module_rewind() { + parent_name="$1" + parent_commit="$2" + base=$(GIT_DIR="$file/.git" \ + git merge-base $src_obj $dst_obj 2>/dev/null) || base='' + test "$base" != "$dst_obj" && return + parent_short=$(short_commit "$parent_commit") + src_short=$(GIT_DIR="$file/.git" short_commit "$src_obj") + dst_short=$(GIT_DIR="$file/.git" short_commit "$dst_obj") + echo 'This commit would rewind a submodule link: + + "'"$file"'" '"$src_short => $dst_short"' + +from the newer version in '"$parent_name"' ('"$parent_short"'). Run + + git reset '"$parent_name"' -- "'"$file"'" + git submodule update -- "'"$file"'" + +to checkout the newer version of the submodule in your work tree. +Then try the commit again. +' + return 1 } diffs=$(git diff-index --cached $against -- | @@ -267,7 +292,28 @@ while read src_mode dst_mode src_obj dst_obj status file; do done test -n "$diffs_module" && echo "$diffs_module" | while read src_mode dst_mode src_obj dst_obj status file; do - check_module + check_module_rewind HEAD "$against" && + check_module || + break +done +) +test -z "$bad" || die "$bad" + +#----------------------------------------------------------------------------- +# Merge checks. +if test -n "$merge_head"; then + merge_diffs=$(git diff-index --cached $merge_head -- | + sed -n '/^:[^:]/ {s/^://;p;}') +else + merge_diffs='' +fi +merge_diffs_normal=$(echo "$merge_diffs" | grep -v '^...... 160000') +merge_diffs_module=$(echo "$merge_diffs" | grep '^...... 160000') +bad=$( +test -n "$merge_diffs_module" && echo "$merge_diffs_module" | +while read src_mode dst_mode src_obj dst_obj status file; do + check_module_rewind MERGE_HEAD "$merge_head" || + break done ) test -z "$bad" || die "$bad" From 69b19172e436eb0911c4dd362470364c694478bb Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 14 Jun 2011 15:42:38 -0400 Subject: [PATCH 38/57] pre-commit-style: Fix exit code on failure The return code from 'die' was absorbed inside a subshell on the right-hand side of a pipeline. Propagate it out to the main script. --- pre-commit-style | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pre-commit-style b/pre-commit-style index 74d2caa34..8c2270431 100644 --- a/pre-commit-style +++ b/pre-commit-style @@ -126,7 +126,7 @@ run_KWStyle() { while read f; do if run_style_on_file "$f" KWStyle; then run_KWStyle_on_file "$f" - fi + fi || return done } @@ -237,7 +237,7 @@ For more information, see merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read MERGED; do - run_uncrustify_on_file "$MERGED" + run_uncrustify_on_file "$MERGED" || return done # end for changed files } @@ -254,12 +254,12 @@ elif $do_uncrustify; then status=0 } check_for_uncrustify - run_uncrustify + run_uncrustify || exit 1 # do_uncrustify will run KWStyle on the files incrementally so excessive # uncrustify merges do not have to occur. elif $do_KWStyle; then if check_for_KWStyle; then - run_KWStyle + run_KWStyle || exit 1 fi fi From f059ba93878b33edf6280aae917bdf4948b8f82b Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 15 Jul 2011 20:39:30 -0400 Subject: [PATCH 39/57] ENH: pre-commit: Prevent add .txx files. A transition occurred in the toolkit to move all .txx files to .hxx files. This prevents accidental additions of or renames to .txx files. Change-Id: Id4ace9cfca2c56506d93396366d146173e015003 --- pre-commit | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pre-commit b/pre-commit index f46cf5d78..4bc6c6b32 100755 --- a/pre-commit +++ b/pre-commit @@ -299,6 +299,22 @@ done ) test -z "$bad" || die "$bad" +#----------------------------------------------------------------------------- +# Do not allow adding of files with .txx extension. +diffs_added=$(git diff-index --diff-filter=A --cached $against -- | + sed -n '/^:[^:]/ {s/^://;p;}') +diffs_normal_added=$(echo "$diffs" | grep -v '^...... 160000') +bad=$( +test -n "$diffs_normal" && echo "$diffs_normal" | +while read src_mode dst_mode src_obj dst_obj status file; do + if echo "$file" | grep -q -E '\.txx$'; then + echo "Attempted to add file $file." + echo "Files with the .txx extension are not allowed -- use .hxx instead." + fi +done +) +test -z "$bad" || die "$bad" + #----------------------------------------------------------------------------- # Merge checks. if test -n "$merge_head"; then From e8180f209b8bf5255ba7ca4833728cb085cdd323 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 6 Oct 2011 13:34:51 -0400 Subject: [PATCH 40/57] pre-commit: Remove KWStyle and uncrustify support These checks can be added in projects that want them using the pre-commit hook chaining. --- pre-commit | 4 - pre-commit-style | 266 ----------------------------------------------- 2 files changed, 270 deletions(-) delete mode 100644 pre-commit-style diff --git a/pre-commit b/pre-commit index f46cf5d78..1a937e16a 100755 --- a/pre-commit +++ b/pre-commit @@ -318,10 +318,6 @@ done ) test -z "$bad" || die "$bad" -#----------------------------------------------------------------------------- -# Style hooks. -. "$GIT_DIR/hooks/pre-commit-style" - #----------------------------------------------------------------------------- # Chain to project-specific hook. . "$GIT_DIR/hooks/hooks-chain.bash" diff --git a/pre-commit-style b/pre-commit-style deleted file mode 100644 index 8c2270431..000000000 --- a/pre-commit-style +++ /dev/null @@ -1,266 +0,0 @@ -#============================================================================= -# Copyright 2010-2011 Kitware, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -#============================================================================= - -# Run uncrustify and KWStyle pre-commit hooks. -# -# 'git config' is used to enable the hooks and set their configuration files. -# The repository .gitattributes must also enable the hooks on the targeted -# files. - -do_KWStyle=$(git config --bool hooks.KWStyle) || do_KWStyle=false - -do_uncrustify=$(git config --bool hooks.uncrustify) || do_uncrustify=false - -#----------------------------------------------------------------------------- -# Check if we want to run the style on a given file. Uses git attributes. If -# the hook.style attribute is set, then all styles are executed. If the -# hook.style attribute is set to a value, only the values given are executed. -# Also, do not run the style check if there are unstaged changes in the file. -# The first positional parameter is the file to check. -# The second positional parameter is the style to check. -# Returns 0 for execute, 1 for don't execute. -run_style_on_file() { - # Do not run on submodule changes. - if git diff-index --cached $against -- "$1" | grep -q '^:...... 160000'; then - return 1 - fi - if ! git diff-files --quiet -- "$1"; then - # A way to always allow skipping. - skip_unstaged=$(git config --bool hooks.styleSkipUnstaged) || - skip_unstaged=false - file_sha=$(git diff-index --cached --abbrev=7 $against -- "$1" | \ - awk '{print substr($3,1,9) substr($4,1,7)}') - if file_skip_unstaged=$(git config "hooks.$1.styleSkipUnstaged"); then - if test ",$file_skip_unstaged," = ",$file_sha," -o \ - ",$file_skip_unstaged," = ",true,"; then - skip_unstaged=true - fi - fi - - if $skip_unstaged; then - echo "The file '$1' contains unstaged stages. Skipping style \ -check '$2'." - else - die "Style check '$2' cannot run on '$1' with unstaged stages. - -Allow skipping the style check for this commit with - - git config \"hooks.$1.styleSkipUnstaged\" $file_sha" - fi - return 1 - fi - style=$(git check-attr hooks.style -- "$1" | - sed 's/^[^:]*: hooks.style: //') - case "$style" in - 'unset') return 1 ;; - 'set') return 0 ;; - 'unspecified') return 1 ;; - *) echo ",$style," | grep -iq ",$2," && return 0 ;; - esac - return 1 -} - -#----------------------------------------------------------------------------- -# KWStyle. -check_for_KWStyle() { - KWStyle_path=$(git config hooks.KWStyle.path) || - KWStyle_path=$(which KWStyle) - if [ $? != 0 ] ; then - echo "KWStyle executable was not found. - - No style verification will be performed with KWStyle! - -Please install KWStyle or set the executable location with - - git config hooks.KWStyle.path /path/to/KWStyle - -See http://public.kitware.com/KWStyle/ -" >&2 - return 1 - fi - KWStyle_conf=$(git config hooks.KWStyle.conf) - if ! test -f "$KWStyle_conf"; then - die "The file '$KWStyle_conf' does not exist. - -Please run - - git config hooks.KWStyle.conf path/to/KWStyle.conf.xml" - fi - KWStyle_overWriteRulesConf=$(git config hooks.KWStyle.overwriteRulesConf) - if test $? -eq 0 && ! test -f "$KWStyle_overWriteRulesConf"; then - die "The hooks.KWStyle.overwriteRulesConf file '$KWStyle_overWriteRulesConf' does not exist." - fi -} - -run_KWStyle_on_file() { - if test -z "$KWStyle_overWriteRulesConf"; then - "$KWStyle_path" -v -xml "$KWStyle_conf" "$1" - else - "$KWStyle_path" -v -xml "$KWStyle_conf" -o "$KWStyle_overWriteRulesConf" "$1" - fi - - if test $? -ne 0; then - cp -- "$1"{,.kws} - die "KWStyle check failed. - -Line numbers in the errors shown refer to the file: -${1}.kws" - fi -} - -run_KWStyle() { - git diff-index --cached --diff-filter=ACMR --name-only $against -- | - while read f; do - if run_style_on_file "$f" KWStyle; then - run_KWStyle_on_file "$f" - fi || return - done -} - -#----------------------------------------------------------------------------- -# uncrustify. -check_for_uncrustify() { - uncrustify_path=$(git config hooks.uncrustify.path) || - uncrustify_path=$(which uncrustify) || - die "uncrustify executable was not found. - -Please install uncrustify or set the executable location with - - git config hooks.uncrustify.path /path/to/uncrustify - - See http://uncrustify.sourceforge.net/" - - uncrustify_conf=$(git config hooks.uncrustify.conf) - if ! test -f "$uncrustify_conf"; then - die "The file '$uncrustify_conf' does not exist. - -Please run - - git config hooks.uncrustify.conf path/to/uncrustify.conf" - fi -} - -run_uncrustify_on_file() { - MERGED="$1" - if run_style_on_file "$MERGED" uncrustify; then - ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" - BACKUP="./$MERGED.BACKUP.$ext" - LOCAL="./$MERGED.STAGED.$ext" - REMOTE="./$MERGED.UNCRUSTIFY.$ext" - NEW_MERGED="./$MERGED.NEW.$ext" - OLD_MERGED="$MERGED" - - mv -- "$MERGED" "$BACKUP" - # We temporarily change MERGED because the file might already be open, and - # the text editor may complain. - MERGED="$NEW_MERGED" - cp -- "$BACKUP" "$MERGED" - cp -- "$BACKUP" "$LOCAL" - - if ! "$uncrustify_path" -c "$uncrustify_conf" -f "$LOCAL" \ - -o "$REMOTE" 2> /dev/null; then - mv -- "$BACKUP" "$OLD_MERGED" - - if test "$merge_keep_temporaries" = "false"; then - rm -f -- "$LOCAL" "$REMOTE" "$BACKUP" - fi - - die "error when running uncrustify on $OLD_MERGED" - fi - - if test $(git hash-object -- "$LOCAL") != $(git hash-object -- "$REMOTE") && - ! run_merge_tool "$merge_tool" "false" - -For more information, see - - git help mergetool" - merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)" - merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" - git diff-index --cached --diff-filter=ACMR --name-only $against -- | - while read MERGED; do - run_uncrustify_on_file "$MERGED" || return - done # end for changed files -} - -# Do not run during merge commits for now. -if test -f "$GIT_DIR/MERGE_HEAD"; then - : -elif $do_uncrustify; then - # We use git-mergetool settings to review the uncrustify changes. - TOOL_MODE=merge - . "$(git --exec-path)/git-mergetool--lib" - # Redefine check_unchanged because we do not need to check if the merge was - # successful. - check_unchanged() { - status=0 - } - check_for_uncrustify - run_uncrustify || exit 1 -# do_uncrustify will run KWStyle on the files incrementally so excessive -# uncrustify merges do not have to occur. -elif $do_KWStyle; then - if check_for_KWStyle; then - run_KWStyle || exit 1 - fi -fi - -# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : From 18358af31028ee70d3ae9d60851a2051b1994d75 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 6 Oct 2011 17:56:05 -0400 Subject: [PATCH 41/57] Load local hooks configuration from work tree Look for a ".hooks-config.bash" file at the top of the work tree. Load it in each hook to get per-project configuration. Currently we do not define any config options, but this adds the framework. --- commit-msg | 2 ++ hooks-config.bash | 20 ++++++++++++++++++++ pre-commit | 2 ++ prepare-commit-msg | 2 ++ 4 files changed, 26 insertions(+) create mode 100644 hooks-config.bash diff --git a/commit-msg b/commit-msg index 414285f66..8c0562e9d 100755 --- a/commit-msg +++ b/commit-msg @@ -35,6 +35,8 @@ die() { exit 1 } +. "$GIT_DIR/hooks/hooks-config.bash" + #----------------------------------------------------------------------------- # Check the commit message layout with a simple state machine. diff --git a/hooks-config.bash b/hooks-config.bash new file mode 100644 index 000000000..a25d7d12e --- /dev/null +++ b/hooks-config.bash @@ -0,0 +1,20 @@ +#============================================================================= +# Copyright 2010-2011 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +# Load hooks configuration from source tree. +config=".hooks-config.bash" && test -r "$config" && . "$config" + +# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/pre-commit b/pre-commit index 1a937e16a..b2cfe2d63 100755 --- a/pre-commit +++ b/pre-commit @@ -23,6 +23,8 @@ die() { exit 1 } +. "$GIT_DIR/hooks/hooks-config.bash" + zero='0000000000000000000000000000000000000000' #----------------------------------------------------------------------------- diff --git a/prepare-commit-msg b/prepare-commit-msg index b791fbfb5..f2bd2ab84 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -15,6 +15,8 @@ # limitations under the License. #============================================================================= +. "$GIT_DIR/hooks/hooks-config.bash" + #----------------------------------------------------------------------------- # Chain to project-specific hook. . "$GIT_DIR/hooks/hooks-chain.bash" From 3bde0fa936309c7723608951460c38d9484040df Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 6 Oct 2011 18:09:05 -0400 Subject: [PATCH 42/57] hooks-chain: Look for chains in project hooks config If a local hook chain rule is not in the local git configuration check for a value defined in .hooks-config.bash: hooks_chain_pre_commit hooks_chain_commit_msg hooks_chain_prepare_commit_msg This allows project to configure chained hooks without adding any values to the local git configuration. Since the project hooks config can be versioned with the hook scripts it references this ensures a consistent state. --- hooks-chain.bash | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hooks-chain.bash b/hooks-chain.bash index a4c7544bd..7aca6ce95 100644 --- a/hooks-chain.bash +++ b/hooks-chain.bash @@ -16,7 +16,9 @@ hooks_chain() { hook="$1" ; shift - chain=$(git config --get hooks.chain-$hook) || return 0 + chain=$(git config --get hooks.chain-$hook) || + eval chain="\${hooks_chain_${hook//-/_}}" + test -n "$chain" || return 0 case "$chain" in '/'*) prefix="" ;; '[A-Za-z]:/'*) prefix="" ;; From 389c117192ee911af4e6afb42c53f70784cab71e Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 21 Oct 2011 13:39:39 -0400 Subject: [PATCH 43/57] Remove .txx pre-commit check. This is an ITK specific check. It is already implemented in the ITK Utilities/Hooks/pre-commit script executed in the hook chain. This should allow commiting when on the ITK release branch where .txx files still currently exist. Change-Id: I1115a2129beb89d6657d599aa6e72bb6515dfca3 --- pre-commit | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pre-commit b/pre-commit index 4bc6c6b32..f46cf5d78 100755 --- a/pre-commit +++ b/pre-commit @@ -299,22 +299,6 @@ done ) test -z "$bad" || die "$bad" -#----------------------------------------------------------------------------- -# Do not allow adding of files with .txx extension. -diffs_added=$(git diff-index --diff-filter=A --cached $against -- | - sed -n '/^:[^:]/ {s/^://;p;}') -diffs_normal_added=$(echo "$diffs" | grep -v '^...... 160000') -bad=$( -test -n "$diffs_normal" && echo "$diffs_normal" | -while read src_mode dst_mode src_obj dst_obj status file; do - if echo "$file" | grep -q -E '\.txx$'; then - echo "Attempted to add file $file." - echo "Files with the .txx extension are not allowed -- use .hxx instead." - fi -done -) -test -z "$bad" || die "$bad" - #----------------------------------------------------------------------------- # Merge checks. if test -n "$merge_head"; then From 4b2beb61ef92a7d8633d3fe0da888fe0ed99f561 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 21 Oct 2011 13:52:49 -0400 Subject: [PATCH 44/57] Remove pre-commit-style. These pre-commit hooks where added to the ITK specific hook chain since all projects do not use them. Change-Id: Ifcb41645374914c4f30f7c5f2feb2e5d40a4b3e3 --- pre-commit | 4 - pre-commit-style | 266 ----------------------------------------------- 2 files changed, 270 deletions(-) delete mode 100644 pre-commit-style diff --git a/pre-commit b/pre-commit index f46cf5d78..1a937e16a 100755 --- a/pre-commit +++ b/pre-commit @@ -318,10 +318,6 @@ done ) test -z "$bad" || die "$bad" -#----------------------------------------------------------------------------- -# Style hooks. -. "$GIT_DIR/hooks/pre-commit-style" - #----------------------------------------------------------------------------- # Chain to project-specific hook. . "$GIT_DIR/hooks/hooks-chain.bash" diff --git a/pre-commit-style b/pre-commit-style deleted file mode 100644 index 8c2270431..000000000 --- a/pre-commit-style +++ /dev/null @@ -1,266 +0,0 @@ -#============================================================================= -# Copyright 2010-2011 Kitware, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -#============================================================================= - -# Run uncrustify and KWStyle pre-commit hooks. -# -# 'git config' is used to enable the hooks and set their configuration files. -# The repository .gitattributes must also enable the hooks on the targeted -# files. - -do_KWStyle=$(git config --bool hooks.KWStyle) || do_KWStyle=false - -do_uncrustify=$(git config --bool hooks.uncrustify) || do_uncrustify=false - -#----------------------------------------------------------------------------- -# Check if we want to run the style on a given file. Uses git attributes. If -# the hook.style attribute is set, then all styles are executed. If the -# hook.style attribute is set to a value, only the values given are executed. -# Also, do not run the style check if there are unstaged changes in the file. -# The first positional parameter is the file to check. -# The second positional parameter is the style to check. -# Returns 0 for execute, 1 for don't execute. -run_style_on_file() { - # Do not run on submodule changes. - if git diff-index --cached $against -- "$1" | grep -q '^:...... 160000'; then - return 1 - fi - if ! git diff-files --quiet -- "$1"; then - # A way to always allow skipping. - skip_unstaged=$(git config --bool hooks.styleSkipUnstaged) || - skip_unstaged=false - file_sha=$(git diff-index --cached --abbrev=7 $against -- "$1" | \ - awk '{print substr($3,1,9) substr($4,1,7)}') - if file_skip_unstaged=$(git config "hooks.$1.styleSkipUnstaged"); then - if test ",$file_skip_unstaged," = ",$file_sha," -o \ - ",$file_skip_unstaged," = ",true,"; then - skip_unstaged=true - fi - fi - - if $skip_unstaged; then - echo "The file '$1' contains unstaged stages. Skipping style \ -check '$2'." - else - die "Style check '$2' cannot run on '$1' with unstaged stages. - -Allow skipping the style check for this commit with - - git config \"hooks.$1.styleSkipUnstaged\" $file_sha" - fi - return 1 - fi - style=$(git check-attr hooks.style -- "$1" | - sed 's/^[^:]*: hooks.style: //') - case "$style" in - 'unset') return 1 ;; - 'set') return 0 ;; - 'unspecified') return 1 ;; - *) echo ",$style," | grep -iq ",$2," && return 0 ;; - esac - return 1 -} - -#----------------------------------------------------------------------------- -# KWStyle. -check_for_KWStyle() { - KWStyle_path=$(git config hooks.KWStyle.path) || - KWStyle_path=$(which KWStyle) - if [ $? != 0 ] ; then - echo "KWStyle executable was not found. - - No style verification will be performed with KWStyle! - -Please install KWStyle or set the executable location with - - git config hooks.KWStyle.path /path/to/KWStyle - -See http://public.kitware.com/KWStyle/ -" >&2 - return 1 - fi - KWStyle_conf=$(git config hooks.KWStyle.conf) - if ! test -f "$KWStyle_conf"; then - die "The file '$KWStyle_conf' does not exist. - -Please run - - git config hooks.KWStyle.conf path/to/KWStyle.conf.xml" - fi - KWStyle_overWriteRulesConf=$(git config hooks.KWStyle.overwriteRulesConf) - if test $? -eq 0 && ! test -f "$KWStyle_overWriteRulesConf"; then - die "The hooks.KWStyle.overwriteRulesConf file '$KWStyle_overWriteRulesConf' does not exist." - fi -} - -run_KWStyle_on_file() { - if test -z "$KWStyle_overWriteRulesConf"; then - "$KWStyle_path" -v -xml "$KWStyle_conf" "$1" - else - "$KWStyle_path" -v -xml "$KWStyle_conf" -o "$KWStyle_overWriteRulesConf" "$1" - fi - - if test $? -ne 0; then - cp -- "$1"{,.kws} - die "KWStyle check failed. - -Line numbers in the errors shown refer to the file: -${1}.kws" - fi -} - -run_KWStyle() { - git diff-index --cached --diff-filter=ACMR --name-only $against -- | - while read f; do - if run_style_on_file "$f" KWStyle; then - run_KWStyle_on_file "$f" - fi || return - done -} - -#----------------------------------------------------------------------------- -# uncrustify. -check_for_uncrustify() { - uncrustify_path=$(git config hooks.uncrustify.path) || - uncrustify_path=$(which uncrustify) || - die "uncrustify executable was not found. - -Please install uncrustify or set the executable location with - - git config hooks.uncrustify.path /path/to/uncrustify - - See http://uncrustify.sourceforge.net/" - - uncrustify_conf=$(git config hooks.uncrustify.conf) - if ! test -f "$uncrustify_conf"; then - die "The file '$uncrustify_conf' does not exist. - -Please run - - git config hooks.uncrustify.conf path/to/uncrustify.conf" - fi -} - -run_uncrustify_on_file() { - MERGED="$1" - if run_style_on_file "$MERGED" uncrustify; then - ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" - BACKUP="./$MERGED.BACKUP.$ext" - LOCAL="./$MERGED.STAGED.$ext" - REMOTE="./$MERGED.UNCRUSTIFY.$ext" - NEW_MERGED="./$MERGED.NEW.$ext" - OLD_MERGED="$MERGED" - - mv -- "$MERGED" "$BACKUP" - # We temporarily change MERGED because the file might already be open, and - # the text editor may complain. - MERGED="$NEW_MERGED" - cp -- "$BACKUP" "$MERGED" - cp -- "$BACKUP" "$LOCAL" - - if ! "$uncrustify_path" -c "$uncrustify_conf" -f "$LOCAL" \ - -o "$REMOTE" 2> /dev/null; then - mv -- "$BACKUP" "$OLD_MERGED" - - if test "$merge_keep_temporaries" = "false"; then - rm -f -- "$LOCAL" "$REMOTE" "$BACKUP" - fi - - die "error when running uncrustify on $OLD_MERGED" - fi - - if test $(git hash-object -- "$LOCAL") != $(git hash-object -- "$REMOTE") && - ! run_merge_tool "$merge_tool" "false" - -For more information, see - - git help mergetool" - merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)" - merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" - git diff-index --cached --diff-filter=ACMR --name-only $against -- | - while read MERGED; do - run_uncrustify_on_file "$MERGED" || return - done # end for changed files -} - -# Do not run during merge commits for now. -if test -f "$GIT_DIR/MERGE_HEAD"; then - : -elif $do_uncrustify; then - # We use git-mergetool settings to review the uncrustify changes. - TOOL_MODE=merge - . "$(git --exec-path)/git-mergetool--lib" - # Redefine check_unchanged because we do not need to check if the merge was - # successful. - check_unchanged() { - status=0 - } - check_for_uncrustify - run_uncrustify || exit 1 -# do_uncrustify will run KWStyle on the files incrementally so excessive -# uncrustify merges do not have to occur. -elif $do_KWStyle; then - if check_for_KWStyle; then - run_KWStyle || exit 1 - fi -fi - -# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : From b966ceb43755028720f56948e45db0c3bdcda500 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 6 Feb 2012 14:39:18 -0500 Subject: [PATCH 45/57] prepare-commit-msg: Add Gerrit Change-Id to merges The "git merge" command generates its own commit message and does not invoke the commit-msg hook. Fortunately it invokes prepare-commit-msg so teach it to call the gerrit/commit-msg hook for merges. Suggested-by: Chris Harris --- prepare-commit-msg | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/prepare-commit-msg b/prepare-commit-msg index f2bd2ab84..aa86d6d42 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2010-2011 Kitware, Inc. +# Copyright 2010-2012 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,14 @@ . "$GIT_DIR/hooks/hooks-config.bash" +# Invoke the Gerrit Change-Id hook here for "git merge" because +# it does not run the normal commit-msg hook. +hooks_GerritId=$(git config --get hooks.GerritId) +case "$hooks_GerritId,$2,$3" in + true,merge,) "$GIT_DIR/hooks/gerrit/commit-msg" "$1" ;; + *) ;; +esac + #----------------------------------------------------------------------------- # Chain to project-specific hook. . "$GIT_DIR/hooks/hooks-chain.bash" From 5ebbe2daccbb2fc20aa8a43bf872dd2a722156b6 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 29 Feb 2012 16:26:33 -0500 Subject: [PATCH 46/57] Set GIT_DIR if not already set Although it seems reasonable to expect GIT_DIR to be set when hooks are invoked, the "git help hooks" documentation does not guarantee it. On msysGit 1.7.8 (and perhaps others) "git gui" runs prepare-commit-msg without setting GIT_DIR. Set GIT_DIR at the beginning of each commit hook if it is not already set. --- commit-msg | 6 +++--- hooks-config.bash | 7 ++++++- pre-commit | 6 +++--- prepare-commit-msg | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/commit-msg b/commit-msg index 8c0562e9d..8570266b3 100755 --- a/commit-msg +++ b/commit-msg @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2010-2011 Kitware, Inc. +# Copyright 2010-2012 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # limitations under the License. #============================================================================= +. "${BASH_SOURCE%/*}/hooks-config.bash" + # Prepare a copy of the message: # - strip comment lines # - stop at "diff --git" (git commit -v) @@ -35,8 +37,6 @@ die() { exit 1 } -. "$GIT_DIR/hooks/hooks-config.bash" - #----------------------------------------------------------------------------- # Check the commit message layout with a simple state machine. diff --git a/hooks-config.bash b/hooks-config.bash index a25d7d12e..a12ad966d 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -1,5 +1,5 @@ #============================================================================= -# Copyright 2010-2011 Kitware, Inc. +# Copyright 2010-2012 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,6 +14,11 @@ # limitations under the License. #============================================================================= +# Make sure GIT_DIR is set. +if test -z "$GIT_DIR"; then + export GIT_DIR=$(git rev-parse --git-dir) +fi + # Load hooks configuration from source tree. config=".hooks-config.bash" && test -r "$config" && . "$config" diff --git a/pre-commit b/pre-commit index b2cfe2d63..834c3fe45 100755 --- a/pre-commit +++ b/pre-commit @@ -1,6 +1,6 @@ #!/usr/bin/env bash #============================================================================= -# Copyright 2010-2011 Kitware, Inc. +# Copyright 2010-2012 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # limitations under the License. #============================================================================= +. "${BASH_SOURCE%/*}/hooks-config.bash" + die() { echo 'pre-commit hook failure' 1>&2 echo '-----------------------' 1>&2 @@ -23,8 +25,6 @@ die() { exit 1 } -. "$GIT_DIR/hooks/hooks-config.bash" - zero='0000000000000000000000000000000000000000' #----------------------------------------------------------------------------- diff --git a/prepare-commit-msg b/prepare-commit-msg index aa86d6d42..035677f32 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -15,7 +15,7 @@ # limitations under the License. #============================================================================= -. "$GIT_DIR/hooks/hooks-config.bash" +. "${BASH_SOURCE%/*}/hooks-config.bash" # Invoke the Gerrit Change-Id hook here for "git merge" because # it does not run the normal commit-msg hook. From 646b891d70dc244bebd0a5738626168292a16f6f Mon Sep 17 00:00:00 2001 From: Chuck Atkins Date: Wed, 10 Oct 2012 10:53:23 -0400 Subject: [PATCH 47/57] Make all hook references relative to thier location Some hooks were getting referenced directly via $GIT_DIR/hooks while at other times getting referenced relative via ${BASH_SOURCE%/*}. By making all references relative then these hooks can reside in a different folder and still be daisy-chained by other hooks. --- commit-msg | 4 ++-- hooks-config.bash | 3 +++ pre-commit | 2 +- prepare-commit-msg | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/commit-msg b/commit-msg index 8570266b3..238407ffa 100755 --- a/commit-msg +++ b/commit-msg @@ -131,7 +131,7 @@ gerrit_error() { } gerrit_hook() { - "$GIT_DIR/hooks/gerrit/commit-msg" "$@" || + "$HOOKS_DIR/gerrit/commit-msg" "$@" || die 'gerrit/commit-msg failed' } @@ -145,5 +145,5 @@ esac #----------------------------------------------------------------------------- # Chain to project-specific hook. -. "$GIT_DIR/hooks/hooks-chain.bash" +. "$HOOKS_DIR/hooks-chain.bash" hooks_chain commit-msg "$@" diff --git a/hooks-config.bash b/hooks-config.bash index a12ad966d..adc35fd7b 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -22,4 +22,7 @@ fi # Load hooks configuration from source tree. config=".hooks-config.bash" && test -r "$config" && . "$config" +# Set up the location for "this" set of hooks. +HOOKS_DIR="${BASH_SOURCE%/*}" + # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/pre-commit b/pre-commit index 834c3fe45..9d7563444 100755 --- a/pre-commit +++ b/pre-commit @@ -322,7 +322,7 @@ test -z "$bad" || die "$bad" #----------------------------------------------------------------------------- # Chain to project-specific hook. -. "$GIT_DIR/hooks/hooks-chain.bash" +. "$HOOKS_DIR/hooks-chain.bash" hooks_chain pre-commit "$@" # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/prepare-commit-msg b/prepare-commit-msg index 035677f32..a5d459252 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -21,11 +21,11 @@ # it does not run the normal commit-msg hook. hooks_GerritId=$(git config --get hooks.GerritId) case "$hooks_GerritId,$2,$3" in - true,merge,) "$GIT_DIR/hooks/gerrit/commit-msg" "$1" ;; + true,merge,) "$HOOKS_DIR/gerrit/commit-msg" "$1" ;; *) ;; esac #----------------------------------------------------------------------------- # Chain to project-specific hook. -. "$GIT_DIR/hooks/hooks-chain.bash" +. "$HOOKS_DIR/hooks-chain.bash" hooks_chain prepare-commit-msg "$@" From 2d9ee2a97cb41c505692d39aa172ed074c98c48e Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 4 Apr 2013 13:12:19 -0400 Subject: [PATCH 48/57] hooks-config: Subsume hooks-chain functionality Remove hooks-chain.bash and place its content in hooks-config.bash. --- commit-msg | 1 - hooks-chain.bash | 33 --------------------------------- hooks-config.bash | 16 ++++++++++++++++ pre-commit | 1 - prepare-commit-msg | 1 - 5 files changed, 16 insertions(+), 36 deletions(-) delete mode 100644 hooks-chain.bash diff --git a/commit-msg b/commit-msg index 238407ffa..b0006fa91 100755 --- a/commit-msg +++ b/commit-msg @@ -145,5 +145,4 @@ esac #----------------------------------------------------------------------------- # Chain to project-specific hook. -. "$HOOKS_DIR/hooks-chain.bash" hooks_chain commit-msg "$@" diff --git a/hooks-chain.bash b/hooks-chain.bash deleted file mode 100644 index 7aca6ce95..000000000 --- a/hooks-chain.bash +++ /dev/null @@ -1,33 +0,0 @@ -#============================================================================= -# Copyright 2010-2011 Kitware, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -#============================================================================= - -hooks_chain() { - hook="$1" ; shift - chain=$(git config --get hooks.chain-$hook) || - eval chain="\${hooks_chain_${hook//-/_}}" - test -n "$chain" || return 0 - case "$chain" in - '/'*) prefix="" ;; - '[A-Za-z]:/'*) prefix="" ;; - '.'*) prefix="" ;; - *) prefix="./" ;; - esac - if test -x "$prefix$chain" ; then - exec "$prefix$chain" "$@" - fi -} - -# vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/hooks-config.bash b/hooks-config.bash index adc35fd7b..c857c8e4a 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -25,4 +25,20 @@ config=".hooks-config.bash" && test -r "$config" && . "$config" # Set up the location for "this" set of hooks. HOOKS_DIR="${BASH_SOURCE%/*}" +hooks_chain() { + hook="$1" ; shift + chain=$(git config --get hooks.chain-$hook) || + eval chain="\${hooks_chain_${hook//-/_}}" + test -n "$chain" || return 0 + case "$chain" in + '/'*) prefix="" ;; + '[A-Za-z]:/'*) prefix="" ;; + '.'*) prefix="" ;; + *) prefix="./" ;; + esac + if test -x "$prefix$chain" ; then + exec "$prefix$chain" "$@" + fi +} + # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/pre-commit b/pre-commit index 9d7563444..5fe22bf54 100755 --- a/pre-commit +++ b/pre-commit @@ -322,7 +322,6 @@ test -z "$bad" || die "$bad" #----------------------------------------------------------------------------- # Chain to project-specific hook. -. "$HOOKS_DIR/hooks-chain.bash" hooks_chain pre-commit "$@" # vim: set filetype=sh tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab : diff --git a/prepare-commit-msg b/prepare-commit-msg index a5d459252..9d3383909 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -27,5 +27,4 @@ esac #----------------------------------------------------------------------------- # Chain to project-specific hook. -. "$HOOKS_DIR/hooks-chain.bash" hooks_chain prepare-commit-msg "$@" From e684969995e937e47cf60a3577dd47d361db7c5b Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 4 Apr 2013 13:26:03 -0400 Subject: [PATCH 49/57] hooks-config: Read 'git config' values from project .hooks-config Allow projects to configure hooks with a 'git config'-formatted file at the top of their source tree called ".hooks-config". This avoids use of bash-specific syntax and makes configuration declarative. --- hooks-config.bash | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hooks-config.bash b/hooks-config.bash index c857c8e4a..91fb28cd0 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -20,6 +20,16 @@ if test -z "$GIT_DIR"; then fi # Load hooks configuration from source tree. +hooks_config=".hooks-config" +if test -r "$hooks_config"; then + hooks_config() { + git config -f "$hooks_config" "$@" + } +else + hooks_config() { + false + } +fi config=".hooks-config.bash" && test -r "$config" && . "$config" # Set up the location for "this" set of hooks. @@ -28,6 +38,7 @@ HOOKS_DIR="${BASH_SOURCE%/*}" hooks_chain() { hook="$1" ; shift chain=$(git config --get hooks.chain-$hook) || + chain="$(hooks_config --get hooks.chain.$hook)" || eval chain="\${hooks_chain_${hook//-/_}}" test -n "$chain" || return 0 case "$chain" in From c530aa9375b3412ecba16dad053052500f828e2f Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 4 Apr 2013 13:42:50 -0400 Subject: [PATCH 50/57] hooks-config: Factor child execution out of hooks_chain Factor child execution into a separate "hooks_child" function so it can be re-used. --- hooks-config.bash | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hooks-config.bash b/hooks-config.bash index 91fb28cd0..1da984ddf 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -40,15 +40,20 @@ hooks_chain() { chain=$(git config --get hooks.chain-$hook) || chain="$(hooks_config --get hooks.chain.$hook)" || eval chain="\${hooks_chain_${hook//-/_}}" - test -n "$chain" || return 0 - case "$chain" in + hooks_child "$chain" "$@" || exit +} + +hooks_child() { + child="$1" ; shift + test -n "$child" || return 0 + case "$child" in '/'*) prefix="" ;; '[A-Za-z]:/'*) prefix="" ;; '.'*) prefix="" ;; *) prefix="./" ;; esac - if test -x "$prefix$chain" ; then - exec "$prefix$chain" "$@" + if test -x "$prefix$child" ; then + "$prefix$child" "$@" fi } From 0d9698a1517db0fd37a0a0b4cff2128f3d27ee71 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 4 Apr 2013 13:48:59 -0400 Subject: [PATCH 51/57] Load project-specific "start" hooks before our checks Read from the project ".hooks-config" a configuration value hooks.start.commit-msg hooks.start.pre-commit hooks.start.prepare-commit-msg to run from our respective hook before its main checks. --- commit-msg | 3 +++ hooks-config.bash | 6 ++++++ pre-commit | 3 +++ prepare-commit-msg | 3 +++ 4 files changed, 15 insertions(+) diff --git a/commit-msg b/commit-msg index b0006fa91..1ca1c7573 100755 --- a/commit-msg +++ b/commit-msg @@ -17,6 +17,9 @@ . "${BASH_SOURCE%/*}/hooks-config.bash" +# Start with project-specific hook. +hooks_start commit-msg "$@" + # Prepare a copy of the message: # - strip comment lines # - stop at "diff --git" (git commit -v) diff --git a/hooks-config.bash b/hooks-config.bash index 1da984ddf..afdbf9dd7 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -43,6 +43,12 @@ hooks_chain() { hooks_child "$chain" "$@" || exit } +hooks_start() { + hook="$1" ; shift + start="$(hooks_config --get hooks.start.$hook)" + hooks_child "$start" "$@" || exit +} + hooks_child() { child="$1" ; shift test -n "$child" || return 0 diff --git a/pre-commit b/pre-commit index 5fe22bf54..6496b45c7 100755 --- a/pre-commit +++ b/pre-commit @@ -17,6 +17,9 @@ . "${BASH_SOURCE%/*}/hooks-config.bash" +# Start with project-specific hook. +hooks_start pre-commit "$@" + die() { echo 'pre-commit hook failure' 1>&2 echo '-----------------------' 1>&2 diff --git a/prepare-commit-msg b/prepare-commit-msg index 9d3383909..4de7f13f7 100755 --- a/prepare-commit-msg +++ b/prepare-commit-msg @@ -17,6 +17,9 @@ . "${BASH_SOURCE%/*}/hooks-config.bash" +# Start with project-specific hook. +hooks_start prepare-commit-msg "$@" + # Invoke the Gerrit Change-Id hook here for "git merge" because # it does not run the normal commit-msg hook. hooks_GerritId=$(git config --get hooks.GerritId) From 11679f83379b6cf5141cf79af56ab7ff966ef797 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 16 Apr 2013 12:52:37 -0400 Subject: [PATCH 52/57] commit-msg: Remove MERGE_HEAD check from msg_is_merge When using "git commit --amend" to edit a merge commit message the MERGE_HEAD is long gone. Also, some Git versions remove this file before the commit-msg hook is called. In either case we should still allow long summary lines that start in 'Merge ' just as we do for 'Revert '. --- commit-msg | 1 - 1 file changed, 1 deletion(-) diff --git a/commit-msg b/commit-msg index 1ca1c7573..5b3a0bdfb 100755 --- a/commit-msg +++ b/commit-msg @@ -44,7 +44,6 @@ die() { # Check the commit message layout with a simple state machine. msg_is_merge() { - test -f "$GIT_DIR/MERGE_HEAD" && echo "$line" | grep "^Merge " >/dev/null 2>&1 } From b9b952190a736e30ef33f2a75cf4aab45bcb3b5a Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 7 Jul 2014 15:18:38 -0400 Subject: [PATCH 53/57] Add .gitattributes to tell git to use LF newlines These hook scripts execute with bash and need LF newlines. Set eol=lf to get LF newlines even when core.autocrlf is enabled. While at it, also enable whitespace=indent-with-non-tab. --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..246b99e1e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +.git* export-ignore +* eol=lf whitespace=indent-with-non-tab From 6f2466c503b192c2065bcdc2135137ec5c74ceef Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 10 Sep 2014 16:31:32 -0400 Subject: [PATCH 54/57] Add a 'pre-push' hook For now do nothing but chain to a project-specific hook. --- hooks-config.bash | 2 +- pre-push | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100755 pre-push diff --git a/hooks-config.bash b/hooks-config.bash index afdbf9dd7..3cb29afdd 100644 --- a/hooks-config.bash +++ b/hooks-config.bash @@ -59,7 +59,7 @@ hooks_child() { *) prefix="./" ;; esac if test -x "$prefix$child" ; then - "$prefix$child" "$@" + echo "$stdin" | "$prefix$child" "$@" fi } diff --git a/pre-push b/pre-push new file mode 100755 index 000000000..4e7c9fd9b --- /dev/null +++ b/pre-push @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +#============================================================================= +# Copyright 2010-2014 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +. "${BASH_SOURCE%/*}/hooks-config.bash" + +# Read all input up front so we can use it and hand it to the chained hook. +stdin="$(cat)" && + +# Chain to project-specific hook. +hooks_chain pre-push "$@" From 1b2a16d909e9f1d5c1335865113c4f7ab154934e Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 15 Apr 2015 10:38:39 -0400 Subject: [PATCH 55/57] pre-commit: Print instructions to chmod files On Windows, "chmod" does not work, so print the instructions to use "git update-index --chmod=" instead. --- pre-commit | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pre-commit b/pre-commit index 6496b45c7..29cc7bb64 100755 --- a/pre-commit +++ b/pre-commit @@ -129,10 +129,26 @@ mode_looks_exe() { git cat-file blob "$2" | head -1 | grep "^#!/" > /dev/null } mode_not_exe () { - echo "The file '$file' has looks executable but does not have an executable mode." + echo "The file '$file' has looks executable but does not have an executable mode. +On UNIX, run: + + $ chmod u+x '$file' && git add -u '$file' + +On Windows, run: + + $ git update-index --chmod=+x '$file' +" } mode_bad_exe () { - echo "The file '$file' has executable mode but does not look executable." + echo "The file '$file' has executable mode but does not look executable. +On UNIX, run: + + $ chmod u-x '$file' && git add -u '$file' + +On Windows, run: + + $ git update-index --chmod=-x '$file' +" } mode_non_file () { echo "The path '$file' has a non-file mode." From 76566ed32d75c182cc1a18ee21ff83dd7622f8c8 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Mon, 25 May 2015 17:31:35 -0400 Subject: [PATCH 56/57] Add a 'post-commit' hook. For now do nothing but chain to a project-specific hook. --- post-commit | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 post-commit diff --git a/post-commit b/post-commit new file mode 100755 index 000000000..2664cb2f3 --- /dev/null +++ b/post-commit @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +#============================================================================= +# Copyright 2010-2015 Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +. "${BASH_SOURCE%/*}/hooks-config.bash" + +# Chain to project-specific hook. +hooks_chain post-commit "$@" From aaf2b7dcf8d963e62367e85c06c18325beafaa50 Mon Sep 17 00:00:00 2001 From: Sebastien Barre Date: Thu, 19 Nov 2015 14:22:55 -0500 Subject: [PATCH 57/57] commit-msg: Make max length of first line configurable --- commit-msg | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/commit-msg b/commit-msg index 5b3a0bdfb..b25597e64 100755 --- a/commit-msg +++ b/commit-msg @@ -53,6 +53,7 @@ msg_is_revert() { msg_first() { len=$(echo -n "$line" | wc -c) + max_len=$(hooks_config --get hooks.commit-msg.maxLength || echo 78) && if test $len -eq 0; then # not yet first line return @@ -61,8 +62,8 @@ msg_first() { -------- '"$line"' --------' - elif test $len -gt 78 && ! msg_is_merge && ! msg_is_revert; then - die 'The first line may be at most 78 characters: + elif test $len -gt "$max_len" && ! msg_is_merge && ! msg_is_revert; then + die 'The first line may be at most '"$max_len"' characters: ------------------------------------------------------------------------------ '"$line"' ------------------------------------------------------------------------------'