Skip to main content

casecrash

Git can track two files your Mac cannot check out.

README.md and readme.md can sit side by side in a git repo — until someone on macOS or Windows clones it and the checkout explodes. The same goes for Unicode lookalikes: NFC café vs NFD café look identical in your terminal but are different byte sequences, and macOS treats them as the same file.

casecrash reads your git index and flags every pair of tracked files that would collide on a case-insensitive or Unicode-normalizing filesystem — before it breaks a teammate's checkout.

Quickstart

pip install casecrash
cd your-repo
casecrash
casecrash: found 2 filename collision(s) in /tmp/demorepo (5 files scanned):

[normalization] collide after Unicode normalization (macOS):
  'cafe\u0301.md'  [NFD]
  'caf\u00e9.md'  [NFC]
  -> breaks on: macOS

[case] collide on case-insensitive filesystems (macOS, Windows):
  'README.md'
  'readme.md'
  -> breaks on: macOS, Windows

Fix: rename one of each colliding pair before a macOS/Windows user clones this repo.  'git mv' the odd one out and commit.

No collisions? Exit 0 and a clean bill of health:

casecrash: no filename collisions in the git index (128 files scanned).

What it detects

Kind Example Breaks on
case README.md vs readme.md, É vs é macOS, Windows
normalization NFC café vs NFD café macOS (APFS compares names without regard to normalization)
exact-duplicate identical index entry twice everywhere (index corruption)

Detection is done on the git index (git ls-files), not the working tree, so it works on any OS — a Linux CI runner can catch a collision that would only bite macOS users. Filenames are handled as raw bytes, so undecodable names can never crash the tool. Submodules are skipped (and counted).

CI usage

casecrash exits 1 when collisions are found, 0 when clean, 2 on errors — so it drops straight into any pipeline:

# .github/workflows/casecrash.yml
name: casecheck
on: [push, pull_request]
jobs:
  casecrash:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install casecrash
      - run: casecrash

JSON output for scripting:

casecrash --format json

Why this happens

Git was born on case-sensitive Linux filesystems and its index happily stores File.txt and file.txt as two separate entries. macOS and Windows disagree: checking out the second name silently overwrites the first (or the clone fails outright). Normalization collisions are sneakier — macOS's HFS+/APFS normalizes filenames, so two byte-different, canonically-equivalent names are the same file there.

This bites real projects: agents and scripts that generate files with slightly different casings, Unicode filenames pasted from different platforms, and the occasional CVE (see CVE-2021-21300, where git's own checkout could be tricked on case-insensitive filesystems).

Install

pip install casecrash

Requires Python 3.9+ and git. Zero dependencies.

License

MIT

Metadata

Release files for casecrash 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for casecrash 0.1.0
File Size Uploaded
casecrash-0.1.0.tar.gz 12.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for casecrash 0.1.0
File Interpreter ABI Platform
casecrash-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.1 kB

Release files / casecrash-0.1.0.tar.gz

Download URL casecrash-0.1.0.tar.gz
Size 12.9 kB
Tags Source
SHA-256 checksum
How to use checksums
0ed943c3d044fa50b8aaf5e4d2a66fda9c9d160978f81122d4de048e97a56f72
BLAKE2b-256 checksum
How to use checksums
4175b1a2b4754f3a5b0d49205abb1d396fea623bcb60b79691c0e2b45b4e1d0c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / casecrash-0.1.0-py3-none-any.whl

Download URL casecrash-0.1.0-py3-none-any.whl
Size 9.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f4d6eec18c64b07398e4f1e16676315f50d6be7bc8867571aa030f6304013205
BLAKE2b-256 checksum
How to use checksums
b87fac53de19ec06ccf2eb20267c36816cc8b33d78f1cb64e214843b16652fde
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page