Formatting with clang-format
and git-clang-format
Target Audience
This page introduces the concepts and provides guidance on how to format your code with clang-format
and git-clang-format
. It's designed for newcomers, so If you’re already a senior engineer with experience in Clang, this page might not be for you. You can skip this page and go directly to the Installation page.
git clang-format
vs git-clang-format
- You can use the
git-clang-format
command (when installed globally) as an alternative togit clang-format
in Node.js by leveraging theclang-format-git
orclang-format-git-python
package. - In this document, all instances of the
git clang-format
command will be uniformly referred to as thegit-clang-format
command.
Formatting source code is something that, at least I think, is crucial for improving code quality. The reason why formatting your code is important is that it ensures a uniform appearance, making it easier to read and understand.
This page is not about debates like whether the {
should be on the same line as the if
statement or on the line below. Instead, it focuses on the tools and methods you can use to consistently apply the same formatting.
clang-format
You’ve probably heard of clang-format
, a tool that automatically formats source files for languages like C and C++. It was created as part of the LLVM Clang open-source project. The concept behind clang-format
is simple. You setup a configuration file that defines the formatting style you want for your code, and then you run clang-format
. It will automatically reformat your code to follow the rules specified in the configuration file.
clang-format
offers options for customizing almost everything. Typically, your settings are stored in a file called .clang-format
, which clang-format
will then use. It can handle code written in C, C++, Java, JavaScript, Objective-C, Protobuf, and C#. You can apply it to an entire file with a simple command like clang-format -i my_source.cpp
.
Example
void test(QString&data, bool extraString) {
int i=0;
for (i=0;i<3;i++) {
data+="reallylongstringtoproducealonglineasanexample" + QString::number(i * 1000) + "/filetoload.html";
if (extraString)
{
data += "some-extra";
}
}
}
To format your code, run:
$ clang-format -i my_source.cpp
After running clang-format
, the code will be formatted as follows:
void test(QString &data, bool extraString)
{
int i = 0;
for (i = 0; i < 3; i++) {
data += "reallylongstringtoproducealonglineasanexample" + QString::number(i * 1000)
+ "/filetoload.html";
if (extraString) {
data += "some-extra";
}
}
}
The code above is entirely trivial, but it illustrates what clang-format
can do for your code. Whether you like this formatting or not isn’t the point here, because you can set up any rules that suit you and your team!
Problems with clang-format
clang-format
is a great tool to use but it has one major issue - legacy code and your commit history. The problem is simple. When you reformat a source file, you end up with tons of changes that mess up the commit history, making branch merging more difficult. It also complicates code reviews and code archaeology, because you have to figure out if the changes are actual code modifications or just formatting changes that don’t affect functionality. Another issue is maintaining the code format when adding new code to already formatted section.
If you’re working on a project that’s already fully (100%) clang-format
compliant, using clang-format
alone for the workflow works perfectly. However, some projects like LLVM, osquery, or Electron aren’t fully formatted. In these cases, formatting the entire file isn’t practical because it inadvertently affects parts of the code unrelated to your changes. This adds unnecessary noise to your diffs, making code reviews more difficult.
In such cases, you need a way to surgically format only the lines changed in your contribution. To do this, you can use the clang-format
Git extension called git-clang-format
.
git-clang-format
git-clang-format
is a simple Python script that's often included with the clang-format
package on Ubuntu. If it’s not available on your system, you can manually download the git-clang-format
Python script from the LLVM source tree and add it to your PATH
. Just ensure the script is executable, and you’ll be able to run git-clang-format
from your shell.
What git-clang-format
solves is that it runs clang-format
only on the changes you've made. This helps address the problems I mentioned earlier. It formats only the changes in your pull requests, meaning every time we modify code, it can be formatted with clang-format
. As a result, our legacy code base will gradually become better formatted without loosing the readability during code reviews.
The workflow using git-clang-format
is quite straightforward.
- Develop your code.
- Run
git-clang-format
.
This ensures that the changes you've made are properly formatted!
Formatting a Single Commit
git-clang-format
operates on staged changes. The workflow is simple:
- Write and edit your files however you like (it’s okay to be messy).
- Stage your changes using
git add
. - Format your staged changes with
git-clang-format
.
Here’s an example:
I’ve added a new file,
x.cpp
, and staged it:sh$ git diff --staged diff --git a/x.cpp b/x.cpp new file mode 100644 index 0000000..af14ed5 --- /dev/null +++ b/x.cpp @@ -0,0 +1,3 @@ +int main() { + +}
Running
git-clang-format
results in this output:sh$ git-clang-format changed files: x.cpp
Now,
git status
shows both staged and unstaged changes:sh$ git status On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: x.cpp Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: x.cpp
git diff
will show the unstaged changes – the changes created byclang-format
:sh$ git diff diff --git a/x.cpp b/x.cpp index af14ed5..237c8ce 100644 --- a/x.cpp +++ b/x.cpp @@ -1,3 +1 @@ -int main() { - -} +int main() {}
This workflow allows you to review clang-format
’s changes independently of your development changes. If you don’t like them, you can discard them by purging your working tree using git checkout
. If you’re satisfied, simply stage the formatting changes with git add
.
Specifying a Formatting Style
You can customize the formatting style using the --style
option. Predefined styles include LLVM
, Google
, Chromium
, Mozilla
, and WebKit
. If your project has a .clang-format
file, you can use it by specifying file
as the style:
$ git-clang-format --style=WebKit
$ git-clang-format --style=file # Uses the `.clang-format` file.
Using with a pre-commit
hook
You can use git-clang-format
with a pre-commit
hook to format your code before committing. This ensures that your code is always formatted correctly. This part is well documented in the Use with husky
and lint-staged
page. Take a look at it if you want to know more about it.
Conclusion
clang-format
is a powerful tool, but its real-world application often requires more than just running clang-format -i
. For most developers, the practical approach is to use git-clang-format
to format only the specific changes in your pull request.
Since git-clang-format
works on the staging tree, it’s easy to review formatting changes separately from development changes. This makes code reviews smoother and helps you maintain a clean, professional codebase without sacrificing flexibility during development. Whether you’re tidying up a single commit or an entire branch, a few additional Git commands can go a long way toward keeping your formatting clean and reviewers happy.