Попытка исправить концы строк с помощью git filter-branch, но безуспешно

Меня укусила проблема с окончанием строки Windows / Linux с помощью git. Судя по GitHub, MSysGit и другим источникам, лучшее решение - настроить локальные репозитории на использование окончаний строк в стиле Linux, но установить core.autocrlf на true. К сожалению, я не сделал этого достаточно рано, поэтому теперь каждый раз, когда я вытягиваю изменения, концы строк сбиваются.

Я думал, что нашел ответ здесь, но я не могу заставить его работать у меня. Мои знания командной строки Linux в лучшем случае ограничены, поэтому я даже не уверен, что делает строка «xargs fromdos» в его скрипте. Я продолжаю получать сообщения об отсутствии такого файла или каталога, и когда мне удается указать его на существующий каталог, он сообщает мне, что у меня нет разрешений.

Я пробовал это с MSysGit в Windows и через терминал Mac OS X.


person Brian Donahue    schedule 02.10.2009    source источник
comment
Я даже почти не могу проголосовать за эту ветку. +1 ++ за то, что он дает лучший ответ по данному вопросу.   -  person sjas    schedule 19.07.2012
comment
Согласен с Чарльзом. Однако в моем случае (с использованием Mac OS X 10.8) сработало ›git config core.autocrlf false, а не› git config core.autocrlf input   -  person user1045085    schedule 04.09.2013


Ответы (8)


Документация git для gitattributes теперь документирует другой подход для «исправления» или нормализации всех окончаний строк в вашем проект. Вот суть:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

Если какие-либо файлы, которые не следует нормализовать, отображаются в статусе git, отключите их текстовый атрибут перед запуском git add -u.

manual.pdf -text

И наоборот, для текстовых файлов, которые git не обнаруживает, нормализация может быть включена вручную.

weirdchars.txt text

При этом используется новый флаг --renormalize, добавленный в git v2.16.0, выпущенном в январе 2018 г. Для более старых версий git есть еще несколько шагов:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"
person Russ Egan    schedule 13.01.2011
comment
Не могли бы вы рассказать мне, какова цель git reset, пожалуйста? - person crdx; 13.09.2012
comment
заставляет git перестроить индекс, во время которого он сканирует каждый файл, чтобы предположить, является ли его двоичный файл. RM удаляет старый индекс, reset строит новый индекс. - person Russ Egan; 15.10.2012
comment
Когда бы я ни делал это, я опускал бит git reset, потому что результаты всегда были одинаковыми, делал я это или нет. git status после rm .git/index показывает мне все файлы, которые необходимо нормализовать, независимо от того, сбрасываю я или нет, поэтому я спросил. Может, с новыми версиями это и не нужно? - person crdx; 16.10.2012
comment
Хм. Кажется, что, делая git add ., я фактически делал то же самое. - person crdx; 16.10.2012
comment
Спасибо. Это сэкономило мне несколько часов на объединение измененных файлов;) - person Alex B.; 14.02.2013
comment
Спасибо, у меня это сработало. Полезная команда после запуска git status - запустить git diff --ignore-space-at-eol, чтобы быть уверенным, что единственные изменения, которые вы фиксируете, - это окончания строки. - person zelanix; 02.05.2014
comment
Отличное решение. Мне пришлось временно изменить настройку core.safecrlf=true на false перед запуском git add -u, а затем снова вернуть значение true. Я сделал это в репозиториях, в которых делал это, запустив git config core.safecrlf false && git add -u && git config --unset core.safecrlf вместо строки $ git add -u, которую написал Расс. Это оставило мою глобальную настройку как core.safecrlf = true, потому что моя однострочная строка устанавливает только локальную (неглобальную) конфигурацию git, а затем сбрасывает ее, когда готово. - person likethesky; 15.08.2014
comment
Этот подход выглядит лучше, поскольку он не пытается исправить двоичные файлы. - person Sergey Shcherbakov; 20.11.2014
comment
Примечание. Единственная реальная разница между этим и старым решением заключается в наличии .gitattributes (с соответствующим содержимым). Без этого git reset не обнаружит никаких модификаций и, следовательно, бесполезен. - person Rob; 31.05.2015
comment
Важно: с помощью этой новой процедуры вы можете даже git reset --hard в качестве третьего шага. Вопреки тому, что можно было ожидать, жесткий сброс по-прежнему указывает на измененное состояние файлов (git status), и полный сброс может быть эффективным ТОЛЬКО, когда .gitattributes удален или его части не прокомментированы (git 1.9). - person Rob; 31.05.2015
comment
Я новичок в использовании Git (до сих пор использовал SVN в течение многих лет). Два вопроса: а) reset или reset --hard сотрут все мои изменения, верно? б) Если я верю, и я создаю резервную копию своих изменений, я снова представлю проблему после восстановления, верно? - person elysch; 08.04.2016
comment
Все зависит от того, о каких изменениях вы говорите. Эту процедуру лучше всего выполнять, когда ваш рабочий каталог чист. Другими словами, перед этим запустите git status. Если он показывает какие-либо ожидающие изменения, сначала зафиксируйте их. Эта процедура затем приведет к новой фиксации поверх той, которая просто нормализует окончание вашей строки. Вы не потеряете никаких других изменений. - person Russ Egan; 11.04.2016
comment
Перенос проекта из TFS в Git и основные проблемы с окончанием строк, внезапное изменение файлов, которые нельзя было отменить и т. Д. Пробовали кучу решений, но это на самом деле решило! - person jaspernygaard; 23.12.2016
comment
Инструкции на странице gitattributes были обновлены, чтобы использовать флаг --renormalize, добавленный в git v2.16.0, выпущенный в январе 2018 года. Флаг --renormalize объединяет процесс повторной обработки окончаний строк для каждого отслеживаемого файла в одну команду: git add --renormalize .. - person Mike Hill; 08.02.2018
comment
Спасибо, Майк. Обновил ответ. - person Russ Egan; 09.02.2018

