16

I am new to commit hooks as well as Clang formatting and am attempting to integrate the two. I have the pre-commit hook set up and I know how to run the Clang formatting on the command line, but am unsure of how to add it to the file.

This is the code I run in the command line for formatting: clang-format -i -style=llvm fileName

I am also trying to run this on all files that are staged for commit. git diff --cached --name-only

This is my pre-commit file:

hook_enabled=true

# Redirect output to stderr.
exec 1>&2

# If the hook is enabled and there are one or more files added to the commit run
# code formatting.
if [ "$hook_enabled" != "false" ] &&
    test $(git diff --cached --name-only $against | wc -c) != 0
then
    cat <<\EOF
  Code formatting changed some files, please review and re-add files with git add
EOF
    exit 1

I also added the clang-formatting to package.json:

    "pre-commit": "check-clang-format",
    "format": "git-clang-format",

Please help me integrate the clang-formatting.

Martina
  • 163
  • 1
  • 1
  • 5
  • Check here https://stackoverflow.com/questions/427207/can-git-hook-scripts-be-managed-along-with-the-repository for what hooks are all about. Understand how githooks work before using them. – Lovato May 06 '19 at 19:52

4 Answers4

18

This is now (finally) very simple using the open source https://pre-commit.com (the framework):

repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
  rev: v14.0.6
  hooks:
  - id: clang-format

It grabs a 1-2 MB binary from PyPI for all common platforms (Windows 64 & 32, macOS universal, manylinux 64 & 32 & arm & ppc & s390x). You can pin clang-format 10, 11, or 12 (And now 13, 14, and various patch versions, often same day releases!). See https://github.com/ssciwr/clang-format-wheel. If you use https://pre-commit.ci, you get automatic update PRs and your PRs get automatically fixed.

Henry Schreiner
  • 905
  • 1
  • 7
  • 18
  • How do we specific config for clang-format here? – Alok Prasad Aug 18 '22 at 17:57
  • It will use `-style=file` by default, so add a `.clang-format` file. Or you can set `args: ["-style=llvm"]` (at the same level as `id:`) or some other style if you don't want a style file. – Henry Schreiner Aug 25 '22 at 15:49
  • 1
    Note: As far as I know, this doesn't work with msys2/mingw despite them having a version of clang-format. When I try, it attempts to grab a new version of clang-format instead of just using the one I have installed. That process fails, as there is no clang-format 14 for msys2/mingw. – Tyler Shellberg Sep 13 '22 at 19:07
  • Guessing this is a standard wheel issue - it's not a platform you can distribute wheels for. `pip install numpy` would fail too. – Henry Schreiner Sep 14 '22 at 20:18
  • Can this pre-commit hook be used while patch-adding files? Ie, adding specific lines or hunks? It seems like it refuses to allow the commit because the entire file is not committed. – Tyler Shellberg Oct 12 '22 at 18:32
  • Pre-commit handles the patches for you by basically running only on the version to be committed. It's not up to the hook (in other words, yes). You do have to add the _changed_ version of the patch added lines - the commit must pass for it to allow it. (You can add -n to ignore pre-commit when committing, then git stash, then pre-commit run -a, then git add -u, git commit --amend, git stash pop. Or you can just add the changed patch lines) – Henry Schreiner Oct 13 '22 at 19:55
15

I'm adding the following to the top of my REPO_ROOT/.git/hooks/pre-commit file:

for FILE in $(git diff --cached --name-only)
do
        clang-format -i $FILE
done

The .clang-format file is placed in the REPO_ROOT.

The other answer and the first comment to the original question doesn't say why it is preferred to avoid this solution, so I'd be happy to hear more about that.

Benjamin
  • 437
  • 4
  • 9
  • 6
    Adding a filer for extension is useful - `if [[ "$FILE" =~ \.(c|h|cpp|cc)$ ]]; then` otherwise all sorts of files will be "formatted" – ilya1725 May 15 '20 at 19:20
  • This will likely break for file names with white space. – bitmask Mar 22 '21 at 08:22
  • 2
    For the life of me I could not get the regex expression to work without expressly calling grep: `for FILE in $(git diff --cached --name-only | grep -E '.*\.(c|cpp|h|hpp)')` – Alex Baum May 03 '21 at 23:31
  • @AlexBaum the regular expression is insufficient, as files may be found as well ([see live example](https://regexr.com/6fnc1)). The word boundary must be detected. This expressions should be better: `.*\.(c|cpp|h|hpp)\b` – user5534993 Feb 18 '22 at 07:15
3

Actually, you don't invoke a clang-format binary at pre-commit hook.

Here is an instruction how to setup clang format at pre-commit hook: https://github.com/andrewseidl/githook-clang-format

Installation First, verify that clang-format is installed. On Linux this should be included with the regular clang package. For

MacOSX with Homebrew, clang-format is available via brew install clang-format.

Now install clang-format.hook from this repository into your repo's .git/hooks. If you don't already have a pre-commit hook, you can simply copy clang-format.hook to .git/hooks/pre-commit. For example:

cp githook-clang-format/clang-format.hook myrepo/.git/hooks/pre-commit

Usage Once the pre-commit hook is installed, clang-format will be run on each file included in the commit when you run git commit.

By default, clang-format uses the LLVM style. To change this, either create a .clang-format file with your desired format in the top level of your repo, or set the hooks.clangformat.style config option in your repo. The .clang-format file method is preferred if you will be working with a team or will be doing any major customizations to the style.

You can generate the .clang-format file from your desired style (here, llvm) using:

clang-format -style=llvm -dump-config > .clang-format

To use the git config method, inside your repo do:

git config hooks.clangformat.style llvm

kenorb
  • 155,785
  • 88
  • 678
  • 743
Anton Vlasov
  • 1,372
  • 1
  • 10
  • 18
  • 7
    "Actually, you don't invoke a clang-format binary at pre-commit hook." This is simply a script that invokes `clang-format` in a pre-commit hook. – jazzpi Sep 29 '20 at 10:06
2

Another option (not pre-commit one, but it also can be applied for commit) is to run git clang-format HEAD~1 <whatever options>. This will affect only lines changed by latest commit. It makes changes in place, so -i is not needed in that case.

  • 1
    Since `clang-format` is not a git subcommand by default, consider adding a link to the script needed for this to work: https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/git-clang-format – Aritz Lopez Dec 14 '20 at 13:17
  • That link points to a file which I canot access (access forbidden). – Martin Ueding Jun 23 '21 at 07:01
  • 2
    https://github.com/llvm/llvm-project/blob/main/clang/tools/clang-format/git-clang-format - here's updated link as llvm moved to Github – Kostya Kozko Jul 06 '21 at 09:58