Git: Как восстановить 2 файлов, которые упорно застрял в «Changed, но не совершенные»?

84

У меня есть репо, в котором есть два файла, которые я предположительно изменил локально.

Итак, я застрял в этом:

$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dir1/foo.aspx
#       modified:   dir2/foo.aspx
#
no changes added to commit (use "git add" and/or "git commit -a")

Doing git diffговорит, что все содержимое файла изменилось, даже если на глаз это кажется неправдой (похоже, есть общие диапазоны строк, которые diff, похоже, не видит).

Что интересно, я не помню, чтобы эти файлы менялись локально. Это репо используется с одним удаленным репо (частным, на GitHub.com, FWIW).

Что бы я ни пробовал, я не могу отказаться от этих локальных изменений. Я пробовал все:

$ git checkout -- .
$ git checkout -f
$ git checkout -- dir1/checkout_receipt.aspx
$ git reset --hard HEAD
$ git stash save --keep-index && git stash drop
$ git checkout-index -a -f

Другими словами, я испробовал все, что описано в разделе Как отменить неустановленные изменения в Git?плюс еще. Но 2 файла остаются "измененными, но не зафиксированными".

Что, черт возьми, могло привести к тому, что два файла застряли вот так и, казалось бы, "не вернули таблицу" ??

PS В приведенном выше списке с командами, которые я уже пробовал, я ошибочно написал, git revertкогда имел в виду git checkout. Прошу прощения и благодарю тех из вас, кто ответил, что я должен попробовать checkout. Я отредактировал вопрос, чтобы исправить. Я определенно уже пробовал checkout.

Грег Хендершотт
источник
Есть ли git diff --ignore-space-changeили git diff --ignore-all-spaceсделать разницу в выходе git diff?
jdd
@jermiahd Да! При любом флаге означает, git diffчто файлы идентичны.
Грег Хендершотт,
2
Возможный дубликат stackoverflow.com/questions/2016404/… . В любом случае мне больше нравится принятый ответ, который должен быть установлен git config --global core.autocrlf falseвместо «истина».
Иоганн
2
Ответ [здесь] [1] сработал для меня и многих других. [1]: stackoverflow.com/questions/2016404/…
Майк К.
2
Это также происходит, когда репозиторий, содержащий 2 или более файлов в одном каталоге с разным регистром, извлекается в файловой системе без учета регистра. Удалите или переименуйте один из файлов, чтобы решить проблему.

Ответы:

34

Какие окончания строк в файлах? Держу пари, что они CRLF. Если да, ознакомьтесь с этим руководством: http://help.github.com/line-endings/

Короче говоря, вам нужно убедиться, что git настроен на преобразование окончаний строк в LF при фиксации, а затем зафиксировать эти файлы. Файлы в репо всегда должны быть LF, извлеченные файлы должны быть собственными ОС, если вы правильно установили git.

Теккуб
источник
1
Благодарю. У меня уже есть, как git config --global core.autocrlf trueи другая сторона, продвигающая репо на GitHub.
Грег Хендершотт
1
Затем вам просто нужно выполнить биты в последнем <pre>блоке этого руководства, чтобы исправить файлы в репо.
Tekkub
5
Я не согласен с тем, что окончания строк всегда должны быть LF в репо (особенно если кто-то уже зафиксировал CRLF), а также что ОС всегда должна быть родной. Мой редактор Windows и среда (в основном для PHP, HTML, CSS и т. Д.) Отлично справляются с окончанием строк LF.
Саймон Ист,
Гениальный ответ, я забыл, что недавно использовал gitattributes для принудительного LF в файлах репо и не ожидал, что git автоматически изменит файл. У нас есть смешанные разработчики Windows и Linux, и это сводило нас с ума, поскольку редакторы на разных платформах продолжали отключать терминаторы строк, как только изменение пронизывает все это, это должно исчезнуть.
Оливер Данжи,
121

Я провел часы, пытаясь решить подобную проблему - удаленный филиал, что я проверил, что упорно показывал четыре файла, как «Измененные но не обновляется», даже при удалении всех файлов и запуска git checkout -f снова (или другие вариации этого поста)!

Эти четыре файла были необходимы, но я определенно не изменял их. Мое окончательное решение - убедить Git, что они не были изменены. Следующее работает для всех извлеченных файлов, показывая статус «изменено» - убедитесь, что вы уже зафиксировали / сохранили все, что действительно было изменено !:

git ls-files -m | xargs -i git update-index --assume-unchanged "{}"

Однако в Mac OSX xargs работает немного иначе (спасибо Дэниелу за комментарий):

git ls-files -m | xargs -I {} git update-index --assume-unchanged {}

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

-Al

