Это почти наверняка окончания строк (CRLF против LF). Я рекомендую не настройка autocrlf=true
; если вам нужно настроить Git, чтобы связываться с окончаниями строк, используйте .gitattributes
для настройки. У меня нет единственной рекомендации «Правильного пути», потому что проблема, которая у вас есть, немного сложная и все решения имеют риски . Вам нужно знать, во что вы ввязываетесь, и выбирать свой собственный курс.
Если существуют зафиксированные файлы - которые вы не можете видеть напрямую; они хранятся только в формате Git - имеют окончания строк CRLF, отключение всех манипуляций сделает проблему vani sh. Будущие зафиксированные файлы будут по-прежнему иметь окончания строк CRLF. Возможно, вы захотите или не захотите использовать это решение.
Если у существующих зафиксированных файлов есть окончания строк только для LF, вы, вероятно, захотите включить при определенных манипуляциях. Вы можете захотеть этого в любом случае, наряду с одноразовой фиксацией для исправления всех файлов репозитория, но вы должны знать, что это делает и как это представляет все в будущем.
Когда вы включены система, использующая окончания строк CRLF, и вы указываете Git связываться с окончаниями строк, Git будет портить окончания строк, потому что вы сказали это. 1 Установка core.autocrlf
говорит Git: Угадай, что у меня есть в моих файлах, и, основываясь на твоих догадках, запутайся в конце строк. Это хорошо, потому что это легко, но плохо, потому что Git делает не обязательно угадывать правильно .
Используя .gitattributes
, вы можете пометить определенные файлы как текст и другие как двоичный и сообщить Git: для текстовых файлов, возиться с окончаниями моей строки; для двоичных вообще не трогайте их . Это гораздо больше работы, но и гораздо безопаснее: Git останавливается угадывает и делает то, что вы говорите. Вы также можете точно сказать, , как связываться с окончаниями строк.
* * * * * Проектные люди Git сами используют .gitattributes
, например:
$ head -8 .gitattributes
* whitespace=!indent,trail,space
*.[ch] whitespace=indent,trail,space diff=cpp
*.sh whitespace=indent,trail,space eol=lf
*.perl eol=lf diff=perl
*.pl eof=lf diff=perl
*.pm eol=lf diff=perl
*.py eol=lf diff=python
*.bat eol=crlf
который настолько близок, насколько я получу к конкретной рекомендации. Обратите внимание, что *.bat
(пакетные файлы) являются единственными файлами с eol=crlf
здесь.
1 Вы можете просто сказать Git никогда не связываться ни с чем и не будет. Именно так я предпочитаю его использовать, но я также избегаю систем, которые используют окончания строк CRLF, так что я никогда не сталкиваюсь с проблемой в первую очередь.
Почему проверка не работает
Вы пытались проверить этот вид бесполезной CRLF:
> mv StdAfx.cpp StdAfx.cpp.save
> git checkout StdAfx.cpp
> diff StdAfx.cpp StdAfx.cpp.save
> git diff -w StdAfx.cpp
[ничего не показывает]
, но это не очень помогает, потому что один из программы, которые помещают окончания строки CRLF, тем самым (потенциально) изменяя файл, сами по себе являются git checkout
! Эти два файла здесь не отличаются, потому что если git checkout
изменило их, оно изменило их каждый раз одинаково.
Основная справочная информация
Есть две вещи, которые Git может сделать с файлами, в двух разных фазах. Чтобы понять это правильно, вам нужно знать кое-что о том, как Git хранит файлы.
Вам нужно знать - возможно, вы уже это делаете - что каждый Git коммит хранит полную копию каждого файл, как снимок, но сохраняет его в специальном, Git только для чтения, сжатом и замороженном формате. Вы не можете увидеть эти файлы: вам нужно Git извлечь их, а затем вы можете проверить извлеченные файлы. Но Git должен преобразовать их во время извлечения.
Конечно, если бы все ваши файлы были заморожены навсегда и также не читались, Git был бы бесполезен. Так что Git делает извлечение ваших файлов в обычные повседневные файлы, которые вы можете перечислить, открыть в редакторе, проверить окончания строк CRLF и так далее. Эти полезные файлы образуют то, что Git называет ваше рабочее дерево или рабочее дерево . Но это преобразованные файлы. Они не то, что на самом деле в коммите. В довольно сильном смысле, эти файлы вообще не находятся в хранилище! Они просто существуют, чтобы вы могли выполнять работу.
Вам также нужно знать о Git index . Индекс, который Git также называет промежуточной областью , является промежуточным этапом как извлечения файлов, так и помещения файлов в новые коммиты. Мы пропустим многие детали, но учтите, что индекс действует как Git , предложенный для следующего коммита , и изначально заполняется путем копирования файлов замороженного формата из коммита, который вы извлекли , Содержимое индекса включает в себя:
- имя файла с косой чертой (вперед), например,
path/to/file.ext
; - режим файла:
+x
или -x
, хранится как mode 100755
или mode 100644
; и - через косвенное обращение через Git объект BLOB-объекта , который вам в большинстве случаев не требуется знать или заботиться о нем, содержимое файла , все еще в сжатом и Git -только формат.
В отличие от копии в коммите, индексная копия на самом деле не заморожена, так как вы можете заменить ее или даже удали это. Вот что делают git add
и git rm
: git add
, в частности, берет копию рабочего дерева и преобразует ее, сжимая в замороженный формат , готовый к go в следующем коммите.
Это означает, что индекс изначально содержит все файлы из текущий коммит. Вы управляете файлами рабочего дерева, копируете результаты обратно в индекс, а затем запускаете git commit
, и Git делает новый фиксированный коммит из данных index , а не из файлов рабочего дерева.
Обратите внимание, что есть два шага копирования:
- Один шаг - копирование из индекса в рабочее дерево .
git checkout
делает это: заполняет индекс и заполняет ваше рабочее дерево из индекса. - Другой шаг - копирование из рабочего дерева в индекс,
git add
делает это, например.
Каждый из этих двух шагов может вносить изменения . При установленном core.autocrlf
каждый делает .
Как Git изменяет окончания строк
Git может обрабатывать любой данный файл как text , что означает состоит из строк с окончанием строки или двоичного , что означает состоит из байтов, которые должны быть сохранены: руки прочь! ничего не трогая! Если установлено core.autocrlf
, Git будет угадывать, является ли файл текстовым или двоичным, проверяя некоторые или все его байты. (Примечание: различные версии Git имеют разные внутренние тесты).
Поскольку двоичные файлы просто не сжимаются без изменения их байтов, эти файлы безопасны. Git не изменяет их во время копирования. Так что здесь интересны только файлы text
.
Буквально только два изменения, которые Git может внести самостоятельно. Вы можете определить фильтры smudge и clean , если вы хотите больше изменений - они работают прямо в одном и том же месте во время копирования, но Git делает только две вещи:
- При копировании из индекса в рабочее дерево Git может заменить окончания строк только LF на окончания строк CRLF.
- При копировании из рабочего дерева в индекс Git может заменить CRLF окончания строк с окончаниями строк только для LF.
Это все Git может сделать самостоятельно. Если вы установите core.autocrlf
, Git сделает оба.
Если вы используете запись .gitattributes
, вы получите более точный контроль. Первая часть - это шаблон имени файла; *
соответствует каждому файлу или *.txt
соответствует файлам, имя которых заканчивается на .txt
. Остальные элементы управления. Обратите внимание, что text
во всех случаях говорит Git: не связываться с файлом . eol=
сообщает Git , как связываться с файлом:
*.txt text eol=crlf
Заменить LF-only CRLF во время индекса -> рабочее дерево и CRLF с LF-only во время рабочего дерева -> индекс.
*.txt text eol=lf
Сохранить файл во время индекса -> работа- дерево, но поверните CRLF в LF во время рабочего дерева -> индекс.
Существует более старый набор атрибутов, который вы можете записать как, например:
*.txt crlf
*.jpg -crlf
*.sh crlf=input
Они сообщают Git то же самое, что и *.txt text eol=crlf
, *.jpg -text
и *.sh text eol=lf
. Вы должны использовать их, только если ваша версия Git слишком старая, чтобы понимать директивы text eol=
. Проверьте вывод git help attributes
вашей конкретной установки.
Предостережения
Все существующие коммиты замораживаются на все время. Если некоторые файлы в этих коммитах фиксируются с окончаниями строки CRLF, они всегда таковы, в этих коммитах .
Если вы выберете eol=crlf
в качестве параметра, Будущее фиксация, при которой вы вообще изменили файл, изменит все строки, чтобы иметь окончания строк только для LF внутри подтвержденной копии - копия вас не могу увидеть. Это потому, что когда вы git add
обновляете свой файл, Git превратит все окончания CRLF в окончания только для LF, поскольку он заменит сохраненный файл в индексе.
Извлечение либо старый (CRLF-окончание) или новый (только LF) коммит создадут файлы рабочего дерева с окончаниями строк CRLF. Хотя Git скажет, что файлы, измененные в коммите, которые удалили все возвраты каретки - и они действительно изменились - вы не сможете понюхать изменения в вашем рабочем дереве, потому что оно там не произойдет.
Если зафиксированная копия файла имеет окончания строк CRLF, но вы не трогаете копию файла рабочего дерева, Git не будет копировать рабочее дерево обратно в индексную копию - по крайней мере, не нормально. Таким образом, индексная копия, которая в буквальном смысле точно такая же, как и зафиксированная копия - у них общий внутренний объект Git blob - все равно будет иметь окончания строки CRLF, , даже если ваш .gitattributes
говорит eol=crlf
, подразумевая добавление файла (копирование его обратно в индекс) удалит возврат каретки.
Более новая версия Git имеет git add --renormalize
, чтобы вы могли повторно применить обновленный .gitattributes
.
Во многих версиях Git (хотя я думаю, что это могло быть исправлено в последних версиях), изменение файла .gitattributes
не сигнализирует Git, чтобы проверить, изменились ли настройки eol=
, Для этого есть простой обходной путь: удалите или переименуйте файл и повторно извлеките его, как вы делали это ранее, пытаясь проверить окончания строк CRLF.
То есть после изменения .gitattributes
копия в вашем рабочем дереве, вам может понадобиться уловка Git для повторного извлечения (путем удаления или переименования файла, затем с помощью git checkout
или git restore
для копирования из индекса в опять дерево работы). Вы можете аналогичным образом обмануть Git, чтобы повторить git add
, используя touch
или аналогичный файл для обновления своей отметки времени, а затем git add
файл. (Без --renormalize
и / или исправлений ошибок Git не замечает, что новое рабочее дерево -> индексная копия на этот раз сделает что-то другое.)