Git: Як повернути 2 файли, які вперто застрягли на "Змінено, але не скоєно"?


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")

Доінг 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? плюс більше. Але два файли залишаються застряглими як "змінені, але не скоєні".

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

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замість "true".
Йоганн

2
Відповідь [тут] [1] спрацював як для мене, так і для багатьох інших. [1]: stackoverflow.com/questions/2016404/…
Майк К

2
Це також трапляється, коли репозиторій, що містить 2 або більше файлів в одному каталозі з різними регістрами, оформляється у файловій системі, що не чує до регістру. Видаліть або перейменуйте один із файлів, щоб вирішити проблему.
465544

Відповіді:


33

Які закінчення рядків у файлах? Б'юсь об заклад, вони 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, і це зводило нас з розуму, оскільки редактори на різних платформах продовжували вимикати лінійні термінатори, як тільки ця зміна пронизана, все це повинно піти.
Олівер Данджі,

120

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

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

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

У Mac OSX, однак, xargs працює дещо інакше (thx Daniel для коментаря):

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

Я додав це як заповнювач для себе наступного разу, але, сподіваюся, це також допоможе комусь іншому.

-Аль


9
У мене було кілька впертих файлів, і я запустив цю команду, тепер git status не змінює жодних змін, але коли я намагаюся змінити гілку, git все одно говорить, що я не можу змінити гілку, оскільки ці два файли мають локальні зміни. Не впевнені, що я зробив неправильно, але здавалося, що це просто приховувало проблему, а не виправляло? Я також не зміг зафіксувати файли після запуску цієї команди. Рішенням для мене було їх видалення, комітування та обмін гілками.
RodH257

5
Дякую! Я спробував ВСІ трюки, згадані в кожній іншій відповіді, яку міг знайти - жоден не спрацював. на mac не вдалося використовувати рядок як є, просто запустив git update-index --assume-unchanged <filename> на кожному файлі, і це змусило проблему зникнути.
Йонатан Карні

2
Чи не слід використовувати опцію "--assume-unchanged", коли ви хочете, щоб локальні модифікації цього файлу НІКОЛИ не здійснювались? Як, коли ви оформляєте файл конфігурації веб-сайту шаблону та оновлюєте його чутливою інформацією, яка не повинна зберігатися у сховищі?
Максим Россіні

6
Це саме те, що мені потрібно, хоча xargs на mac, здається, працює дещо інакше (я запускаю 10.10 Yosemite). Це нарешті у мене git ls-files -m | xargs -I {} git update-index --assume-unchanged {}
Даніель

3
Щоб скасувати дію команди:git ls-files -v|grep '^h' | cut -c3- | xargs -i git update-index --no-assume-unchanged "{}"
Марінос

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

Інша можливість полягає в тому, що різниця (яка заважає вам повернути ці файли за допомогою команди checkout) полягає у режимі файлу. Це сталося зі мною. У моїй версії git ви можете виявити це за допомогою

git diff dir1 / foo.aspx

І це покаже вам зміни режиму файлу. Однак це все одно не дозволить вам їх скасувати. Для цього також використовуйте

git config core.filemode false

або змініть git .config у текстовому редакторі, додавши

[ядро]

filemode = false

Після цього ви можете використовувати

git reset HEAD dir1 / foo.aspx

і файл повинен зникнути.

(Я отримав все це з відповіді на питання Як зробити git ігнорувати зміни режиму (chmod)? )


1
Якщо у вас Windows, першим припущенням має бути діагноз / рішення Eyal
AlcubierreDrive

Будьте особливо обережні, щоб ніколи не використовувати cygwin git з cmd.exe. Якщо ви хочете git в cmd.exe, встановіть msysgit.
AlcubierreDrive

Тільки для підтвердження того, що це справді була проблема у Windows.
Dejan Marjanović

Для мене в Windows це не була проблема (для неї core.filemodeвже було встановлено значення false). У моєму випадку виправлення / обхідний шлях був у відповіді Алана Форсайта .
Venryx,

3

Спробуйте скасувати локальні зміни :

git checkout -- dir1/foo.aspx
git checkout -- dir2/foo.aspx

У мене було "повернення" на мозок, і я мав намір писати checkout. Я вже намагався checkout. Все одно дякую за вашу відповідь. Це була гарна відповідь на моє початкове запитання, тому я підтримую.
Грег Хендерсхотт,

3

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

Запуск цієї команди іноді працює:
(Вимикає "розумні", але часто безпомічні перетворення, що закінчуються рядком)

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

Другий спосіб відтворення: У наведеному вище сценарії замініть цей рядок:
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

У мене була та сама проблема, з цікавим доповненням, що файли змінювались у вікнах, але не при перегляді їх із WSL. Жодне базікання з закінченнями рядків, скиданнями тощо не змогло це змінити.

Зрештою, я знайшов рішення в цій відповіді . Далі наводиться текст для Conventionince:


Я вирішив цю проблему, виконавши такі дії

1) Видаліть кожен файл із індексу Git.

git rm --cached -r .

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

git reset --hard

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



1

Ця проблема також може бути причиною, оскільки git розглядає відмінності з використання великих літер як різні файли, але Windows розглядає їх як один і той же файл. Якщо у назві файлу було лише змінено великі літери, тоді кожен користувач Windows цього репозиторію опиниться в цій ситуації.

Рішення полягає в тому, щоб підтвердити правильність вмісту файлів, а потім повторно надіслати його. Нам довелося об’єднати два файли разом, оскільки вони були різними. Потім витягніть, і виникне конфлікт злиття, який ви можете вирішити, видаливши повторюваний файл. Повторно введіть дозвіл злиття, і ви повернетеся до стабільного стану.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.