Search code examples
gitshgithooks

Git pre-commit hook that searches for text in staged hunks


I want to prevent committing files with a certain string, e.g.:

// TODO foo bar baz

So I use a git pre-commit hook:

#!/bin/sh

EXTENSIONS='\.(cs|cshtml|csproj|css|js|scss|sh|ts|xml|yml|md)?$'
FORBIDDEN='TODO'

git diff --cached --name-only | \
  sed -n -E "/$EXTENSIONS/p" | \
  GREP_COLORS='4;5;37;41' xargs grep --color --with-filename -n "$FORBIDDEN" \
  && echo 'ERROR: Found forbidden texts; remove them then retry' && exit 1 \
  || exit 0

That works. It has one flaw though.

The grep searches for the forbidden text in the entire file, rather than just the staged hunks. So if a file has hunks which I stage, and one hunk with the forbidden text (which I do not stage), then it will cancel the commit.

Is it possible to search for the pattern in the staged hunks only, rather than the entire file?

(I'd like for the script to work in dash.)


Solution

  • I think the hook could be as simple as

    #!/bin/sh
    ! git diff --cached -- "*.cs" "*.cshtml" "*.csproj" "*.css" "*.js" "*.scss" "*.sh" "*.ts" "*.xml" "*.yml" "*.md" |
        grep --quiet "TODO"
    

    Exclamation sign to invert exit code: 1 for found, 0 for not found. Quotes over * to prevent shell to expand patterns in the current directory and pass the patterns to Git — Git interprets them in the entire tree.