7

I am trying to add a git pre-commit hook that will check all changed and new file for TODO: text.

I tried

#!/bin/sh

. git-sh-setup  # for die 
git-diff-index -p -M --cached HEAD -- | grep '^+' |
grep TODO: && die Blocking commit because string TODO: detected in patch
:

That i saw in a similar question, but no luck.

codingninja
  • 1,360
  • 2
  • 13
  • 24
  • "No luck" isn't terribly helpful diagnostic information. If something didn't work as you expect, please explain *how* it didn't -- did it output any error message, for example? – cdhowie Jul 26 '14 at 06:13
  • Figured it out. I had copied what was in the `pre-commit.sample` file onto a plain text file, instead of just renaming. Thanks for the help @VonC – codingninja Jul 26 '14 at 06:39
  • 1
    @jesusjjf OK, I have included your conclusion in the answer for more visibility. – VonC Jul 26 '14 at 06:43

2 Answers2

11

This bit loops over the files that are staged, yes, but it then greps the entire file regardless of what portions of it are staged!

   git diff --cached --name-only | \
       grep -E $FILES_PATTERN | \
       echo 'COMMIT REJECTED Found'  $i 'references. Please remove them before commiting' && exit 1

That's no good - don't test strings that aren't staged.

This solution tests one or multiple FORBIDDEN strings against only the staged code, not the entire file:

pre-commit

#!/bin/bash

RESTORE='\033[0m'
RED='\033[00;31m'
YELLOW='\033[00;33m'
BLUE='\033[00;34m'

FORBIDDEN=( 'TODO:' 'DO NOT COMMIT' 'console.log' 'die' )
FOUND=''

for j in "${FORBIDDEN[@]}"
do
  for i in `git diff --cached --name-only`
  do

    # the trick is here...use `git show :file` to output what is staged
    # test it against each of the FORBIDDEN strings ($j)

    if echo `git show :$i` | grep -q "$j"; then

      FOUND+="${BLUE}$i ${RED}contains ${RESTORE}\"$j\"${RESTORE}\n"

    fi
  done
done

# if FOUND is not empty, REJECT the COMMIT
# PRINT the results (colorful-like)

if [[ ! -z $FOUND ]]; then
  printf "${YELLOW}COMMIT REJECTED\n"
  printf "$FOUND"
  exit 1
fi

# nothing found? let the commit happen
exit 0

enter image description here

Community
  • 1
  • 1
WEBjuju
  • 5,797
  • 4
  • 27
  • 36
9

First, the hook must be in the .git/hook folder and name pre-commit (with execution right: chmod +x .git/hooks/pre-commit)

The OP jesusjjf confirms in the comments that was the issue:

I had copied what was in the pre-commit.sample file onto a plain text file, instead of just renaming.

Second, here are some script examples:


You have another example using a similar technique, using git rev-parse and git diff-index, and the empty tree I mentioned before:

#!/bin/sh

if git rev-parse --verify HEAD >/dev/null 2>&1; then
    against=HEAD
else
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

for FILE in `git diff-index --name-status $against -- | cut -c3-` ; do
    # Check if the file contains 'debugger'
    if [ "grep 'debugger' $FILE" ]
    then
        echo $FILE ' contains debugger!'
        exit 1
    fi
done
exit 

The comments on this gist mention:

On my system, if [ "grep 'debugger' $FILE" ] always evaluates to true.
Changing it to if grep -q 'debugger' "$FILE" fixes that.


A more recent example:

#!/bin/bash

# Pre commit hook that prevents FORBIDDEN code from being commited.
# Add unwanted code to the FORBIDDEN array as necessary

FILES_PATTERN='\.(rb|js|coffee)(\..+)?$'
FORBIDDEN=( debugger ruby-debug )

for i in "${FORBIDDEN[@]}"
do
  git diff --cached --name-only| grep ".js" |xargs sed 's/ //g'|grep "ha_mobile.debug=true" && \
        echo 'COMMIT REJECTED Found ha_mobile.debug=true references. Please remove them before commiting' && exit 1

  git diff --cached --name-only | \
        grep -E $FILES_PATTERN | \
        GREP_COLOR='4;5;37;41' xargs grep --color --with-filename -n $i && \
        echo 'COMMIT REJECTED Found'  $i 'references. Please remove them before commiting' && exit 1
done

exit 0
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Tried the second one with the array of forbidden text, and still not having luck. Does my pre-commit file have to be committed as part of the repo for it to trigger? – codingninja Jul 26 '14 at 06:26
  • @jesusjjf no: it needs to be in the `repo/.git/hooks` folder, called `pre-commit`, and being executable. Add an echo at the beginning to check it is executed. Hooks are never committed (or shouldn't: http://stackoverflow.com/a/3703207/6309) or shared between repos (push/pull). – VonC Jul 26 '14 at 06:27
  • @jesusjjf by "no luck", I assume the hook is not executed, right? What OS are you on? what git version are you using? – VonC Jul 26 '14 at 06:32
  • Don't forget: `chmod +x .git/hooks/pre-commit` ! – emc Oct 01 '15 at 18:27
  • 1
    @npc I agree, and I have added that step in the answer for more visibility. – VonC Oct 01 '15 at 18:46