
Следите за концом строки
- 21 марта 2019
- 11 мин
- 16 841
Один из самых частых вопросов о Гите — почему так сложно работать с окончаниями строк. В этой статье мы попробуем ответить на этот вопрос и рассказать о множестве опций и настроек для контроля над окончаниями строк в Гите.
Гит имеет дело с двумя системами для работы с концами строк в репозиториях. Корень проблемы в том, что популярные операционные системы по-разному обозначают конец строки: Unix, Linux и Mac OS X используют LF, а Windows CRLF.
В этой статье мы не будем брать во внимание, что в предыдущих версиях Mac OS X использовался CR.
Ничего из этого не было бы проблемой, если бы каждый из нас жил в своём маленьком, изолированном мире и никогда не обменивался кодом между разными операционными системами. Под обменом кодом, в данном случае, будем понимать всё — от работы над кросс-платформенным проектом до копирования кода из браузера. Всякий раз, когда вы скачиваете архив проекта с Гитхаба, копируете код из чьего-то блога или гиста или используете код из файла на облачном хранилище, вы работаете с текстом, а значит имеете дело с невидимыми символами окончаний строк.
Все эти действия с кодом потенциально могут привнести разные настройки окончаний строк в вашу кодовую базу. Это может привести к беспорядочным диффам и сделать работу с Гитом в целом неприятной.
Основное решение, которое принял Гит для этой проблемы — указать, что лучший способ хранить окончания строк в репозитории для текстовых файлов — использование LF. Это правило ни к чему вас не принуждает, но большинство разработчиков, использующих Гит и Гитхаб, приняли его как соглашение и мы тоже рекомендуем так настроить ваш конфиг.
Основы
Перед тем, как мы опишем настройки для управления окончаниями строк, нам надо узнать несколько вещей о core.eol и разобраться с тем, что значит записать что-либо в базу данных Гит.
Конец строки
core.eol — первый параметр, о котором нужно знать.
Почти всегда, кроме самых редких случаев, нам не стоит менять дефолтное значение этого параметра. Хотя сам по себе core.eol мало что делает, нам нужно знать его значение каждый раз, когда мы хотим, чтобы Гит изменил окончания строк. Так как этот параметр будет использоваться во всём, о чём пойдёт речь дальше, хорошо бы знать о его существовании и о том, что его значение, вероятно, менять не придётся.
core.eol = native— значение по умолчанию. При записи файла в рабочую папку, Гит изменит окончания строк на соответствующие вашей платформе по умолчанию. Для Windows это будетCRLF, для Unix, Linux и Mac OS X —LF;core.eol = crlf— если установлено такое значение, Гит всегда будет использовать для обозначения конца строкиCRLFпри записи файла в вашу рабочую директорию;core.eol = lf— это значение скажет Гиту всегда использоватьLFдля обозначения конца строки при записи файла в вашу рабочую папку.
Чтобы узнать, какое значение core.eol установлено в вашей системе, нужно запустить в консоли команду git config --global core.eol. Если команда ничего не вернёт, значит, в вашей системе используется значение по умолчанию, native.
Запись и вывод объектов из базы данных
Прежде чем двигаться дальше, мы поговорим о двух важных операциях: записи в объектную базу и выводе данных из неё в рабочую директорию.
Возможно, вы уже знаете, что Гит хранит свою базу данных в папке .git. Он создаёт эту директорию и несколько файлов в ней, после запуска команды git init. Файлы в папке .git определяют все конфигурации Гита, в них хранится история проекта. Это обычные файлы и мы можем их читать и редактировать так же, как файлы самого проекта.
Каждый раз, когда мы делаем команду типа git commit, мы записываем объекты в эту базу данных. Запись в базу данных включает в себя:
- сохранение всего содержимого файла;
- добавление его в список со всеми файлами, которые отслеживает Гит;
- создание блоб-файла;
- вычисление SHA-ключа — хэш-кода, в котором хранится информация о содержимом файла.
Во время записи в базу данных **Гит может запустить фильтры и преобразовать окончания строк.
Есть ещё один случай, когда у Гита появляется возможность преобразовать окончания строк — это запись файлов из базы данных в нашу рабочую папку. Это то, что мы подразумеваем под выводом из базы данных. Такой процесс можно запустить множеством команд, но самая очевидная и простая для понимания — git checkout. Вывод из объектной базы данных также происходит после запуска команд, которые делают изменения в нашей рабочей папке, например, git clone или git reset.
Старая система
Теперь давайте поговорим о старой системе — оригинальном наборе функций в Гите, предназначенном для решения конкретной проблемы с окончаниями строк. Есть большая вероятность, что вы прямо сейчас пользуетесь старой системой и даже не подозреваете об этом.
Вот как это работает: у Гита есть настройка конфигураций core.autocrlf, которая специально создана для того, чтобы все окончания строк в текстовом файле преобразовывались в LF при записи в объектную базу данных репозитория. Вот список разных настроек для core.autocrlf и их значений:
core.autocrlf = false— это значение по умолчанию, которое большинству людей следует сменить. Результатом использования этого значения станет то, что Гит не будет связываться с окончаниями строк в ваших файлах. Там могут быть разные окончаниями строк:LF,CRLF,CRили микс из всех них, но Гиту это будет безразлично. Такое значение может привести к тому, что диффы станут менее читаемыми и появятся сложности при слиянии веток. У большинства пользователей Unix/Linux установлено именно это значение, потому что у них нет проблем сCRLFи им не нужно, чтобы Гит делал дополнительную работу каждый раз при записи файлов в базу данных или в рабочую папку.core.autocrlf = true— значит, что Гит обработает все текстовые файлы и убедится, что всеCRLFзаменены наLFперед записью в базу данных. При обратном процессе он преобразует всеLFвCRLF. Такая установка гарантирует, что ваш репозиторий можно будет использовать на других платформах, сохраняяCRLFв вашем рабочей папке. Поэтому параметрtrueдляcore.autocrlfрекомендован для Windows.core.autocrlf = input— значит, что Гит обработает все текстовые файлы и убедится, что всеCRLFизменены наLFпри записи файлов в базу данных. Однако обратной замены не произойдёт. При записи файлов в рабочую папку из базы данных, для обозначения конца строки останутсяLF. Этот параметр обычно используется в Unix / Linux / OS X для предотвращения записиCRLFв репозиторий. Идея заключается в том, что если вы вставили код из браузера и случайно записалиCRLFв один из ваших файлов, Гит удостоверится, что произойдёт замена наLFпри записи в базу данных.
Чтобы увидеть, какое значение для core.autocrlf установлено в вашей системе, нужно запустить в командной строке git config --global core.autocrlf .
Если команда ничего не вернёт, то вы используете значение по умолчанию, false.
Как же Гит определяет, что файл текстовый? Хороший вопрос. У Гита есть внутренний эвристический метод, который проверяет, двоичный ли файл. Если файл не двоичный, то Гит считает его текстовым. Но Гит иногда может ошибаться, и это будет причиной для знакомства со следующей настройкой.
Параметр core.safecrlf был создан на тот случай, если Гит ошибётся и изменит окончания строк там, где лучше было бы оставить их в покое.
core.safecrlf = true— перед записью в базу данных при подготовке к заменеCRLFнаLF, Гит убедится, что сможет успешно прервать операцию. Он проверит, что можно откатить изменения (изLFвCRLF), а если нет, то отменит операцию.core.safecrlf = warn— сделает то же, что и предыдущий параметр, но вместо того, чтобы прервать операцию, Гит просто предупредит вас о том, что может случиться что-то нехорошее.
Наконец, вы можете создать в корне своего репозитория файл .gitattributes и указать в нём настройки для конкретных файлов. Это позволит вам управлять такими настройками, как autocrlf для каждого типа файлов.
Например, для того, чтобы Гит заменил
CRLFнаLFво всех текстовых файлах, можно написать в.gitattributesтакую строку:*.txt crlf.Или можно сделать, чтобы Гит никогда не заменял
CRLFнаLFв текстовых файлах с помощью такой строки:*.txt -crlfЧтобы Гит заменял
CRLFнаLFв текстовых файлах только при записи в базу данных, но возвращалLFпри записи в рабочий каталог, нужно написать:*.txt crlf=input
Хорошо, видите, какой беспорядок мы тут учинили? И он становится ещё больше, если мы начинаем работать над проектами, которые подталкивают нас к другим глобальным настройкам. Введём в дело новую систему, доступную начиная с версии Гит 1.7.2.
Новая система
Новая система определяет все настройки для окончаний строк в файле .gitattributes вашего репозитория, инкапсулируя их внутри и делая независимыми от глобальных настроек.
В новой системе за то, чтобы указать Гиту, в каких файлах надо заменить CRLF на LF, отвечаете вы, сообщая об этом с помощью атрибута text в файле .gitattributes. В этом случае будет полезен мануал для .gitattributes, а ниже вы найдёте несколько примеров использования атрибута text.
*.txt text— устанавливает атрибутtextдля всех текстовых файлов. Это значит, что Гит будет запускать процесс заменыCRLFнаLFкаждый раз при записи в БД и делать обратную замену при выводе из базы данных в рабочий репозиторий.*.txt -text— снимет со всех текстовых файлов этот фильтр. Это значит, что в указанных файлах не будет заменыCRLFнаLF.*.txt text=auto— установит для всех, подходящих под условие файлов, заменуCRLFнаLF, если Гит с помощью своего эвристического метода определит эти файлы как текстовые, а не бинарные.
Если файл не определён, Гит вернётся к старой системе и настройке core.autocrlf.
Именно так работает обратная совместимость, но я рекомендую, особенно тем, кто использует Windows для разработки, явно создавать файл gitattributes.
Ниже пример файла .gitattributes с общими настройками, который можно использовать для своего проекта. Пример взят отсюда.
# Общие настройки, которые всегда должны быть использованы для настроек вашего языка.
# Автоматическое определение текстовых файлов и выполнение нормализации LF
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
* text=auto
#
# Строка выше будет обрабатывать все файлы, которых нет в списке ниже.
#
# Documents
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.md text
*.tex text
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.sql text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG по дефолту рассматривается как бинарный. Если вы хотите, чтобы он распознавался как текст, закомментируйте следующую строку и раскомментируйте следующую за ней.
*.svg binary
#*.svg text
*.eps binary
#
# Исключить файлы из экспорта
#
.gitattributes export-ignore
.gitignore export-ignore
Как вы могли заметить, с помощью следующей команды можно сказать Гиту обнаруживать все текстовые файлы и автоматически конвертировать в них CRLF в LF:
* text=autoЭто, конечно, лучше, чем требовать от всех одной глобальной настройки для core.autocrlf, но и означает, что вы полностью доверяете Гиту в определении бинарности файлов. На наш взгляд, лучше явно указать текстовые файлы, которым требуется нормализация. Если вы собираетесь использовать этот параметр, не забудьте, что он должен быть указан на первой строке файла .gitattributes, чтобы последующие строки могли его переопределить.
Похожие статьи
Софт для программиста на каждый день
Список программ, без которых работа будет невыносимой.
Фигма или Фотошоп. Что выбрать новичку
Подробное сравнение двух гигантов.
Работа с Git через консоль
Как сделать форк вашего репозитория в GitHub, создать ветку и начать работу через консоль.