Самый простой способ исправить это - сделать одну фиксацию, которая исправляет все окончания строк. Предполагая, что у вас нет измененных файлов, вы можете сделать это следующим образом.

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# Re-add all the deleted files to the index
# (You should get lots of messages like:
#   warning: CRLF will be replaced by LF in <file>.)
git diff --cached --name-only -z | xargs -0 git add

# Commit
git commit -m "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .
person CB Bailey    schedule 02.10.2009
comment
Я получил такую ​​же рекомендацию от Github. Не в восторге от этого, но переписывание истории тоже заставляет меня нервничать. Я могу пока просто воспользоваться этим решением. - person Brian Donahue; 04.10.2009
comment
Я на самом деле отмечу ваш ответ как исправление, потому что я выбрал именно его. Это было единственное устройство, которое действительно получало все файлы сразу. Другие, казалось, получали только несколько файлов за раз, и каждое извлечение и проверка открывали больше файлов, которые нужно было изменить. Спасибо! - person Brian Donahue; 05.10.2009
comment
P.S. Я порекомендовал ваше исправление ребятам с github.com, и они обновили свое справочное руководство, чтобы использовать ваше решение (ранее он просто рекомендовал новый клон и полный сброс, который, похоже, не получил все файлы). help.github.com/dealing-with-lineendings - person Brian Donahue; 05.10.2009
comment
Спасибо ... это отличное исправление. Нашел на GitHub. - person PHLAK; 05.11.2009
comment
Вы также можете проверить config.safecrlf, чтобы убедиться, что вы не меняете crlf в нетекстовых файлах (таких как двоичные). Проверьте это в документации kernel.org/pub/ software / scm / git / docs / git-config.html. - person vrish88; 23.07.2010
comment
@ vrish88: Однако, если вы попали в такую ​​ситуацию, вы, вероятно, будете страдать от смешанных концовок, и core.safecrlf может фактически помешать вам делать то, что вам нужно. Наверное, проще не использовать safecrlf. git не часто ошибается при обнаружении двоичного файла, и если это так, вы можете вручную пометить его как двоичный с помощью атрибута .gitattribute и восстановить правильную версию из предыдущей фиксации. - person CB Bailey; 23.07.2010
comment
Кажется, вы хотите autocrlf = input в OSX / Unix и autocrlf = true в Windows - person Eric Bloch; 16.03.2011
comment
Новое решение, рекомендованное в Ответ Русса Игана ниже проще и не связан с такими страшными вещами, как удаление всего исходного кода, поэтому я бы очень рекомендовал людям использовать его, хотя это старое решение имеет 10 раз столько голосов! - person Porculus; 21.07.2011
comment
Просто будьте осторожны, когда некоторые файлы (например, тестовые данные) намеренно имеют смешанные окончания строк. - person ryenus; 04.01.2013
comment
Второй ответ Расса Игана ниже. У меня были неправильные окончания строк из TFS - ›миграция GIT, которые мне пришлось исправить. Вышеупомянутый подход пропустил большое количество файлов, в то время как, похоже, Русс - нет. К сожалению, я недостаточно знаю о Git, чтобы понять, почему это так. - person MisterZimbu; 03.06.2014
comment
Два примечания: 1) вы можете сделать git commit ... --no-verify, чтобы пропустить перехватчики перед фиксацией, 2) Я заметил (в Windows), что обычно мне приходится повторять процедуру дважды, чтобы действительно нормализовать все файлы ( ага, это довольно странно). См. мой ответ - person jakub.g; 04.12.2015
comment
Эти шаги помогут вам решить проблему с eol, но обратите внимание, что история файлов (git blame) будет повреждена. - person tuchk4; 22.08.2016

Моя процедура работы с окончаниями строк следующая (проверено в бою на многих репозиториях):

При создании нового репо:

  • поместите .gitattributes в самый первый коммит вместе с другими типичными файлами как .gitignore и README.md

При работе с существующим репо:

  • Создать / изменить .gitattributes соответственно
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n (--no-verify is to skip pre-commit hooks)
    • Мне приходится делать это достаточно часто, поэтому я определил это как псевдоним alias fixCRLF="..."
  • repeat the previous command
    • yep, it's voodoo, but generally I have to run the command twice, first time it normalizes some files, second time even more files. Generally it's probably best to repeat until no new commit is created :)
  • несколько раз переходите между старой (непосредственно перед нормализацией) и новой ветвью. После переключения ветки иногда git находит еще больше файлов, которые необходимо перенормировать!

