Search code examples
gitbashpre-commit-hook

Excluding certain file types from a pre-commit hook


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.


Solution

  • 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"