Git. Символы конца строки EOL

15.07.2022

Теги: GitIDELinuxWeb-разработкаWindowsКонфигурацияНастройкаФайл

Символы конца строки EOL для текстовых файлов различаются в зависимости от операционной системы. Linux использует перевод строки LF, Windows использует возврат каретки + перевод строки CRLF. Если несколько разработчиков работают над одним проектом на GitHub под разными операционными системами — бардак практически гарантирован.

Главное, что нужно помнить — в репозитории все текстовые файлы должны быть с окончаниями LF.

Настройки EOL для Git

Настройка core.eol имеет значение по умолчанию native, другие возможные значения — это lf и crlf. Git использует значение этой настройки, когда записывает файлы в рабочую директорию при выполнении таких команд, как git checkout или git clone. Имеет смысл, только если core.autocrlf равно true.

Настройка core.autocrlf имеет значение по умолчанию false, другие возможные значения — это true и input. Настройка определяет, будет ли Git выполнять какие-либо преобразования EOL при записи/чтении в/из репозитория. Значение по умолчанию опасно, потому что может привести к записи в репозиторий CRLF файлов.

  • core.autocrlf=false — ничего не делать при записи в репозиторий, ничего не делать при чтении из репозитория
  • core.autocrlf=input — при записи в репозиторий заменять CRLF на LF, при чтении из репозитория ничего не делать
  • core.autocrlf=true — при записи в репозиторий заменять CRLF на LF, при чтении из репозитория заменять LF на core.eol

Значение input подходит при работе под Linux:

$ git config --local core.eol native
$ git config --local core.autocrlf input

Значение true подходит при работе под Windows:

$ git config --local core.eol native
$ git config --local core.autocrlf true

При выполнении этих команд будет создан файл .git/config в директории проекта:

[core]
eol = native
autocrlf = input
[core]
eol = native
autocrlf = true

Можно записать эти значения в глобальный файл конфигурации Git ~/.gitconfig, если заменить --local на --global.

Все настройки Git

Поскольку мы тут работаем с настройками Git, есть смысл упомянуть, какие они бывают и как их посмотреть.

  • Системная конфигурация Git управляет настройками для всех пользователей и всех репозиториев на компьютере.
  • Глобальная конфигурация Git управляет настройками текущего вошедшего пользователя и всех его репозиториев.
  • Локальная конфигурация Git управляет настройками для отдельно взятого репозитория.

Эти три файла конфигурации выполняются в каскадном порядке — сначала системный, затем глобальный, и наконец, локальный. Это означает, что локальная конфигурация Git всегда будет перезаписывать настройки, установленные в глобальной или системной конфигурации.

$ git config --list
$ git config --list --system
$ git config --list --global
$ git config --list --local

Если не указать, какую конфигурацию надо показать (первая команда) — будут показаны все три конфигурации, объединенные в вывод консоли. Чтобы посмотреть настройки вместе с именем файла конфигурации, можно использовать ключ show-origin.

$ git config --list --show-origin
file:C:/Program Files/Git/etc/gitconfig http.sslcainfo=C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
file:C:/Program Files/Git/etc/gitconfig http.sslbackend=openssl
file:C:/Program Files/Git/etc/gitconfig diff.astextplain.textconv=astextplain
..........
file:C:/Users/Evgeniy/.gitconfig        user.name=Evgeniy Tokmakov
file:C:/Users/Evgeniy/.gitconfig        user.email=...............
file:C:/Users/Evgeniy/.gitconfig        core.autocrlf=false
..........
file:.git/config        core.repositoryformatversion=0
file:.git/config        core.filemode=false
file:.git/config        core.bare=false
..........
$ git config --list --show-origin | grep autocrlf
file:C:/Program Files/Git/etc/gitconfig core.autocrlf=true
file:C:/Users/Evgeniy/.gitconfig        core.autocrlf=false
file:.git/config                        core.autocrlf=true

Небольшой эксперимент

У меня операционная система Windows. Создаем директорию repo-eol-example, внутри нее — текстовой файл file.txt. Добавим в файл пару строк и убедимся, что окончания строк — CRLF.

Переходим в директорию проекта, выполняем три команды

$ git init
$ git config --local core.eol native
$ git config --local core.autocrlf true

Добавляем наш файл в индекс и фиксируем изменения

$ git add file.txt
$ git commit -m "add file.txt"

Добавляем в наш файл еще строку, чтобы он изменился

И восстановим его из репозитория в изначальном виде

$ git checkout -- file.txt

Что произошло? При добавлении файла в репозиторий (commit) символы CRLF были заменены на LF. При извлечении файла в рабочую директорию (checkout) — символы LF были заменены на CRLF.

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

$ git config --local core.autocrlf false
$ git checkout -- file.txt

Что произошло? При извлечении файла в рабочую директорию — символы EOL остались без изменений, как они сохранены в репозитории.

