gitshellclang-format

Use git-clang-format per commit on a branch, but only for some paths


I have a branch that I've been working on with a few commits. Some of these commits have code that should be formatted with clang-format, but not all code should be formatted. The practical solution to this is of course to do this:

git clang-format main -- path/to/files/to/format

This compares the currently checked out files with how they were in main and runs clang-format on them. -- path/to/files/to/format then limits the formatting to only this path. I could very easily commit this in a commit with subject "Clang format files", or similar, and carry on with my work.

I really want to be able to not have these formatting commits though. While using tools that automatically follows the formatting rules helps a lot, sometimes I still get into this situation with badly formatted code in my branch.

Previously I found a way to use git filter branch to do the formatting. It looks like this, in my .bashrc:

clang-format-branch() {
    sha=$1
    git filter-branch -f --tree-filter \
      "git-clang-format $sha || true" \
      -- \
      $sha..HEAD
}

If I'm on my-branch that's branched out from main, I can run this command like this:

clang-format-branch main

and it (slowly) rewrites my branch to have all files formatted properly. git-clang-format compares against main to find files to format. As there are sometimes no changes, the || true is needed to not stop git filter-branch.

Now, if you're like me, you'd think that this is simple: Simply put these two together in the following way:

git filter-branch -f --tree-filter "git-clang-format main -- path/to/files/to/format || true" -- main..HEAD

Which works perfectly! Now to my trouble: How do I get this into my .bashrc file? The simple solution I tried is this:

clang-format-branch() {
    sha=$1
    shift 1
    git filter-branch -f --tree-filter \
      "git-clang-format $sha $@ || true" \
      -- \
      $sha..HEAD
}

to be able to run:

clang-format-branch main -- path/to/files/to/format

but then I get this:

fatal: bad revision 'path/to/files/to/format || true'

In some way, this get interpreted as a parameter to tree-filter instead of as a command to run. My guess is that it's because of the --, which led me to think that there's maybe something wrong with escaping of the quotes. Here's where I spent time reading about quotes and checking if other tools (e.g. git-filter-repo) would help, but nothing has worked so far and I'm out of ideas. Is there some simple way to make this work?

I'm running on Windows with git bash if that has something to do with it.


Solution

  • Your problem is the $@, which exists to construct multiple resulting strings from multiple supplied arguments, and does, here. Use $* instead, which bungs all the arguments into the (single) result, which will then be re-burst by the shell when filter-branch runs it that way, as your filter.

    Let me recommend this too-hacky-but-not-too-too-hacky function for your ~/.bashrc:

    -x ()
    {
        local -;
        set -x;
        "$@"
    }
    

    which is what I used (-x clang-format @~10 -- main.c) to check my guess.