Git pre-commit hook to format and re-add files at the same time

We are currently using the git hook (below) to run an astil in our source code before allowing the user to commit transactions. This has a caveat that the user has to follow, format their code, and then commit again, which is a little annoying. Ideally, we would like the handcode to format the code and then include that formatted code in the original commit. I tried to re-add the changed files, but it throws ref errors (obviously). I also tried to get the history in the pre-commit capture and try to exit the hook and rerun the git commit command with no luck.

# Run astyle on changed .cs files, ignoring $ignored
res=$(exec git diff --cached --name-only | \
    grep -Ev $ignored | \
    xargs astyle --options=conf/astylerc | \
    tail -n 1)
num_formatted=$(echo $res | cut -b 1) # We are only interested in the number preceeding 'formatted'.
[[ $num_formatted -ne 0 ]] && echo "WARNING: Code has been automatically formatted. Please re-add and re-commit" && exit 1 || echo "No code to format! Continuing commit"

      

Does anyone have any ideas?

+2


source to share


2 answers


In your pre-commit hook, you need to add your files, so if your hook looks something like this:

#!/bin/bash
echo 1 > file
exit 0

      

then you will need to change it to add:

#!/bin/bash
echo 1 > file
git add file
exit 0

      



To get a list of all changed files, you can use git -ls-files:

git ls-files -m

However, it would be better if you could just list from your code which files have changed, or just add all files again. git diff-tree -r --name-only --no-commit-id <tree-ish>

should work for you to get a list of all files.

Basically, adding files again after modification works because the commit doesn't happen until the pre-commit hook is done, so whatever is put in the working tree at that point is committed.

+3


source


Edited after this answer.

You can format and add files back to the hook. The problem is that you may have unstaged staged file changes. To do this in a clean way, you can get the file from the index as tmp, format the tmp, and replace the index entry with the formatted tmp. Here's an approach to something that should solve the problem:



# Regexp for grep to only choose some file extensions for formatting
exts="\.\(ext\|ext2\)$"

# The formatter to use
formatter=`which your_formatter`

# Check availability of the formatter
if [ -z "$formatter" ]
then
  1>&2 echo "$formatter not found. Pre-commit formatting will not be done."
  exit 0
fi

# Format staged files
for file in `git diff --cached --name-only --diff-filter=ACMR | grep $exts`
do
  echo "Formatting $file"
  # Get the file from index
  git show ":$file" > "$file.tmp"
  # Format it
  "$formatter" -i "$file.tmp"
  # Create a blob object from the formatted file
  hash=`git hash-object -w "$file.tmp"`
  # Add it back to index
  git update-index --add --cacheinfo 100644 "$hash" "$file"
  # Remove the tmp file
  rm "$file.tmp"
done

# If no files left in index after formatting - fail
ret=0
if [ ! "`git diff --cached --name-only`" ]; then
  1>&2 echo "No files left after formatting"
  exit 1
fi

      

0


source







All Articles