'black as pre-commit hook always fails my commits

I'm trying to use pre-commit to manage Black as a Git pre-commit hook, but I must be doing it wrong.

In my pre-commit config file I have:

-   repo: https://github.com/psf/black
    rev: 19.3b0
    hooks:
    -   id: black

What I'm expecting to happen is for Black to just modify the staged file, and for the commit to succeed. Because the whole point of Black is that it auto-enforces Python code style rules, no questions asked.

What actually happens when I stage a (non-black-compliant) file and try to commit: Black goes ahead and modifies the file to make it compliant, as intended... But the problem is that it returns a "fail". So the commit fails. And then I have to unstage the file, then restage it before committing again... and only then does the commit succeed.

This is a huge annoyance and can't possibly be the intended workflow?

What am I doing wrong?



Solution 1:[1]

(author of pre-commit here)

the framework intentionally does not provide a way to auto-commit modifications. Here's a few issues which have asked for such:

A comment from one of those issues:

pre-commit itself will never touch the staging area. These are good ways to silently break commits. In my mind this is one of the worst things that [other frameworks do and suggest] -- hooks are very frequently not perfect and magically changing what's being committed should not be taken lightly.

That said, if you would like to foot gun, your hook can call git add -u and pre-commit won't know any better :) a sketch of that (untested, discouraged)

  - id: yapf
    entry: bash -c 'yapf "$@"; git add -u' --

(note: using bash will potentially reduce portability)

Another comment notes

Fortunately, git add -u && !! is pretty easy to run if you're ok firing from the hip :)

Solution 2:[2]

One of my developers came with a good tip, in case you have a fail by commit due to black (for example due to the single/double quotes) that's resolved with the pre-commit-hook (like with double-quote-string-fixer). you get in kind of 'nobodies land git situation'. There is a changed file in the staged files, but cannot be committted by the pre-commit-hook, git status won't see any changes, but commit fails (a realy black hole in my opinion). You only get a fail on commit, but nothing can be done (excecpt a reset head on this file). Once you are in thsi situation and run: use commit -m 'Resolving pre-commit-hook changes' --no-verify ..... tada!: it's resolved.

Solution 3:[3]

Looking at black's README, you probably want to use the --check option, which simply exits successfully or unsuccessfully depending on whether the file meets standards. That will cause the commit to fail without modifying the file.

Solution 4:[4]

I'm the same boat as you. From what I've been researching, commits can't be amended by pre-commit hooks.

As far as I can figure out, the best next thing is what bk2204 outlined. We ask black to prevent any commits that include python files that haven't been formatted properly by black. It still makes sure that any commits are formatted, but it is indeed annoying that it won't just automatically format the files for us.

It makes sense. Any changes in a commit have to be staged. If we could do that from the git hook, then our problem would be solved. You got halfway there by modifying the file directly from the git hook. The next half would be staging all the changes of the modified files. But evidently... "You cannot amend a commit in a pre commit hook" which means no staging. https://stackoverflow.com/a/14641656/6032076

I would have commented on bk2204's answer, but I don't have 50 rep yet.

Scratch all that, this answer (https://stackoverflow.com/a/16832764/6032076) claims that commits can be changed in a pre commit hook. In that case files are being added, so I bet in our case files can be restaged/amended.

Solution 5:[5]

In my experience there is no need to unstage any files, either just git add the files that black, or other pre-commit hooks, as modified again which will overwrite them in the stage area and commit with the same message as before unless the error was with a commit message check.

If your commit includes all of your changed files then simply changing the commit command from git commit -m "Your commit message" to git commit -am "Your commit message" will do the job nicely.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Anthony Sottile
Solution 2 michel.iamit
Solution 3 bk2204
Solution 4
Solution 5 Steve Barnes