Алан Форсайт
источник
10
У меня было несколько упрямых файлов, и я выполнил эту команду, git status теперь не меняет, но когда я пытаюсь изменить ветку, git все равно говорит мне, что я не могу изменить ветку, потому что эти два файла имеют локальные изменения. Не уверен, что я сделал не так, но казалось, что это просто скрыло проблему, а не исправило ее? Я также не смог зафиксировать файлы после выполнения этой команды. Решением для меня было удалить их, зафиксировать и поменять местами ветки.
RodH257 07
5
благодаря! Я пробовал ВСЕ трюки, упомянутые в каждом другом ответе, который я мог найти, - ни один из них не работал. на Mac нельзя было использовать строку как есть, просто запускал git update-index --assume-unchanged <filename> для каждого файла, и это решило проблему.
Йонатан Карни
6
Это именно то, что мне нужно, хотя xargs на Mac, похоже, работает немного иначе (я использую 10.10 Yosemite). Это, наконец, сработало для меня:git ls-files -m | xargs -I {} git update-index --assume-unchanged {}
Дэниел
4
Чтобы отменить действие команды:git ls-files -v|grep '^h' | cut -c3- | xargs -i git update-index --no-assume-unchanged "{}"
Marinos An
3
Это решение не устраняет проблему. Он это скрывает. Есть несколько вещей, которые нельзя выполнить после assume-unchaged, как в случае с @ RodH257. Я считаю, что наиболее правильным ответом в случае, когда команды нравятся git checkout -- file, git stashа git reset --hard HEADне работают, является, как уже было .gitattributes
Marinos An
20

вот как я исправил ту же проблему в моем случае: open .gitattributes change:

* text=auto

кому:

#* text=auto

сохранить и закрыть, затем вернуться или сбросить, спасибо @Simon East за подсказку

Абу Ассар
источник
1
Удаление text=autoпараметра в .gitattributes сработало для меня, а затем после того, как я git reset --hardвернул этот параметр, файлы больше не отображались как измененные!
ErikE
1
Очевидно, что-то не так с этой text=autoнастройкой. Я работаю в репозиториях с коммитами из нескольких ОС, и я до сих пор не понял, что вызывает у меня больше проблем: сохранить или отбросить.
Маринос Ан
1
@Marinos: Да, есть, в частности, git позволяет оставлять существующие текстовые файлы с неправильным окончанием строк при первом добавлении этого параметра. Это просто неправильно, и если вы не забудете сделать это самостоятельно, вы в конечном итоге столкнетесь с одним из этих необратимых изменений.
Роман Старков
12

Другая возможность заключается в том, что разница (которая не позволяет вам вернуть эти файлы с помощью команды проверки) является одним из файлового режима. Вот что случилось со мной. В моей версии git вы можете обнаружить это, используя

git diff dir1 / foo.aspx

И он покажет вам изменения режима файла. Однако он по-прежнему не позволит вам их вернуть. Для этого используйте либо

git config core.filemode false

или измените свой git .config в текстовом редакторе, добавив

[core]

filemode = false

После этого вы можете использовать

git сбросить HEAD dir1 / foo.aspx

и файл должен исчезнуть.

(Я получил все это из ответа на вопрос, как заставить git игнорировать изменения режима (chmod)? )

Эяль
источник
1
Если вы используете Windows, диагностика / решение Эяля должны быть вашим первым предположением
AlcubierreDrive
Будьте особенно осторожны, чтобы никогда не использовать cygwin git из cmd.exe. Если вы хотите использовать git в cmd.exe, установите msysgit.
AlcubierreDrive
Просто чтобы подтвердить, что это действительно проблема в Windows.
Деян Марьянович
Для меня в Windows это не было проблемой ( core.filemodeуже было установлено значение false). В моем случае исправление / обходное решение было тем, что было в ответе Алана Форсайта .
Venryx
3

Попробуйте отменить локальные изменения :

git checkout -- dir1/foo.aspx
git checkout -- dir2/foo.aspx
Тадек
источник
У меня в голове было «откат», и я хотел писать checkout. Я уже пробовал checkout. В любом случае спасибо за ваш ответ. Это был хороший ответ на мой первоначальный вопрос, поэтому я буду поддерживать его.
Грег Хендершотт
3

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

Выполнение этой команды иногда срабатывает:
(Отключает "умные", но часто бесполезные преобразования git в конце строки)

git config --local core.autocrlf false

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

Саймон Ист
источник
Удаление text=autoпараметра в .gitattributes сработало для меня, а затем после того, как я git reset --hardвернул этот параметр, файлы больше не отображались как измененные!
ErikE
2
git checkout dir1/foo.aspx
git checkout dir2/foo.aspx
Стив Прентис
источник
У меня в голове было «откат», и я хотел писать checkout. Я уже пробовал checkout. В любом случае спасибо за ответ. Это был хороший ответ на мой первоначальный вопрос, поэтому я буду поддерживать его.
Грег Хендершотт
2

