Skip to content

Instantly share code, notes, and snippets.

@Git-on-my-level
Created May 14, 2025 13:29
Show Gist options
  • Select an option

  • Save Git-on-my-level/e5a4d2a67bd51ed045c11b17402ec547 to your computer and use it in GitHub Desktop.

Select an option

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.
#!/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