Created
May 14, 2025 13:29
-
-
Save Git-on-my-level/e5a4d2a67bd51ed045c11b17402ec547 to your computer and use it in GitHub Desktop.
Bash script to fix Git file case mismatches on case-sensitive filesystems (e.g., macOS APFS). Solves the problem of when you rename a file, but git doesn't recognize the filename as changed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Set IFS to prevent word splitting issues with spaces in paths | |
| IFS='' | |
| # Use git ls-files -z and a while loop with null delimiter | |
| # The '-d $'\0'' tells read to use null character as delimiter | |
| # The '-r' prevents backslash escapes from being interpreted | |
| git ls-files -z | while IFS= read -r -d $'\0' file_path; do | |
| # Skip empty paths that might rarely occur or .git directory | |
| if [[ -z "$file_path" || "$file_path" == .git/* ]]; then | |
| continue | |
| fi | |
| # Get the directory and base name of the file | |
| file_dir=$(dirname "$file_path") | |
| file_base=$(basename "$file_path") | |
| # Determine the directory to check - use . for files in the root | |
| check_dir="$file_dir" | |
| # List contents of the parent directory to find the actual filesystem case | |
| # Use a subshell to avoid affecting the outer loop's directory | |
| # Redirect cd errors to /dev/null | |
| # Capture the null-delimited output of find directly | |
| actual_file_system_path=$( | |
| cd "$check_dir" 2>/dev/null || exit 1 | |
| find . -maxdepth 1 -iname "$file_base" -print0 | |
| ) | |
| # Check if find found a match | |
| if [[ -n "$actual_file_system_path" ]]; then | |
| # Remove the "./" prefix from the found path. | |
| # find -print0 outputs "./filename\0". | |
| # The read -d $'\0' captures "./filename\0" into the variable. | |
| # We need to remove "./" and the trailing null for comparison. | |
| cleaned_name="${actual_file_system_path#./}" | |
| # Remove the trailing null character | |
| cleaned_name="${cleaned_name%$'\0'}" | |
| # Compare the actual filesystem name with the git-indexed base name | |
| if [[ "$cleaned_name" != "$file_base" ]]; then | |
| echo "Renaming '$file_path' to '$check_dir/$cleaned_name'" | |
| # Use git mv to rename the file, preserving history | |
| corrected_path="$check_dir/$cleaned_name" | |
| git mv "$file_path" "$corrected_path" | |
| fi | |
| # else: If find doesn't find the file (shouldn't happen for git-tracked files), we silently skip it. | |
| fi | |
| done | |
| echo "" | |
| echo "Git status after renaming:" | |
| git status |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment