1

I plan to fix only indentations and trailing spaces on all commits. I do not want to place a .clang-format file in the directory. I do not want to touch bracing too.

The command I have considered should be like

git filter-branch --tree-filter 'clang-format ?????????' --tag-name-filter cat -- --all

How can I do that?

Requirements:

  • No harm to the .git folder.
  • IndentWidth: 2
  • Remove all trailing white spaces
  • No other change to the file (e.g. brace open/close locations).
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
mercury
  • 189
  • 1
  • 15
  • Is this a git question or a clang-format question? The two concepts should be orthogonal. – mkrieger1 Feb 04 '21 at 23:18
  • 1
    @mkrieger1, both. Orthogonal solutions do not fix my problem. I need both contraints not mess up each other. For me, clang-format should run under such a specific condition. For example, I cannot place a `.clang-format` file in the directory. – mercury Feb 04 '21 at 23:19
  • Have you read https://clang.llvm.org/docs/ClangFormatStyleOptions.html to figure out the options you want to use, and was there a problem when you tried to apply them? I'm not sure what exactly you mean by "no harm to the .git folder", because `git filter-branch` rewrites the history which *by definition* touches the contents of the .git folder. – mkrieger1 Feb 04 '21 at 23:21
  • @mkrieger1, solutions based on linux wild `sed` can harm the files inside `.git` folder. I just said that to prevent some crazy proposals. – mercury Feb 04 '21 at 23:25
  • Okay, then use `git filter-branch`. It is designed to do this and will not kill itself. – mkrieger1 Feb 04 '21 at 23:26
  • Does this answer your question? [How can I clang-format my WHOLE git history?](https://stackoverflow.com/questions/58042532/how-can-i-clang-format-my-whole-git-history) – mkrieger1 Feb 04 '21 at 23:27
  • @mkrieger1, that's where I got the base code. Without `.clang-format` how can I put IndentWith to the inline switch as well as what is the command for the trailing spaces? – mercury Feb 04 '21 at 23:31
  • So have you read https://clang.llvm.org/docs/ClangFormatStyleOptions.html? I'm pretty sure this is explained there (specifically, it's explained in the [first section](https://clang.llvm.org/docs/ClangFormatStyleOptions.html#configuring-style-with-clang-format)). – mkrieger1 Feb 04 '21 at 23:34
  • See also https://stackoverflow.com/questions/44482062/removing-trailing-spaces-with-clang-format – mkrieger1 Feb 04 '21 at 23:35
  • @mkrieger1, I have seen that page. I am not going to use google or other styles. I only need two changes. Nothing else is supposed to change. Also, I cannot find anything about trailing spaces too. Do you really have any answer to this question? – mercury Feb 04 '21 at 23:41
  • About trailing spaces read -->> [here](https://stackoverflow.com/questions/44482062/removing-trailing-spaces-with-clang-format) <<-- You do not need to do anything special, clang-format will remove them in any case it seems. – mkrieger1 Feb 04 '21 at 23:43
  • @mkrieger1, `-style=Google` is very bad. I explained why I do not use this method. – mercury Feb 04 '21 at 23:48
  • I do not advise you to use `-style=Google` I just pointed you to the answer where it says that clang-format removes trailing whitespace in any case. – mkrieger1 Feb 04 '21 at 23:49

2 Answers2

1

From the Git side: the filter-branch command1 works in principle by:

  • extracting each commit, one at a time, into a temporary directory;
  • running your filter(s) in that temporary directory on that extracted commit; and then
  • building a new replacement commit from the results left behind in the directory.

The replacement commits get strung together, one at a time, in the usual way, and when all commits have been filtered, Git updates the various branch and other such references (--all) as needed, with --tag-name-filter deciding the new tag names (for you case this is all correct).

The --tree-filter method, which is by far the slowest filter, uses this approach without any optimizations. Other filters exist to (a) go faster if you don't plan to touch most of the files and/or (b) make changes to things like the commit messages or other metadata, instead of or in addition to the snapshots. There's probably no point in trying to use a more optimal filter as you really do need to get all the files out in order to reformat them. So the thing to take away here is that each clang-format run will be run in a private, temporary directory that contains no Git repository. The $PWD of that command will be this private directory.

To make things go faster, it's a good idea to consider using the -d option to put that private directory in a memory or SSD or otherwise fast-access file system. For instance, if /tmp is an in-memory file system, mkdir /tmp/foo; git filter-branch -d /tmp/foo ... may run much faster than the one without the -d /tmp/foo directive. The default -d is a subdirectory that Git makes within the .git directory; if that's on a slow, but highly reliable, file system, your filter-branch operation will go very slowly.

From the clang-format side: use any -style= options you like. As numerous commenters noted, trailing whitespace stripping is the default (and apparently there are no options to control this at all now).

Note that it would be fine to add a .clang-format file to the temporary directory. If you leave it there, it will be added to each replacement commit. If you remove it again after adding it, the lack of a .clang-format file (because you removed it) will be what is in each commit. If you do nothing, there will or won't be a .clang-format file based on whether there was one in the commit being filtered.

If you decide that, after all, you would like to use a .clang-format file and add it to each commit, note that you will have to copy the file from outside the temporary directory, to the temporary directory, before each run of clang-format, because Git itself clears out the temporary directory between each commit. Note further that you will not know where the temporary directory is,2 so to copy a known file, use a full path: cp /tmp/new-clang-format-file .clang-format && clang-format -style=file, for instance.


1git filter-branch is nominally outdated now, but its replacement, filter-repo, isn't included with Git, so there's a bit of a dilemma here. You can install filter-repo separately, or for this kind of one-time job, you can just live with filter-branch.

2Even with the -d option, Git makes various sub-directories of the directory you specify (it drops a bunch of temporary files in various places for its own purposes).

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you very much. Would you also please advise how to fix the indentation too? – mercury Feb 05 '21 at 00:04
  • I don't actually know: I've always used a .clang-format style file generated with one of the "big houses" options (microsoft, google, whoever) and then tweaked to local tastes. So the .clang-format files I've inherited have always been enormous. – torek Feb 05 '21 at 00:05
  • giving `-style=/media/..../.clang-format` will give me an error: `Invalid value for -style`. – mercury Feb 05 '21 at 00:06
  • Yes, it's just literally `-style=file` and then clang-format searches for the `.clang-format` file from $PWD upwards. So if you're using a file, you kind of *have* to copy it into place. You can remove it afterwards, if you don't want to leave the directives in the commit. ... Oh, or: `mkdir /tmp/foo; cp /media/.../.clang-format /tmp/foo; git filter-branch -d /tmp/foo` will probably work, though I have never tested that. – torek Feb 05 '21 at 00:08
  • Again I get `No such file or directory`. I put it in a file called aaa in the parent directory and said `-style=aaa`. – mercury Feb 05 '21 at 00:11
  • I don't think `-style` takes *file* names. It seems to take the literal string `file`, which means "search for .clang-format" (and the underscore variant), and "microsoft", "google", etc. That is, the *name* arguments to `-style` are Magic Names. The remaining `-style` arguments are the individual thingies you can specify *in* a file. – torek Feb 05 '21 at 07:59
0

It seems clang-format does so many changes that I had not told it. It also crashes on the network drives.

For those who read this post in future: I replaced clang-format with sed as it does such a simple job much better than clang-format and with no hassle. Here is my solution.

git filter-branch -f --tree-filter "find . -regex '.*\.\(cpp\|hpp\|c\|h\)' | xargs -L1 sed -i -b -e 's/\s*$/\r/g' -e 's/\t/  /g'" --tag-name-filter cat -- --all

I also had troubles with **/*.h wild-card. It was not working correct. So, I replaced it with find.

I added -f due to some errors.

I also added \r as it is a linux environment editing a windows file. Do not use \r if you do not need it.

mercury
  • 189
  • 1
  • 15