There is a (sort of) way to do this. I would not want to, but if you really want to, continue in this direction.
First, you need to separate the two elements that you have:
- phased changes
- unidentified work tree elements
In addition, you want the first set to be available for reformatting.
This can be done using git stash , as I showed in the answer to How to properly git stash / pop in pre-commit hooks to get a clean working tree for tests? (see the warning there for an error in git stash too, though).
Basically, you want to reach a point in the script where the tests run:
In this state, you can run elements of the work tree via formatters, and git add result in the pre-commit hook (as you already discovered). This will avoid formatting the โotherโ fragment, since it was not in the working directory version. Then you can continue committing (i.e. the rest of the script does not apply here).
The problem is that the working tree version from stash is now being restored. Since you changed the index, you cannot return to this even after the commit is completed:
Instead, you want to find the difference between the stored index ( stash^1 ) and the hidden tree ( stash ) and apply this to the new HEAD commit. There are at least two ways to do this without using git plumbing commands. Both can lead to conflicts due to reformatting the perfect version:
git diff stash^1 stash | git apply --reject git diff stash^1 stash | git apply --reject (and finally git stash drop )git stash branch tempbranch; git commit -m for-cherry-pick; git checkout prev-branch; git cherry-pick -n tempbranch; git branch -D tempbranch
Method 1 is simpler but erratic, since changes that will have merge conflicts are deleted in the "reject" files. Method 2 uses a merge mechanism, so if necessary changes get conflict markers. (If there are no conflicts, -n prevents the commit so that you can make your own with the real message, rather than copying the dummy for-cherry-pick message.)
Of course, I did not test anything. In addition, there are methods for doing this without using git stash , for example, checking the index versions of git add -ed files in a separate directory, formatting things there, and then adding formatted versions back so that none of this process affects the current working directory. In any case, this is probably superior if you are really determined to do it. Here's the script for this method (also not really tested), it requires a bit of added reliability, using -z and xargs -0 , perhaps to handle file names containing spaces in the control of the diff-index output section)
# make a directory for formatting files WORKDIR=$(mktemp -d -t reformat) || exit 1
Here's what I would recommend instead: instead of formatting the code at this point in pre-commit, just check if it is formatted. If so, allow the commit. If not, reject it. This is much more in the spirit of pre-commit bindings, and it allows the script to be used in a different answer . Basically, the moment he says:
status=...
you just run something that checks if the formatter will change anything (perhaps letting the formatting do its job and seeing if the result differs from that in the index that needs to be fixed). It gives you your status. Then you do the rest in the script, with git reset --hard -q && git stash apply --index -q && git stash drop -q restores everything as it was when creating stash.