Директива eol=lf
предотвратит добавление Git символов возврата каретки, но не помешает Git сохранить существующие символы возврата каретки.
Чтобы по-настоящему понять, что здесь происходит, требуется немного знаний о том, как Git хранит файлы внутри коммитов. Ключи к этому:
Каждый коммит хранит полный снимок каждого файла в формате только для чтения, сжатом, только для Git и без дублирования. Это означает, что файлы, которые вы на самом деле видите и с которыми работаете, находятся не в репозитории: файлы, которые вы используете, находятся в вашем рабочем дереве или рабочем дереве< /эм>.
Все части любого коммита, включая все его файлы, буквально неизменяемы. Если вы берете внутренний объект Git (включая фиксацию) из репозитория, каким-то образом изменяете его и кладете обратно, вы не меняете оригинал, а просто добавляете еще один, и этот новый получает другой хэш-идентификатор.
Чтобы получить файлы из коммита в ваше рабочее дерево, Git должен их скопировать. Это очевидно; что не очевидно, так это то, что существует третья копия каждого файла. Эта третья — или, на самом деле, средняя — копия находится в том, что Git называет индексом, или индексом, или — редко в наши дни — в кэше. Все три имени относятся к одному и тому же объекту.
То есть предположим, что HEAD
присоединено к имени ветки master
, а master
в настоящее время представляет фиксацию, хэш-идентификатор которой равен a123456...
. Другими словами, этот коммит — с его большим уродливым хэш-идентификатором — является вашим текущим коммитом. Внутри этой фиксации у нас есть файлы с именами README.md
и main.py
и — в вашем случае — web/migrate.sh
. Существует три копии этого файла. Копии здесь заключены в кавычки, потому что две из них находятся в формате с автоматическим удалением дубликатов, поэтому на самом деле существует только одна базовая копия.
Мы можем проиллюстрировать эти три копии в таблице, используя специальное имя HEAD
для ссылки на коммит a123456...
(текущий коммит):
HEAD index work-tree
-------------- -------------- --------------
README.md README.md README.md
main.py main.py main.py
web/migrate.sh web/migrate.sh web/migrate.sh
Откуда взялись эти файлы? Что ж, когда вы впервые клонируете репозиторий, ваш Git получает все коммиты от какого-то другого Git. Эти коммиты абсолютно одинаковы в каждом Git и имеют одинаковые хэш-идентификаторы во всех Git. Затем ваш Git копирует один из этих коммитов — тот, который вы проверяете, — в индекс вашего Git и копирует файлы из своего индекса в ваше рабочее дерево. Вот где у вас есть три копии каждого файла.
Файлы рабочего дерева — это обычные повседневные файлы, которые вы можете читать и записывать с помощью любой программы на вашем компьютере. Другие файлы нет. Когда (или после) вы выполнили некоторую работу с копией рабочего дерева одного из ваших файлов, вы запускаете на ней git add
. Причина этого в том, что git add
сообщает Git: сделайте копию индекса соответствующей копии рабочего дерева. Например, если вы изменили main.py
, версия main.py
в индексе теперь отличается от версии main.py
в репозитории:
HEAD index work-tree
-------------- -------------- --------------
README.md(1) README.md(1) README.md
main.py(1) main.py(2) main.py
web/migrate.sh(1) web/migrate.sh(1) web/migrate.sh
Копия, которая находится в коммите, буквально неизменяема, поэтому HEAD
— что на данный момент является сокращением от commit a123456...
— всегда будет содержать эти три версии файлов. Но индекс, хотя и использует внутренний формат, не является фиксацией1 и не доступен только для чтения. Таким образом, git add
может заменить копию индекса.
(Выполнение git commit
берет все, что есть в индексе, и использует его для создания новой фиксации. Новая фиксация затем становится текущей фиксацией, так что имя HEAD
и имя текущей ветки теперь относятся к новый коммит вместо коммита a123456...
. Но нам пока не нужно заходить так далеко.)
1Что это такое, это, немного сложно, но в первом приближении вы можете думать об индексе как о хранении вашего предлагаемого следующего коммита. Каждый раз, когда вы проверяете какую-либо фиксацию, Git должен настроить индекс так, чтобы он был готов к следующей фиксации: обычно, заполняя его из только что извлеченной фиксации.
Копирование из индекса или в него происходит, когда Git корректирует окончания строк.
Копия файла в индексе Git находится в сжатом, предназначенном только для Git, дедуплицированном формате. Копия файла в вашем рабочем дереве имеет обычный повседневный компьютерный формат. Таким образом, каждый раз, когда Git копирует из Git-индекса в ваше рабочее дерево, он должен расширить файл; и каждый раз, когда Git копирует из вашего рабочего дерева в его индекс, он должен сжимать и удалять дубликаты файла.
Этот процесс копирования — идеальное время для внесения любых изменений в файл, которые вы хотите. Вот тут-то и вступают в игру .gitattributes
и окончание строки. Предположим, что файл в индексе, который попал туда, находясь в репозитории, имеет строки, заканчивающиеся новой строкой, только с \n
. Предположим, однако, что вы хотите, чтобы ваша копия файла work-tree имела окончание строки \r\n
или CRLF.
Если Git превращает \n
в \r\n
на пути из индекса и превращает \r\n
в \n
на пути входа в индекс, это достигает вашей цели. Это то, что * text eol=crlf
сделает.
Но что, если вы этого не хотите? Что, если вы хотите, чтобы \n
концовки оставались \n
концовками? Это то, что * text eol=lf
сделает. Как \n
окончания остаются \n
окончаниями? не внося никаких изменений.
Таким образом, * text eol=lf
означает не вносить изменения. Но что, если файл внутри репозитория, который поэтому копируется в индекс, имеет окончания строки \r\n
(CRLF)? Что ж, тогда то же самое можно сказать и о вашем файле рабочего дерева.
Чтобы некоторые файлы в репозитории имели окончание строки, состоящее только из \n
, вам необходимо:
- удалить
\r
из копий рабочего дерева;
git add
результирующие файлы; а также
git commit
, чтобы сделать новый коммит.
Затем этот новый коммит можно распространить на все другие копии этого репозитория и использовать вместо существующего (плохого) коммита, который имеет окончания \r\n
(CRLF) для этих файлов.
Обратите внимание, что плохая фиксация будет продолжать существовать: в этом и заключается контроль версий. Мы не устраняем плохие, потому что они есть и у всех остальных, и мы будем помнить, что они используют плохой.
Теперь, если ни у кого больше нет копии этого репозитория или есть неверная фиксация, мы находимся в особой ситуации. В этом случае мы можем отказаться от плохой фиксации в пользу новой улучшенной фиксации. (Как именно это сделать в Git — это тема для другого ответа.) Но в целом мы просто добавляем исправление, а оригинал сохраняем.
person
torek
schedule
27.09.2020
git config --global core.autocrlf input
выдаст вам LF при оформлении заказа. - person zrrbite   schedule 27.09.2020