У вас также могла быть проблема, связанная с каталогами с именами буквенных регистров. Некоторые из ваших коллег могли изменить имя каталога, например, с myHandler на MyHandler . Если позже вы нажмете и вытащите некоторые файлы из исходного каталога, у вас будет 2 отдельных каталога в удаленном репозитории И только один на вашем локальном компьютере, поскольку в Windows у вас может быть только один. И у тебя проблемы.

Чтобы проверить, так ли это, просто посмотрите, имеет ли удаленный репозиторий двойную структуру.

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

смекси
источник
2

Я думаю, было бы полезно дать подсказку, как воспроизвести проблему , чтобы лучше понять проблему:

$ git init
$ echo "*.txt -text" > .gitattributes
$ echo -e "hello\r\nworld" > 1.txt
$ git add 1.txt 
$ git commit -m "committed as binary"
$ echo "*.txt text" > .gitattributes
$ echo "change.." >> 1.txt

# Ok let's revert now

$ git checkout -- 1.txt
$ git status
 modified:   1.txt

# Oooops, it didn't revert!!


# hm let's diff:

$ git diff
 warning: CRLF will be replaced by LF in 1.txt.
 The file will have its original line endings in your working 
 directory.
 diff --git a/1.txt b/1.txt
 index c78c505..94954ab 100644
 --- a/1.txt
 +++ b/1.txt
 @@ -1,2 +1,2 @@
 -hello
 +hello
  world

# No actual changes. Ahh, let's change the line endings...

$ file 1.txt 
 1.txt: ASCII text, with CRLF line terminators
$ dos2unix 1.txt
 dos2unix: converting file 1.txt to Unix format ...
$ git diff
 git diff 1.txt
 diff --git a/1.txt b/1.txt
 index c78c505..94954ab 100644
 --- a/1.txt
 +++ b/1.txt
 @@ -1,2 +1,2 @@
 -hello
 +hello
  world

# No, it didn't work, file is still considered modified.

# Let's try to revert for once more:
$ git checkout -- 1.txt
$ git status
 modified:   1.txt

# Nothing. Let's use a magic command that prints wrongly committed files.

$ git grep -I --files-with-matches --perl-regexp '\r' HEAD

HEAD:1.txt

2-й способ воспроизведения: в приведенном выше скрипте замените эту строку:
echo "*.txt -text" > .gitattributes
на
git config core.autocrlf=false
и оставьте остальные строки как есть


Что все это говорит? Текстовый файл может (при некоторых обстоятельствах) быть зафиксирован с помощью CRLF (например, -textв .gitattributes/ или core.autocrlf=false).

Когда позже мы захотим обработать тот же файл как текст ( -text-> text), его нужно будет снова зафиксировать.
Конечно, вы можете временно отменить его (как правильно ответил Абу Ассар ). В нашем случае:

echo "*.txt -text" > .gitattributes
git checkout -- 1.txt
echo "*.txt text" > .gitattributes

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


Для записи:

Чтобы проверить, какие файлы могут вызвать эту проблему в вашем репо, выполните следующую команду (git должен быть скомпилирован с --with-libpcre):

git grep -I --files-with-matches --perl-regexp '\r' HEAD

При фиксации файла (ов) (предположим, что вы хотите рассматривать их как текст), это то же самое, что делать то, что предлагается по этой ссылке http://help.github.com/line-endings/ для устранения таких проблем. . Но вместо удаления .git/indexи выполнения resetвы можете просто изменить файл (ы), затем выполнить git checkout -- xyz zyfи затем зафиксировать.

Маринос Ан
источник
2

У меня была такая же проблема с тем интересным дополнением, что файлы были изменены в Windows, но не при просмотре их из WSL. Никакое возня с окончанием строки, сбросом и т. Д. Не могло изменить это.

В конце концов, я нашел решение в этом ответе . Ниже приводится текст для удобства:


Я решил эту проблему, выполнив следующие действия

1) Удалите каждый файл из индекса Git.

git rm --cached -r .

2) Перепишите индекс Git, чтобы учесть все новые окончания строк.

git reset --hard

Решение было частью шагов, описанных на сайте git https://help.github.com/articles/dealing-with-line-endings/

MCO
источник
1

Для меня проблема заключалась не в окончании строк. Речь шла об изменении регистра в имени папки (Reset_password -> Reset_Password). Мне помогло это решение: https://stackoverflow.com/a/34919019/1328513

Женя
источник
1

Эта проблема также может быть вызвана тем, что git рассматривает различия в использовании заглавных букв как разные файлы, но Windows рассматривает их как один и тот же файл. Если в имени файла было изменено только его заглавные буквы, то каждый пользователь Windows этого репо окажется в этой ситуации.

Решение состоит в том, чтобы подтвердить правильность содержимого файлов, а затем повторно подтвердить его. Нам пришлось объединить содержимое двух файлов вместе, поскольку они были разными. Затем потяните, и возникнет конфликт слияния, который вы можете разрешить, удалив дубликат файла. Повторно подтвердите разрешение слияния, и вы вернетесь в стабильное состояние.

Эндрю Уэст
источник