clang-format is a tool that can format source code of C-like languages (C /
C++ / Java / JavaScript / Objective-C / Protobuf). It supports various presets
but it is also possible to fine tune its behavior with a configuration file
named .clang-format
. It has quite a lot
configuration options.
In this post, I’m showing how to use this tool as a git filter in order to automatically format Java code when committing code to git. The inspiration comes from these examples in the Git documentation and by tools like prettier from the JavaScript ecosystem.
First of all, you need to have the clang-format
program somewhere in your
PATH
. You can get it here. For
Windows, there is a
bundle containing various
tools. Just unzip it with 7zip and keep only the bin/clang-format.exe
file.
Next, you’ll need to define your style preferences. Mine currently look like this:
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BinPackArguments: false
BinPackParameters: false
BreakAfterJavaFieldAnnotations: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Attach
ColumnLimit: 120
ContinuationIndentWidth: 4
IndentCaseLabels: true
IndentWidth: 4
SortIncludes: false
SpaceAfterCStyleCast: true
TabWidth: 4
UseTab: Never
You can start with a preset and play with it until it is what you like.
In order to find the style options, clang-format searches for a file called
.clang-format
at the current directory. When it can’t find it, it continues
the search recursively to parent directories. This means that if you keep your
git repositories under a common folder, you can store the .clang-format
file
at the root. And if a project needs different settings, you can always override.
At this point you can experiment with clang-format
from the command line to
see how it works. There are also plugins for IDEs that use it.
Now, the fun part. We’ll setup a git repository to automatically format Java
files with clang-format. You need a .gitattributes
file on your repository
(see documentation). There, you
specify that java files will go through the clang-format-java
filter:
*.java filter=clang-format-java
Note that git doesn’t know anything about this, so we need to define what that filter does. Run the following commands:
git config --global filter.clang-format-java.clean 'clang-format -assume-filename=test.java'
git config --global filter.clang-format-java.smudge cat
A filter has two operations, clean and smudge. Clean happens when files are staged, smudge when files are checked out.
When files are staged, we run clang-format -assume-filename=test.java
. The
-assume-filename
parameter helps clang-format to understand we’re formatting a
Java file. This is needed because the input is received from the stdin and there
is no filename information available.
The effect is that when we’re staging files (e.g. with git add
) what is staged
goes first through clang-format and gets “cleaned” (hence the name of the clean
operation).
The smudge operation cat
simply outputs the clean file as-is.
Note that the .gitattributes
file is part of the git repository, while the
filter definition is part of the git configuration. If someone uses the
repository without having configured the filter, git will not give an error.
It’s a good idea to add the commands that setup the filters in a readme or as
comments in the .gitattributes
file, e.g.:
# To enable the custom clang-format-java filter, you need to run:
# git config --global filter.clang-format-java.clean 'clang-format -assume-filename=test.java'
# git config --global filter.clang-format-java.smudge cat
*.java filter=clang-format-java