В .gitattributes я явно объявляю все текстовые файлы как имеющие LF EOL , поскольку обычно инструменты Windows совместимы с LF, в то время как инструменты, отличные от Windows, несовместимы с CRLF (даже многие инструменты командной строки nodejs предполагают LF и, следовательно, могут изменять EOL в ваших файлах).

Содержание .gitattributes

Мой .gitattributes обычно выглядит так:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

Чтобы выяснить, какие отдельные расширения отслеживаются git в текущем репо, посмотрите здесь

Проблемы после нормализации

Как только это будет сделано, есть еще одно распространенное предостережение.

Допустим, ваш master уже обновлен и нормализован, а затем вы оформляете заказ outdated-branch. Часто сразу после проверки этой ветки git отмечает многие файлы как измененные.

Решение - сделать фальшивую фиксацию (git add -A . && git commit -m 'fake commit'), а затем git rebase master. После перебазирования фальшивая фиксация должна исчезнуть.

person jakub.g    schedule 04.12.2015
comment
Я думал, что схожу с ума, пока не прочитал ваш пост, потому что мне тоже приходилось запускать указанную последовательность команд несколько раз. Вуду! ;) - person Sean Fausett; 06.02.2016
comment
В git версии 2.7.0.windows.1 я использовал следующее: git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n - person Sean Fausett; 06.02.2016

git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Объяснение:

  • git status --short

    Это отображает каждую строку, о которой git знает и не знает. Файлы, которые не находятся под управлением git, помечаются в начале строки знаком «?». Измененные файлы помечаются буквой M.

  • grep "^ *M"

    Это отфильтровывает только те файлы, которые были изменены.

  • awk '{print $2}'

    Это показывает только имя файла без каких-либо маркеров.

  • xargs fromdos

    Это берет имена файлов из предыдущей команды и запускает их через утилиту fromdos для преобразования окончаний строк.

person Lloyd Moore    schedule 09.03.2012
comment
Это круто. Спасибо. Для тех, кто ищет решение с использованием Homebrew, используйте dos2unix вместо fromdos. - person Almir Sarajčić; 12.02.2017

Вот как я исправил все окончания строк во всей истории, используя git filter-branch. Символ ^M необходимо вводить с помощью CTRL-V + CTRL-M. Я использовал dos2unix для преобразования файлов, так как это автоматически пропускает двоичные файлы.

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
person pfrenssen    schedule 02.04.2015

«| Xargs fromdos» считывает из стандартного ввода (файлы, которые находит find) и использует его в качестве аргументов для команды fromdos, которая преобразует окончания строк. (Является ли fromdos стандартным для этих сред? Я привык к dos2unix). Обратите внимание, что вы можете избежать использования xargs (особенно полезно, если у вас достаточно файлов, и список аргументов слишком длинный для xargs):

find <path, tests...> -exec fromdos '{}' \;

or

find <path, tests...> | while read file; do fromdos $file; done

Я не совсем уверен в ваших сообщениях об ошибках. Я успешно опробовал этот метод. Какую программу производит каждый? Для каких файлов / каталогов у вас нет прав? Однако вот попытка угадать, что это может быть у вас:

Один из простых способов получить ошибку «файл не найден» для сценария - использовать относительный путь - используйте абсолютный. Точно так же вы можете получить ошибку разрешений, если вы не сделали свой скрипт исполняемым (chmod + x).

Добавляйте комментарии, и я постараюсь помочь вам с этим разобраться!

person Cascabel    schedule 02.10.2009
comment
Я видел другой пример с dos2unix и думал, что это каким-то образом копирует файлы в папку с таким именем, но теперь я понял. Вау, теперь это кажется очевидным. Спасибо за вашу помощь! - person Brian Donahue; 04.10.2009

хорошо ... в cygwin у нас нет легко доступного fromdos, и этот awk substeb взрывается вам в лицо, если у вас есть пробелы в путях к измененным файлам (которые у нас были), поэтому мне пришлось сделать это несколько иначе:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

Престижность @lloyd за большую часть этого решения

person Anton Kraievyi    schedule 30.04.2012

Выполните следующие действия, если ни один из других ответов вам не подходит:

  1. Если вы работаете в Windows, сделайте git config --global core.autocrlf true; если вы используете Unix, сделайте git config core.autocrlf input
  2. Запустите git rm --cached -r .
  3. Удалите файл .gitattributes
  4. Запустите git add -A
  5. Беги git reset --hard

Тогда ваш местный должен быть чистым.

person zs2020    schedule 06.05.2014
comment
Действительно? Удаление .gitattributes файла - это решение проблемы с окончанием строки? - person Aleksandr M; 07.05.2014
comment
Да, пожалуйста, обратитесь к комментарию @AleksandrM - person Mr_and_Mrs_D; 25.05.2014