Git pre-commit get all Python files
Asked Answered
L

2

1

I'd like to run black to format all the staged .py files on a commit. Unfortunately due to employer network VPN and restrictions I'm unable to use the pre-commit package because it times out when trying to load the repo.

So I decided to write my own pre-commit hook. Currently I have this

#!/bin/sh 

# Check if this is the initial commit 
if git rev-parse --verify HEAD >/dev/null 2>&1 
then 
    echo "pre-commit: About to create a new commit..." 
    against=HEAD 
else
    echo "pre-commit: About to create the first commit..."
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 
fi 

# Autoformat with black
echo "pre-commit: Autoformatting Python code with black"
black $(git diff-index --cached --name-only --diff-filter=d $against)

The first part I found from Atlassian. The second part needs a filter of some sort to only take the .py files from the column which is returned by diff-index. How to get only the .py files from the list?

Also: I'm quite new to git hooks. Is this a robust way for me and my colleagues to make sure all code is formatted with black?

Laureen answered 20/11, 2020 at 19:18 Comment(3)
I think it would be problematic to change the contents of the files on a pre-commit hook. Usually pre-commit hooks are used to check certain conditions and accordingly either allow or deny the commit to proceed. You should probably look at another solution.Interjection
@Interjection No problem, just don't forget to git add modified files at the end of the hook.Aluminate
git is a version control system. I'd hesitate to use it as a code formatter. The fact that you have to "remember" an extra step is your clue that it's the wrong tool for the jobInterjection
F
4

If you're looking for a way to do this in bash, the missing piece to the puzzle there is the filter to git diff-index.

in your case, you can diff against HEAD which will limit it to the filenames being committed:

$ git diff --staged --name-only --diff-filter=d -- '*.py'
t.py

the expression as written in your original post is also a bit problematic due to files potentially containing spaces in them -- usually the fix for this is to use -z with the output and xargs -0 to run the tool (this will cause filenames to be '\0' delimited):

git diff --staged --name-only --diff-filter=d -z -- '*.py' |
    xargs -0 black

note that this will potentially miss some files that the pre-commit framework will find -- for example extensionless executables with a python shebang, or other files which are conventionally python such as a .pdbrc

Funchal answered 21/11, 2020 at 1:10 Comment(0)
F
2

for what it's worth, with pre-commit you can use the local+system escape hatch to avoid downloading tools (if you have them already installed)

for example:

repos:
-   repo: local
    hooks:
    -   id: black-system
        name: black (system)
        entry: black
        types: [python]
        language: system
        require_serial: true

that said, this largely defeats the main purposes of the framework in that it no longer is managing the installation of the tools (so you'll have to set them up manually yourself)

notably, if you don't have black installed you'll see something like:

$ pre-commit  run --all-files
black (system)...........................................................Failed
- hook id: black-system
- exit code: 1

Executable `black` not found

disclaimer, I'm the maintainer of pre-commit

Funchal answered 21/11, 2020 at 0:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.