Setting up pre-commit git hook to check Rust code formatting
The whole thing this article is about is pretty common. But it might save you some time when you set up a Rust repository. Quick recap: git hooks are scripts that are triggered by git on certain actions. Here we are interested in the pre-commit hook which is fired before you enter the commit message. We will use it to check the code style with rustfmt
which you need to install first: cargo install rustfmt
. Try running it with the following command: cargo fmt -- --force --write-mode diff
. This will output the suggested patch to fix your codestyle. In-place fixes can be performed with the cargo fmt -- --force --write-mode overwrite
command.
Github Gist with the materials of this tutorial
Writing the hook
So let's wrap it in a simple bash script that will check files staged for commit:
#!/bin/bash
HAS_ISSUES=0
FIRST_FILE=1
for file in $(git diff --name-only --staged); do
FMT_RESULT="$(rustfmt --skip-children --force --write-mode diff $file 2>/dev/null || true)"
if [ "$FMT_RESULT" != "" ]; then
if [ $FIRST_FILE -eq 0 ]; then
echo -n ", "
fi
echo -n "$file"
HAS_ISSUES=1
FIRST_FILE=0
fi
done
if [ $HAS_ISSUES -eq 0 ]; then
exit 0
fi
echo ". Your code has formatting issues in files listed above. Format your code with \`make format\` or call rustfmt manually."
exit 1
Explanation:
git diff --name-only --staged
gather names of staged files;rustfmt --skip-children --force --write-mode diff $file 2>/dev/null || true
does the check magic:--skip-children
-- this flag is required to not check submodules of a module. This is here because those files may be uncommitted but cause the check to fail.--force
-- just a bypass for the warning that suggests installing the nightly version ofrustfmt
.--write-mode diff
-- we check that the diff is empty which actually means that the code style is OK.2>/dev/null || true
--rustfmt
signalizes that it cannot format some lines (for example, very long constant strings) with error messages. With that, we suppress error messages and make the command to exit with 0. That might not be a very good practice but it works for me. Actually, long strings were the only problem.
The rest of the script is pretty trivial and is all about pretty-printing the result of the check.
Putting it to work
When working with got hooks you need to keep in mind that you need to share them. My approach is to create the folder called .githooks
and put all scripts into it. For example, the above script is saved under the name pre-commit
. Don't forget to make it executable!
To make git see hooks in .githooks
run git config core.hooksPath .githooks
. I put this command to my Makefile
to make it runnable with make init
. I also put a script under make format
to quickly fix everything.
Final result
Now if you have any formatting issues git will let you know!
β― git commit
build.rs, src/main.rs. Your code has formatting issues in files listed above.
Format your code with `make format` or call rustfmt manually.
You can also slightly modify this script by using git diff --name-only HEAD~1 HEAD
to fetch the list of files to perform code style check on your CI system.