diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ceb4f453133..caaffef6f960 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -89,6 +89,15 @@ repos: always_run: true pass_filenames: false language: system + # Case-only renames break macOS (case-insensitive) checkouts when casing + # drifts between branches. Existing check-case-conflict only catches two + # distinct paths that collide when lowercased, not a single-file rename. + - id: prevent-case-only-rename + name: prevent case-only renames + entry: bash tools/prevent_case_only_rename.sh + always_run: true + pass_filenames: false + language: system # Javascript linting, formatting. # We're using the local node_modules to simplify things, otherwise would diff --git a/tools/prevent_case_only_rename.sh b/tools/prevent_case_only_rename.sh new file mode 100755 index 000000000000..5884a94f8270 --- /dev/null +++ b/tools/prevent_case_only_rename.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Reject staged case-only renames (Foo.tsx -> foo.tsx). +# These break macOS case-insensitive checkouts when casing drifts across branches. +# check-case-conflict only catches two distinct paths that collide when lowercased. +set -euo pipefail + +git diff --cached -M --name-status | awk -F '\t' ' + $1 ~ /^R/ && tolower($2) == tolower($3) && $2 != $3 { + print "case-only rename forbidden:", $2, "->", $3 + err = 1 + } + END { exit err + 0 } +'