Предупреждения от Git

Когда случается нештатная ситуация — Git предупреждает об этом. Например, если мы установили следующие настройки для Git:

$ git config --local core.eol native
$ git config --local core.autocrlf input

И пытаемся записать CRLF файл в репозиторий — Git предупреждает, что символы CRLF будут заменены на LF (при записи в репозиторий). Тут ситуация явно нештатная — вроде бы настройки соответствуют Linux, но при этом в рабочей директории откуда-то взялся CRLF файл, а этого быть не должно.

$ git add other.txt
warning: CRLF will be replaced by LF in other.txt.
The file will have its original line endings in your working directory

При извлечении такого файла из репозитория в рабочую директорию — никаких преобразований EOL не будет, потому что input работает только при записи в репозиторий. И мы получим LF окончания строк в этом файле — так, как и должно быть в Linux.

Еще одна нештатная ситуация — мы установили следующие настройки для Git:

$ git config --local core.eol native
$ git config --local core.autocrlf true

И пытаемся записать LF файл в репозиторий — Git предупреждает, что символы LF будут заменены на CRLF (при чтении из репозитория). Тут ситуация явно нештатная — вроде бы настройки соответствуют Windows, но при этом в рабочей директории откуда-то взялся LF файл, а этого быть не должно.

$ git add another.txt
warning: LF will be replaced by CRLF in another.txt.
The file will have its original line endings in your working directory

При извлечении такого файла из репозитория в рабочую директорию — будет выполнена замена LF на CRLF. И мы получим CRLF окончания строк в этом файле — так, как и должно быть в Windows.

Тут важно то, что как в первой, так и во второй ситуации — файл будет сохранен в репозитории с LF окончаниями строк, как и должно быть.

Настройка core.safecrlf

Как Git узнает, что файл является текстовым? У Git есть внутренний метод эвристической проверки, является ли файл двоичным или нет. Файл считается текстовым, если он не является двоичным. Git иногда может ошибаться — и по этой причине существует настройка core.safecrlf.

Эту настройку нужно установить в значение true. Тогда при подготовке к замене CRLF на LF — Git проверит, что сможет успешно отменить операцию. Это защита от того, чтобы выполнить замену в файле, который не является текстовым — и, тем самым, безнадежно его испортить.

Работа под Windows

Лично мне удобно везде использовать LF, хотя у меня основная система Windows — поэтому установил себе настройки, чтобы вообще не заменять EOL.

$ git config --global core.eol lf
$ git config --global core.autocrlf false

Современные IDE способны работать под Windows с EOL как в Linux, так что необходимости в заменах просто нет. В настройках VS Code у меня установлено значение LF для EOL.

{
    ..........
    "files.eol": "\n", // символ конца строки как в linux
    ..........
}

Чтобы следить за символами конца строки — можно установить расширение «Render Line Endings», которое показывает символы LF и CRLF.

{
    ..........
    "editor.renderWhitespace": "all", // показывать символы пробелов
    "files.eol": "\n", // символ конца строки как в linux
    ..........
    "code-eol.newlineCharacter": "↓", // символ LF
    "code-eol.crlfCharacter": "←↓", // символы CRLF
    // подсвечивать как ошибку EOL в файле, если не совпадает с настройкой files.eol
    "code-eol.highlightNonDefault": true,
}

Когда в проект случайно попадёт файл с CRLF символами конца строки — эти символы будут подсвечены красным цветом (вообще, цветом errorForeground темы).

Но такая подсветка будет всего секунду, потому что у меня еще настроено автосохранение открытых файлов — и файл будет сохранен с окончаниями LF.

{
    ..........
    "editor.renderWhitespace": "all", // показывать символы пробелов
    "files.eol": "\n", // символ конца строки как в linux
    "files.autoSave": "afterDelay", // автоматическое сохранение файла
    "files.autoSaveDelay": 1000, // задержка перед сохранением файла
    ..........
    "code-eol.newlineCharacter": "↓", // символ LF
    "code-eol.crlfCharacter": "←↓", // символы CRLF
    // подсвечивать как ошибку EOL в файле, если не совпадает с настройкой files.eol
    "code-eol.highlightNonDefault": true,
}

Чтобы настройки VS Code всегда были правильными, можно создать файл .editorconfig в корне проекта и установить расширение «EditorConfig for VS Code». Расширение читает файл .editorconfig и устанавливает правильные настройки VS Code.

# эта настройка должна быть в самом начале; если установлена в true,
# парсер не будет искать другие конфиги родительских директориях
root = true

