Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run git-clang-format on series of git commits

I have written a series of git commits, with awful code formatting.
Before I push them to github, I want to run git-clang-format on each commit, to get a nicely formatted code in my history.

Is there some combination of rebase and git-clang-format which will accomplish this?

like image 869
Chris Jefferson Avatar asked May 15 '17 08:05

Chris Jefferson


3 Answers

That looks like a job for git filter-branch, which can rewrite the commits you want. Since those commits are not yet pushed, changing their content (and, consequently their SHA1) is not a big deal.
And the effect is similar to what a rebase or cherry-picking would do, except you can run any command for each commit being replayed.

You can run a filter-branch over the last few commits:

See "Reformatting Your Codebase with git filter-branch", by Elliot Chance

git filter-branch --tree-filter 'git-clang-format' -- <SHA1>..HEAD

Considering the git-clang-format syntax, you can apply it only on the changed files in each commits.
For instance, for .cpp files:

git filter-branch --tree-filter 'git-clang-format $(\
  git diff-index --diff-filter=AM --name-only $GIT_COMMIT |\
    grep .cpp)' -- <SHA1>..HEAD

Update 2017, with Git 2.14.x/2.15 (Q4 2017) you have an illustration:

See commit 2118805, commit 6134de6 (14 Aug 2017) by Brandon Williams (mbrandonw).
(Merged by Junio C Hamano -- gitster -- in commit a36f631, 25 Sep 2017)

Makefile: add style build rule

Add the 'style' build rule which will run git-clang-format on the diff between HEAD and the current worktree.
The result is a diff of suggested changes.

.PHONY: style
style:
    git clang-format --style file --diff --extensions c,h
like image 88
VonC Avatar answered Nov 15 '22 07:11

VonC


I do this manually right now so the format does not mess up anything. Example: header reorganization is a possibility which can cause compile failures.

Steps start from latest commit. If you start at HEAD~# then the changes almost never unless they are atomic and unrelated. git clang-format only changes the code you changed(and related code blocks) but not other non-touched code.

  1. git clang-format HEAD~1 :result will be files changed. Compile and verify.
  2. git commit these new files as a temp commit.
  3. git rebase -i HEAD~2
  4. Change the new commit to be a "fixup" and finish the rebase.

(you can do this manually on a command line but for some reason I still do this with the editing).

You then repeat the steps for HEAD~2, HEAD~3 until you have finished working your way up to the chain.

Some Notes on this. clang-format will change the same code over and over again in certain cases. It has gotten much rarer but have to ignore them at times.

like image 23
TML Winston Avatar answered Nov 15 '22 08:11

TML Winston


If you want to apply clang-format only to the changed lines in each commit do the following:

# The first commit you want to edit.
# You can use the following command if it’s a child of origin/master.
export FIRST_COMMIT=$(git rev-list --ancestry-path origin/master..HEAD | tail -n 1)

git filter-branch --tree-filter 'git-clang-format $FIRST_COMMIT^' -- $FIRST_COMMIT..HEAD

This will do the following for each commit:

  1. checkout the commit as it was in the original history
  2. compute the diff to FIRST_COMMIT’s parent (e.g. origin/master). (NOTE: If you only were only to compute the diff to the current commits parent you may undo clang-format changes done on other commits!)
  3. clang-format the affected lines
  4. create a copy of the commit on top of the previous one
like image 29
rumpel Avatar answered Nov 15 '22 09:11

rumpel