How to prevent git from committing two files with names differing only in case? How to prevent git from committing two files with names differing only in case? git git

How to prevent git from committing two files with names differing only in case?


There's nothing built in (although there should be, no doubt). What you can do is provide a pre-commit hook that verifies that all names are OK and prevents the commit if not.

This hook only needs to be run on the Linux box (although making it work on Linux and Mac is easy, it's just Windows with its default impoverished toolbox that is problematic). You might want to add it to a side branch and give the Linux folks instructions on setting it up.

You may want to check branch names as well, as in git pre-commit or update hook for stopping commit with branch names having Case Insensitive match. (Interesting: the answer on this question is my own; I had forgotten it.)

First, let's write a "check for case conflict" function. This is just a matter of sorting with case-folding (so that "helloworld" and "helloWorld" are placed adjacent to each other), then using uniq -di to print any duplicate (after case-folding) strings, but no non-duplicates:

sort -f | uniq -di

If this produces any output, these are the "bad names". Let's capture the output in a temporary file and check its size, so we can print them to standard output as well:

#! /bin/shTF=$(mktemp)trap "rm -f $TF" 0 1 2 3 15checkstdin() {    sort -f | uniq -di > $TF    test -s $TF || return 0   # if $TF is empty, we are good    echo "non-unique (after case folding) names found!" 1>&2    cat $TF 1>&2    return 1}

Now we just need to use it on files that will be committed, and perhaps on branch names as well. The former are listed with git ls-files, so:

git ls-files | checkstdin || {    echo "ERROR - file name collision, stopping commit" 1>&2    exit 1}

You can fancy this up to use git diff-index --cached -r --name-only --diff-filter=A HEAD to check only added files, allowing existing case collisions to continue, and/or try to check things across many branches and/or commits, but that gets difficult.

Combine the above two fragments into one script (and test) and then simply copy it to an executable file named .git/hooks/pre-commit.

Checking branch names is a bit trickier. This really should happen when you create the branch name, rather than when you commit to it, and it's impossible to do a really good job on the client—it has to be done on a centralized server that has a proper global view.

Here is a way to do it on the server in a pre-receive script, in shell script rather than in Python (as in the linked answer). We still need the checkstdin function though, and you might want to do it in an update hook rather than a pre-receive hook, since you don't need to reject the entire push, just the one branch name.

NULLSHA=0000000000000000000000000000000000000000 # 40 0s# Verify that the given branch name $1 is unique,# even IF we fold all existing branch names' cases.# To be used on any proposed branch creation (we won't# look at existing branches).check_new_branch_name() {    (echo "$1"; git for-each-ref --format='%(refname:short)' refs/heads) |      checkstdin || {        echo "ERROR: new branch name $1 is not unique after case-folding" 1>&2        exit 1  # or set overall failure status    }}while read oldsha newsha refname; do    ... any other checks ...    case $oldsha,$refname in    $NULLSHA,refs/heads/*) check_new_branch_name ${refname#refs/heads/};;    esac    ... continue with any other checks ...done