Excluding certain file types from a pre-commit hook

5.5k views Asked by At

I would like to have a pre commit git hook that checks (and, if possible, autoremoves) trailing whitespaces.

In Make git automatically remove trailing whitespace before committing I found a link to a github page where such a hook is implemented. This works fine, but as @VonC mentions on that page

Since that hook gets the file name of each file, I would recommend to be careful for certain type of files: you don't want to remove trailing whitespace in .md (markdown) files! – VonC

and further down

I would rather make the hook able to detect .md file and not remove the whitespaces, rather than asking the end user to add a --no-verify option on the git commit. – VonC

As far as I could see there is no mention of a solution for this.

Since I use .md files in my project with intentional trailing whitespaces, this is an issue for me.
The solution could possibly be trivial, but I have no experience in the language the script is written in (also currently not interested in learning it).

This is the script (copy of github):

#!/bin/bash
#

# A git hook script to find and fix trailing whitespace
# in your commits. Bypass it with the --no-verify option
# to git-commit
#
# usage: make a soft link to this file, e.g., ln -s ~/config/pre-commit.git.sh ~/some_project/.git/hooks/pre-commit

# detect platform
platform="win"
uname_result=`uname`
if [ "$uname_result" = "Linux" ]; then
  platform="linux"
elif [ "$uname_result" = "Darwin" ]; then
  platform="mac"
fi

# change IFS to ignore filename's space in |for|
IFS="
"
# autoremove trailing whitespace
for line in `git diff --check --cached | sed '/^[+-]/d'` ; do
  # get file name
  if [ "$platform" = "mac" ]; then
    file="`echo $line | sed -E 's/:[0-9]+: .*//'`"
  else
    file="`echo $line | sed -r 's/:[0-9]+: .*//'`"
  fi  
  # display tips
  echo -e "auto remove trailing whitespace in \033[31m$file\033[0m!"
  # since $file in working directory isn't always equal to $file in index, so we backup it
  mv -f "$file" "${file}.save"
  # discard changes in working directory
  git checkout -- "$file"
  # remove trailing whitespace
  if [ "$platform" = "win" ]; then
    # in windows, `sed -i` adds ready-only attribute to $file(I don't kown why), so we use temp file instead
    sed 's/[[:space:]]*$//' "$file" > "${file}.bak"
    mv -f "${file}.bak" "$file"
  elif [ "$platform" == "mac" ]; then
    sed -i "" 's/[[:space:]]*$//' "$file"
  else
    sed -i 's/[[:space:]]*$//' "$file"
  fi  
  git add "$file"
  # restore the $file
  sed 's/[[:space:]]*$//' "${file}.save" > "$file"
  rm "${file}.save"
done

if [ "x`git status -s | grep '^[A|D|M]'`" = "x" ]; then
  # empty commit
  echo
  echo -e "\033[31mNO CHANGES ADDED, ABORT COMMIT!\033[0m"
  exit 1
fi

# Now we can commit
exit

How can this be modified so that (for example), .md files are excluded in the check? Also, if it's possible to exclude multiple file types, that would be great.

2

There are 2 answers

2
Arkadiusz Drabczyk On BEST ANSWER

Try this:

#!/bin/bash
#

# A git hook script to find and fix trailing whitespace
# in your commits. Bypass it with the --no-verify option
# to git-commit
#
# usage: make a soft link to this file, e.g., ln -s ~/config/pre-commit.git.sh ~/some_project/.git/hooks/pre-commit

LIST="md txt c cpp"

lookup() {
    IFS=" "
    for i in $LIST
    do
    if [ "$i" = "$1" ]
    then
        return 1
        break
    fi
    done
    return 0
}

# detect platform
platform="win"
uname_result=`uname`
if [ "$uname_result" = "Linux" ]; then
    platform="linux"
elif [ "$uname_result" = "Darwin" ]; then
    platform="mac"
fi

# change IFS to ignore filename's space in |for|
IFS="
"
# autoremove trailing whitespace
for line in `git diff --check --cached | sed '/^[+-]/d'` ; do
    # get file name
    if [ "$platform" = "mac" ]; then
    file="`echo $line | sed -E 's/:[0-9]+: .*//'`"
    else
    file="`echo $line | sed -r 's/:[0-9]+: .*//'`"
    fi

    lookup $(echo "$file" | awk -F . '{print $NF}')
    if [ $? -eq 1 ]
    then
    echo Omitting "$file"
    continue
    fi

    # display tips
    echo -e "auto remove trailing whitespace in \033[31m$file\033[0m!"
    # since $file in working directory isn't always equal to $file in index, so we backup it
    mv -f "$file" "${file}.save"
    # discard changes in working directory
    git checkout -- "$file"
    # remove trailing whitespace
    if [ "$platform" = "win" ]; then
    # in windows, `sed -i` adds ready-only attribute to $file(I don't kown why), so we use temp file instead
    sed 's/[[:space:]]*$//' "$file" > "${file}.bak"
    mv -f "${file}.bak" "$file"
    elif [ "$platform" == "mac" ]; then
    sed -i "" 's/[[:space:]]*$//' "$file"
    else
    sed -i 's/[[:space:]]*$//' "$file"
    fi
    git add "$file"
    # restore the $file
    sed 's/[[:space:]]*$//' "${file}.save" > "$file"
    rm "${file}.save"
done

if [ "x`git status -s | grep '^[A|D|M]'`" = "x" ]; then
    # empty commit
    echo
    echo -e "\033[31mNO CHANGES ADDED, ABORT COMMIT!\033[0m"
    exit 1
fi

# Now we can commit
exit

List of file extensions that should be excluded is in LIST at the beginning of the script:

LIST="md txt c cpp"
0
serv-inc On

You can use pre-commit. The simplest config is

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.2.0
    hooks:
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]

Check out the other hooks, e.g. one that detects merge conflicts.