TL; DR
Создайте драйвер фильтра плюс .gitattributes
: создайте нечеткий фильтр , который запускает tr '\n' '\r'
, и clean filter , который запускает tr '\r' '\n'
, и отметьте файл (ы) вопрос как использовать этот фильтр. Сохраните файл внутри Git, используя LF-only окончания строк. (Драйвер фильтра определен в файле .git/config
или $HOME/.gitconfig
, а имена или шаблоны имен файлов указаны в .gitattributes
.)
Long
Как вы уже видели, Git сильно предпочитает строки с новой строкой. (Он может работать со строками, разделенными символом новой строки, где в последней строке отсутствует терминатор, но это означает, что добавление строки приводит к изменению предыдущей последней строки, поскольку теперь в нем есть терминатор новой строки, в то время как новая последняя строка отсутствует символ новой строки.) Это не имеет значения для отдельных снимков, но имеет значение для создания полезных различий.
Современные MacOS, как и все остальные, используют переводы строки. Только древние обратно совместимые форматы имеют только строки CR. См., Например, на этом веб-сайте SuperUser Stack Exchange: .
Git не имеет встроенного фильтра для преобразования в или из таких концов строк. Git имеет , однако имеет механизм общего назначения для внесения изменений в файлы рабочего дерева.
Помните, что когда Git сохраняет какой-либо файл в моментальном снимке, файл представлен тем, что Git называет BLOB-объектом , который хранится внутри в специальном сжатом (иногда сильно сжатом), только для Git форма. Эта форма бесполезна ни для чего , но Git, поэтому, когда вы получаете файлы в полезной форме - например, через git checkout
- Git расширяет их в их обычную для вашего компьютера форму. Между тем, всякий раз, когда вы берете обычный файл, подобный этому, и конвертируете его в форму только для Git, Git сжимает файл до его формы только для Git. Это происходит всякий раз, когда вы копируете файл обратно в Git index , используя git add
.
Индексная копия каждого файла существует, пока у вас есть рабочее дерево, точно так же как подтвержденная копия. Индексная копия находится в том же формате Git-only. Ключевым отличием здесь является то, что подтвержденная копия не может быть изменена, но индексная копия может быть изменена . Выполнение git commit
делает снимок того, что находится в индексе прямо в этой точке , и создает новый снимок для нового коммита. Следовательно, индекс действует как , что входит в следующий коммит . Используя git checkout
, вы копируете существующий коммит в указатель, и Git расширяет его до рабочего дерева; затем, используя git add
, вы выборочно заменяете определенные индексные копии сжатыми версиями файлов рабочего дерева, которые вы изменили.
Это копирование в или из индекса и рабочего дерева является идеальной точкой для преобразования LF-в-CRLF в стиле Windows или наоборот, так что именно здесь Git делает это. Если у вас есть какое-то преобразование другое , которое не встроено непосредственно в Git, вы можете сказать Git сделать это.
Пятна и чистые фильтры
A smudge filter - это фильтр, который Git применяет при преобразовании файла из сжатой индексной копии в копию рабочего дерева. Здесь, если вы выбрали замену символов новой строки на CRLF в стиле «конец строки или разделители» в стиле Windows, у Git есть внутренний конвертер, который сделает это: eol=crlf
. clean filter - это тот, который Git применяет при преобразовании файла из несжатой копии рабочего дерева в сжатую индексную копию; и здесь eol=crlf
указывает Git выполнить обратное преобразование.
Если вы хотите заменить переводы строк только на CR, вы должны придумать свои собственные конвертеры. Допустим, вы называете общий процесс convert-cr
:
*.csv filter=convert-cr
(вместо *.csv eol=crlf
). Эта строка переходит в .gitattributes
(файл, который можно зафиксировать, и вы должны его зафиксировать).
Теперь вы должны определить фильтр convert-cr
. Это происходит в файле конфигурации Git, и здесь мы обнаруживаем небольшой недостаток: файл конфигурации не является коммитируемым. Это проблема безопасности: Git будет запускать здесь произвольные команды, и если я смогу зафиксировать этот файл, а вы клонируете его, вы запустите команды I , не имея возможности сначала их проверить. Таким образом, вы должны поместить это в ваш .git/config
самостоятельно или в глобальную конфигурацию (например, git config --global --edit
):
[filter "convert-cr"]
clean = tr '\r' '\n'
smudge = tr '\n' '\r'
Теперь, когда Git конвертирует из формата Git-only, он переводит символы новой строки в CR, а всякий раз, когда Git преобразует в Git-only формат, он переводит CR в символы новой строки. .
Это не помогает с существующими сохраненными файлами
Все имеющиеся у вас сегодня снимки, содержащие \r
, сохраняются таким образом навсегда. Git никогда не изменит существующий сохраненный файл! Сохраненные данные драгоценны и нерушимы. С этим ничего не поделаешь. Что ж, в почти ничего нет: вы можете полностью выбросить эти коммиты, делая новые и улучшенные коммиты, которые вы используете вместо этого. Но это довольно болезненно: каждый коммит запоминает свои родительские коммиты, поэтому, если вы замените ранний коммит в своем хранилище, вы должны заменить каждого ребенка, внука и т. Д., Чтобы они все помнят эту новую последовательность коммитов. (git filter-branch
делает эту работу.)
Однако вы можете указать Git, как diff конкретные файлы в существующих коммитах, также используя .gitattributes
и diff драйверы . Есть несколько способов сделать это, но самый простой - определить атрибут textconv , который превращает «двоичный» файл, например, файл, в сохраненной версии которого могут быть только символы CR, в текст ( файл, ориентированный на строку, т. е. на новую строку. Используемый здесь фильтр textconv точно такой же, как и фильтр smudge.
Подробнее см. в документации gitattributes .