# правила для текстовых файлов
[*.{txt,md,html,css,scss,js,jsx,ts,tsx,py,php,json,xml,sh}]
# кодировка файлов
charset = utf-8
# концы строк как в linux
end_of_line = lf
# пустая строка в конце файла
insert_final_newline = true
# удалять пробелы в конце строк
trim_trailing_whitespace = true
# заменять табуляцию на пробелы
indent_style = space
# табуляция заменяется 4 пробелами
indent_size = 4
{
    ..........
    "files.encoding": "utf8", // кодировка файлов
    "files.eol": "\n", // концы строк как в linux
    "files.insertFinalNewline": true, // пустая строка в конце файла
    "files.trimTrailingWhitespace": true, // удалять пробелы в конце строк
    "editor.insertSpaces": true, // заменять табуляцию на пробелы
    "editor.tabSize": 4, // табуляция заменяется 4 пробелами
    ..........
}

Еще лучше — разместить файл .editorconfig в корне директории, которая содержит все проекты, над которыми идет работа. Тогда при открытии любого проекта VS Code будет подхватывать этот файл и его не надо будет создавать отдельно для каждого проекта.

Работа в команде

В настоящее время настройку core.autocrlf использовать нежелательно. На смену ей пришел файл .gitattributes в корне рабочей директории проекта, который нужно добавить под наблюдение Git.

*   text=auto
$ git add .gitattributes
$ git commit -m "Add .gitattributes"

Тем самым мы говорим Git, чтобы он самостоятельно определял текстовые файлы и заменял CRLF на LF при записи в репозиторий. Это эквивалентно установке core.autocrlf=true в файле конфигурации, но файл .gitattributes имеет приоритет над файлом конфигурации.

Таким образом, у всех разработчиков, которые работают над одним проектом, будет одинаковое поведение Git при записи в репозиторий. А вот настройка core.eol у каждого разработчика будет своя, из файла конфигурации на компьютере. И извлекать файлы в рабочую директорию разработчик может с любыми окончаниями — LF или CRLF.

Если файла .gitattributes нет — Git по старинке будет использовать core.autocrlf из файла конфигурации для замены символов EOL.

Если случилась беда

Все-таки это произошло — в репозиторий попали CRLF файлы. Проверить это можно с помощью команды

$ git ls-files --eol
i/crlf  w/crlf  attr/                   file-crlf-one.txt
i/crlf  w/crlf  attr/                   file-crlf-two.txt
i/lf    w/lf    attr/                   file-lf-one.txt
i/lf    w/lf    attr/                   file-lf-two.txt

Первая колонка — окончания строк в репозитории, вторая колонка — окончания строк в рабочей директории. Такая команда может выдать несколько тысяч строк, а нам интересно — есть ли вообще в репозитории такие файлы, так что нужен фильтр.

$ git ls-files --eol | grep "i/crlf"
i/crlf  w/crlf  attr/                   file-crlf-one.txt
i/crlf  w/crlf  attr/                   file-crlf-two.txt

Давайте наведем порядок — создадим файл .gitattributes, добавим его в репозиторий, выполним команду нормализации EOL в репозитории.

*   text=auto
$ git add .gitattributes
$ git commit -m "Add .gitattributes"
[master 347c98e] Add .gitattributes
 1 file changed, 1 insertion(+)
 create mode 100644 .gitattributes
$ git add --renormalize .
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   file-crlf-one.txt
        modified:   file-crlf-two.txt
$ git commit -m "Normalize eol"
[master e54c4b7] Normalize eol
 2 files changed, 4 insertions(+), 4 deletions(-)

Смотрим, что у нас теперь в репозитории — все хорошо, все окончания строк сейчас LF:

$ git ls-files --eol
i/none  w/none  attr/text=auto          .gitattributes
i/lf    w/crlf  attr/text=auto          file-crlf-one.txt
i/lf    w/crlf  attr/text=auto          file-crlf-two.txt
i/lf    w/lf    attr/text=auto          file-lf-one.txt
i/lf    w/lf    attr/text=auto          file-lf-two.txt

Теперь надо заменить файлы в рабочей директории, для этого выполняем две команды:

$ git rm --cached -r .
rm '.gitattributes'
rm 'file-crlf-one.txt'
rm 'file-crlf-two.txt'
rm 'file-lf-one.txt'
rm 'file-lf-two.txt'
$ git reset --hard
HEAD is now at e54c4b7 Normalize eol

Смотрим, что у нас теперь в рабочей директории (у меня Windows и core.eol установлена в native):

$ git ls-files --eol
i/none  w/none  attr/text=auto          .gitattributes
i/lf    w/crlf  attr/text=auto          file-crlf-one.txt
i/lf    w/crlf  attr/text=auto          file-crlf-two.txt
i/lf    w/crlf  attr/text=auto          file-lf-one.txt
i/lf    w/crlf  attr/text=auto          file-lf-two.txt

Дополнительно

Поиск: Git • Linux • Web-разработка • Windows • Конфигурация • Настройка • EOL • CRLF • LF • Файл • IDE

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.