Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

We're currently using a git hook (below) to run astyle on our source code before allowing the user to commit. This has the caveat that the user must commit, have their code formatted, then commit again which is a bit of a nuisance. Ideally we'd want the hook to format the code and then include that formatted code in the original commit instead. I've tried re-adding the changed files but it causes ref errors (obviously). I've also tried getting the history in the pre-commit hook and trying to exit the hook and re-run 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?

like image 485
DTI-Matt Avatar asked Jun 25 '15 18:06

DTI-Matt


People also ask

How do you bypass pre-commit hook?

Use the --no-verify option to skip git commit hooks, e.g. git commit -m "commit message" --no-verify . When the --no-verify option is used, the pre-commit and commit-msg hooks are bypassed.

How do you enforce a pre-commit hook?

If you want enforcement, use an update hook in the central repo. If the hook is doing per-commit verification, you can still provide a pre-commit hook; developers will likely adopt it voluntarily, so that they can find out right away when they've done something wrong, rather than waiting until they try to push.

What does pre-commit hook do?

The pre-commit hook is run first, before you even type in a commit message. It's used to inspect the snapshot that's about to be committed, to see if you've forgotten something, to make sure tests run, or to examine whatever you need to inspect in the code.


2 Answers

You can stash just the unstaged changes using the technique described here. Then run the formatter on just the staged changes and pop the stash. Below pre-commit hook uses clang-format-diff

#!/bin/sh

# stash unstaged changes
git commit --no-verify -m 'Save index'
old_stash=$(git rev-parse -q --verify refs/stash)
git stash push -m 'Unstaged changes'
new_stash=$(git rev-parse -q --verify refs/stash)
git reset --soft HEAD^

# format staged changes
git diff -U0 --no-color --staged HEAD -- '*.java' | $PWD/clang-format-diff.py -i -p1

git add -u
if [ "$old_stash" != "$new_stash" ]; then # if unstaged changes were stashed reapply to working tree
    git stash pop
fi
exit 0
like image 157
lights Avatar answered Sep 20 '22 08:09

lights


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

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

then you would need to modify it to have the add:

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

To get a list of all modified files, you could use git-ls-files:

git ls-files -m

However, it would be better if you could just get a list from your code of which files are modified 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 the files again after modifying works because the commit does not occur until after your pre-commit hook runs, so whatever is staged in the working tree at that point is committed.

like image 45
Michael Avatar answered Sep 18 '22 08:09

Michael