Как предотвратить git от фиксации двух файлов с именами, отличающимися только регистром?

Мы разрабатываем в смешанной среде — кто-то работает на Mac, а кто-то на Linux. Временами это оказалось непростой задачей, поскольку люди, работающие в Linux, привыкли к тому, что их файловые системы чувствительны к регистру, поэтому нет проблем с фиксацией (случайно или иным образом) нескольких файлов, различающихся только по регистру. (например, FileName.ext против filename.ext)

Однако, когда люди на Mac отправляются проверить репозиторий, наличие файловой системы без учета регистра означает, что два файла, отличающиеся только регистром, перезаписывают друг друга и вызывают общий хаос.

Я знаю, что существуют различные настройки git, которые помогают людям в файловых системах, нечувствительных к регистру, лучше работать с изменениями регистра (например, core.ignorecase), но они не решают проблему, когда в репозитории есть два разных файла, отличающихся только регистром.

Я понимаю, что единственный способ исправить это - убедиться, что люди Linux не передают два файла, отличающихся только регистром. -- Есть ли какой-либо параметр в git, который будет отображать предупреждение или ошибку, если пользователь в файловой системе с учетом регистра попытается зафиксировать файл(ы), которые будут перепутаны друг с другом в файловой системе без учета регистра?


person R.M.    schedule 14.12.2016    source источник
comment
Обратите внимание на этот Сообщение Unix & Linux SE содержит подход к поиску таких файлов в каталоге для большинства Linux, но не подключен к git. (Поэтому он не запускается до фиксации файлов и не ограничивается только зафиксированными файлами.)   -  person R.M.    schedule 15.12.2016


Ответы (1)


Там нет ничего встроенного (хотя должно быть, без сомнения). Что вы можете сделать, так это предоставить хук перед фиксацией, который проверяет, что все имена в порядке, и предотвращает фиксацию, если нет.

Этот хук нужно запускать только в Linux (хотя заставить его работать на Linux и Mac легко, проблематична только Windows с его бедным набором инструментов по умолчанию). Возможно, вы захотите добавить его в боковую ветку и дать людям Linux инструкции по его настройке.

Вы также можете проверить имена веток, как в git pre-commit или update hook для остановки фиксации с помощью имена веток, имеющие совпадение без учета регистра. (Интересно: ответ на этот вопрос принадлежит мне, я его забыл.)

Во-первых, давайте напишем функцию «проверки на конфликт регистра». Это всего лишь вопрос сортировки с сворачиванием регистра (чтобы «helloworld» и «helloWorld» располагались рядом друг с другом), а затем использование uniq -di для вывода любых повторяющихся (после сворачивания регистра) строк, но не дубликатов:

sort -f | uniq -di

Если это приводит к каким-либо результатам, это «плохие имена». Давайте зафиксируем вывод во временном файле и проверим его размер, чтобы мы могли распечатать их и в стандартный вывод:

#! /bin/sh

TF=$(mktemp)
trap "rm -f $TF" 0 1 2 3 15
checkstdin() {
    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
}

Теперь нам просто нужно использовать его для файлов, которые будут зафиксированы, и, возможно, также для имен веток. Первые перечислены с git ls-files, поэтому:

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

Вы можете придумать, как использовать git diff-index --cached -r --name-only --diff-filter=A HEAD для проверки только добавленных файлов, позволяя продолжать существующие коллизии case, и/или пытаться проверять вещи во многих ветвях и/или коммитах, но это становится затруднительным.

Объедините два приведенных выше фрагмента в один скрипт (и тест), а затем просто скопируйте его в исполняемый файл с именем .git/hooks/pre-commit.

Проверка имен веток немного сложнее. Это действительно должно произойти, когда вы создаете имя ветки, а не когда вы фиксируете его, и невозможно действительно хорошо работать на клиенте — это должно быть сделано на централизованном сервере, который имеет надлежащее глобальное представление.

Вот способ сделать это на сервере в сценарии предварительного получения, в сценарии оболочки, а не в Python (как в связанном ответе). Нам по-прежнему нужна функция checkstdin, и вы можете захотеть сделать это в хуке обновления, а не в хуке предварительного получения, так как вам не нужно отклонять весь push, только одну ветвь имя.

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
person torek    schedule 15.12.2016
comment
Не согласен с тем, что это проблема git, с которой приходится иметь дело, поскольку она будет включать логику для каждой перестановки системных соглашений об именах файлов. В любом случае проголосовал за качественный ответ. - person Joe Atzberger; 15.12.2016
comment
@JoeAtzberger: Это правда, что здесь бесконечная череда проблем. Но я думаю, что мы могли бы пройти по крайней мере 90% пути с помощью универсального решения для сопоставления имен файлов (в самом Git) и простого обнаружения и манипулирования регистром-конфликтом (как Git-поддерживаемый сторонний код contrib, своего рода способ сценарии пост-получения почты эволюционировали). То, о чем я думаю для карты имен файлов, является эквивалентом чего-то вроде .gitattributes или .git/info/exclude, вероятно, сохраненного в .git/info, который говорит заменить путь X на путь Y. Сторонний код создаст карту, а checkout будет ее использовать. - person torek; 15.12.2016
comment
Чуть более подробно, карта фактически будет применяться при переходе файлов из индекса и в него, так же, как фильтры очистки и размытия и фильтры преобразования конца строки (CR/LF). Это позволит пользователю Windows или Mac извлекать, изменять и фиксировать файлы, имена которых они не могут получить на самом деле. Это не будет предназначено для долгосрочного общего использования, если только оно не окажется практичным (кажется маловероятным). - person torek; 15.12.2016