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

270

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

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

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

Брайан Донахью
источник
Я не могу одобрить эту тему даже почти достаточно. +1 ++ за это, предоставляя лучший ответ по этому вопросу.
Sjas
Согласитесь с Чарльзом. Однако в моем случае (с использованием Mac OS X 10.8)> git config core.autocrlf false работал, а не> вход git config core.autocrlf
user1045085

Ответы:

187

В документации 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"
Русь Иган
источник
1
Не могли бы вы сказать мне, какова цель этого git reset, пожалуйста?
crdx
1
заставляет git перестроить индекс, во время которого он просматривает каждый файл, чтобы угадать, является ли он двоичным. Команда rm удаляет старый индекс, а сброс создает новый индекс.
Расс Иган
16
Спасибо, это сработало для меня. Полезной командой после запуска git statusявляется запуск git diff --ignore-space-at-eolтолько для того, чтобы быть уверенным, что единственными изменениями, которые вы вносите, являются окончания строк.
Zelanix
1
Примечание. Единственная «реальная» разница между этим и «старым» решением заключается в наличии .gitattributes (с соответствующим содержимым). Без этого не git resetбудет обнаружено никаких модификаций, и, следовательно, бесполезно.
Роб
3
Инструкции по gitattributes страницы были обновлены , чтобы воспользоваться --renormalizeфлагом добавленного в мерзавце v2.16.0 , который был выпущен в январе 2018 года --renormalizeфлаг консолидирует процесс повторных обработки окончаний строк для каждого гусеничного файла в одну команду: git add --renormalize ..
Майк Хилл
389

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

# 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 .
CB Bailey
источник
7
PS Я порекомендовал ваше исправление ребятам на github.com, и они обновили свое справочное руководство по использованию вашего решения (ранее оно только рекомендовало свежий клон и полный
Брайан Донахью
31
Спасибо ... это отличное решение. Нашел это на GitHub.
PHLAK
4
Вы также можете проверить config.safecrlf, чтобы убедиться, что вы не меняете crlfs в нетекстовых файлах (таких как двоичные файлы). Проверьте это в документации к kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
4
@ vrish88: Тем не менее, если вы находитесь в такой ситуации, вы, скорее всего, будете страдать от смешанных окончаний и core.safecrlf может фактически помешать вам делать то, что вам нужно. Вероятно, проще не использовать safecrlf. git не часто неправильно распознает двоичные файлы, и если это так, вы можете вручную пометить его как двоичный файл с помощью .gitattribute и восстановить правильную версию из предыдущего коммита.
CB Bailey
26
Более новое решение, рекомендованное в ответе Расса Игана ниже, является более простым и не включает в себя такие страшные вещи, как удаление всего вашего исходного кода , поэтому я очень рекомендую людям использовать его, даже если это старое решение имеет в 10 раз больше голосов!
Porculus
11

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

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

  • положить .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это пропустить предварительную фиксацию хуков)
    • Я должен делать это достаточно часто, чтобы я определил его как псевдоним alias fixCRLF="..."
  • повторите предыдущую команду
    • да, это voodoo, но обычно мне приходится запускать команду дважды, первый раз нормализует некоторые файлы, второй - еще больше файлов. Как правило, лучше повторить, пока не будет создан новый коммит :)
  • переходите назад и вперед между старой (непосредственно перед нормализацией) и новой веткой несколько раз. После переключения ветки, иногда 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 помечает многие файлы как измененные.

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

jakub.g
источник
1
Я думал, что схожу с ума, пока не прочитал твой пост, потому что мне тоже приходилось запускать указанную последовательность команд несколько раз. Voodoo! ;)
Шон Фосетт
С git-версией 2.7.0.windows.1я использовал следующее: git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n
Шон Фосетт
4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Объяснение:

  • git status --short

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

  • grep "^ *M"

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

  • awk '{print $2}'

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

  • xargs fromdos

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

Ллойд Мур
источник
Это круто. Спасибо. Для тех, кто ищет решение, используя Homebrew dos2unixвместо fromdos.
Альмир Сарайчич
4

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

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
pfrenssen
источник
3

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

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

или

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

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

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

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

Cascabel
источник
Я видел другой пример с dos2unix и думал, что это каким-то образом копирует файлы в папку с именем, но теперь я понял. Вау, кажется очевидным сейчас. Спасибо за вашу помощь!
Брайан Донахью
1

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

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

спасибо @lloyd для большей части этого решения

Антон К
источник
-2

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

  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

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

zs2020
источник
4
В самом деле? Удаление .gitattributesфайла является решением проблемы с окончаниями строк?
Александр М
Да, пожалуйста, отправьте комментарий @AleksandrM
Mr_and_Mrs_D