mirror of
https://github.com/duckdb/duckdb.git
synced 2026-10-28 16:55:13 +08:00
I enabled the pre-commit hook to do automatic formatting of files but I ran into a bunch of issues when using it. This fixes them: 1. Run `format.py` once instead of once per staged file under `xargs -P 0`, which forked thousands of processes for large merge commits. `format.py` parallelises internally already. 2. Add `--staged` so `format.py` picks the staged files itself, 3. Collect worker-thread failures in the main thread: `sys.exit(1)` inside a thread was swallowed, so formatting errors exited 0. 4. Skip rewriting files whose formatted output is unchanged. 5. Leave the formatting unstaged and abort the commit instead of `git add`-ing it, which also staged deliberately-unstaged hunks. New `--modified-list` tells the two apart; `DUCKDB_FORMAT_HOOK_RESTAGE=1` restores the old behaviour.
70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Adds `make format-fix` functionality to .git/hooks/pre-commit."""
|
|
|
|
import os
|
|
|
|
HOOK_MARKER = '# make format-fix hook #'
|
|
HOOK_PATH = '.git/hooks/pre-commit'
|
|
|
|
|
|
FORMAT_SCRIPT = (
|
|
'duckdb/scripts/format.py' if os.path.isfile('duckdb/scripts/format.py') else 'scripts/format.py'
|
|
) # this is to support extension repos
|
|
FORMAT_CMD = f'python3 {FORMAT_SCRIPT} --staged --fix --noconfirm --modified-list "$modified_files"'
|
|
|
|
HOOK_BODY = f"""\
|
|
|
|
# make format-fix hook #
|
|
# format.py determines the staged files itself (skipping deleted files and files
|
|
# it does not format) and parallelises over them, so this is a single invocation
|
|
# rather than one per file.
|
|
modified_files=$(mktemp)
|
|
trap 'rm -f "$modified_files"' EXIT
|
|
{FORMAT_CMD} || exit 1
|
|
if [ -s "$modified_files" ]; then
|
|
# By default the formatting is left unstaged and the commit is aborted: staging
|
|
# it automatically would also sweep in changes that were deliberately left
|
|
# unstaged (e.g. after a `git add -p`). Set DUCKDB_FORMAT_HOOK_RESTAGE=1 to
|
|
# stage the reformatted files and let the commit proceed.
|
|
if [ "${{DUCKDB_FORMAT_HOOK_RESTAGE:-0}}" = "1" ]; then
|
|
xargs -0 -r git add -- <"$modified_files"
|
|
else
|
|
echo ""
|
|
echo "The formatter reformatted the following files:"
|
|
tr '\\0' '\\n' <"$modified_files" | while read -r file; do
|
|
echo " $file"
|
|
done
|
|
echo ""
|
|
echo "The changes were left unstaged. Review them, stage them with 'git add'"
|
|
echo "and commit again, or set DUCKDB_FORMAT_HOOK_RESTAGE=1 to stage them"
|
|
echo "automatically."
|
|
exit 1
|
|
fi
|
|
fi
|
|
"""
|
|
|
|
|
|
def main():
|
|
if os.path.isfile(HOOK_PATH):
|
|
with open(HOOK_PATH, 'r') as f:
|
|
content = f.read()
|
|
if HOOK_MARKER in content:
|
|
print("Hook already exists, no action needed. exiting.")
|
|
else:
|
|
print(
|
|
".git/hooks/pre-commit file exists but does not contain make `make format-fix` functionality for staged files"
|
|
)
|
|
print("Adding `make format-fix` functionality for staged files")
|
|
with open(HOOK_PATH, 'a') as f:
|
|
f.write(HOOK_BODY)
|
|
else:
|
|
print("Creating .git/hooks/pre-commit file with `make format-fix` functionality for staged files")
|
|
with open(HOOK_PATH, 'w') as f:
|
|
f.write('#!/bin/sh\n' + HOOK_BODY)
|
|
os.chmod(HOOK_PATH, 0o755)
|
|
print("Done! ✅")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|