Странный случай git - git stash, за которым следует git stash, применить потерянные незафиксированные данные?

У меня есть файл, скажем, file.txt. Я сделал git mv file.txt для file1.txt, затем создал новый файл с именем file.txt и работал над ним. К сожалению, я еще не добавил этот файл в git. В любом случае, проблема в том, что я сделал git stash, затем git stash apply, но новый файл .txt исчез... как бы его вернуть?


person khelll    schedule 10.04.2010    source источник
comment
При изучении Git неплохо сделать резервную копию всего каталога проекта (включая подкаталог .git) перед выполнением команд, функциональность которых вы не полностью понимаете. Я делал это довольно много раз, когда впервые начал использовать Git, и избавил себя от многих проблем. Хорошей практикой является попытка восстановления из того, что осталось после ошибки, но иногда вы действительно удаляете что-то навсегда по ошибке. Жесткий сброс, принудительная отправка, перебазирование и т. д. по своей природе создают риск потери данных, факт, который четко указан в большинстве руководств, которые я читал.   -  person Dan Bechard    schedule 09.10.2013


Ответы (4)


Проблема здесь в основном в непонимании того, что делает git stash save. Он сохраняет только изменения в отслеживаемых файлах. Неотслеживаемые файлы не сохраняются git stash. Когда вы переместили файл.txt в файл1.txt, новый файл.txt является неотслеживаемым и не будет сохранен git stash. Это не ошибка, просто так себя ведет git stash. Возможно, документация для git stash должна быть более ясной по этому поводу.

Как указано в документации для git stash save, он выполнит git reset --hard после сохранения ваших изменений. Именно git reset --hard перезаписал новый файл .txt. Кто-то может возразить, что git reset --hard должен генерировать предупреждение, если неотслеживаемый файл будет перезаписан, но я бы все же не назвал это ошибкой. Он делает то, что должен делать.

Здесь важно понять — и это избавило бы вас от многих проблем — это то, что git stash save не сохраняет неотслеживаемые файлы (и, вероятно, не должно).

person Dan Moulding    schedule 10.04.2010
comment
@Dan Moulding, я согласен с этим, но если git stash откатывается и перезаписывает незафиксированные данные, должно быть какое-то уведомление. - person maček; 10.04.2010
comment
@smotchkkiss: Это разумно. В этом случае пользователь явно ожидал, что новый файл file1.txt будет сохранен git stash и повторно применен git stash apply. Это просто неправильное ожидание. Однако, как вы сказали, я думаю, что было бы разумно получить предупреждение (и запросить подтверждение), если неотслеживаемый файл будет перезаписан git reset --hard. - person Dan Moulding; 10.04.2010
comment
Это хорошее объяснение того, почему это произошло, но, честно говоря, такое поведение git немного шокирует и наносит большой ущерб.... - person khelll; 10.04.2010
comment
Я не верю, что это недоразумение; Я считаю, что это чугунный жук. Git никоим образом не должен отбрасывать изменения в неотслеживаемом файле без предупреждения. Файл отслеживается в HEAD, который пользователь пытается спрятать, а не в индексе. По этой причине я считаю, что git обязательно должен сохранить версию рабочего дерева в операции хранения. - person CB Bailey; 10.04.2010
comment
@Charles: новый файл и переименованный файл, хотя у них и может быть одно и то же имя, — это два совершенно разных файла. Git отслеживает только один из них. Сказать, что новый файл отслеживается, потому что Git знал о каком-то другом файле с таким же именем в каком-то другом дереве, просто неправильно. Посмотрите, что git status говорит о состоянии рабочего дерева после того, как исходный файл перемещен и создан новый — это в значительной степени говорит само за себя. - person Dan Moulding; 10.04.2010
comment
@Charles: Кроме того, если есть ошибка (и я все равно не сказал бы, что она есть), это то, что Git не предупреждает, что он собирается перезаписать неотслеживаемый файл. И вы, кажется, согласны с этим. В этом случае ошибка связана с git reset --hard, а не с git stash save, поскольку первая перезаписывает неотслеживаемый файл без предупреждения. - person Dan Moulding; 10.04.2010
comment
@Dan: Нет, git stash - это пользовательская операция, которая теряет содержимое файла, так что это ошибка в git stash. Он перезаписывает измененный файл версией HEAD с расширением reset --hard. Поскольку тайник сбрасывается на отслеживаемую версию файла, он должен убедиться, что он сохранил любую версию, которую он перезаписывает. git stash должно быть безопасной операцией, это не похоже на reset --hard, который просит git выбросить вещи. Я не считаю, что наблюдаемое поведение тайника приемлемо. - person CB Bailey; 10.04.2010
comment
@Charles: Вы только что сказали, что Git никогда не должен выбрасывать неотслеживаемые файлы без предупреждения. git reset --hard делает это, и это причина, по которой git stash save перезаписывает неотслеживаемый файл. git stash save не сохраняет неотслеживаемые файлы. В момент запуска git stash save в этом случае Git считает файл.txt неотслеживаемым файлом. Так что это не и не должно сохраняться. Лучшее, что вы можете получить, — это предупреждение о том, что неотслеживаемый файл будет перезаписан. Но это git reset --hard нужно изменить, если мы хотим получить такое поведение. - person Dan Moulding; 10.04.2010
comment
@Charles: учтите следующее: Git не создает большой двоичный объект, представляющий файл, пока вы не добавите файл в индекс. Чтобы git stash сохранил файл, ему необходимо, чтобы этот файл имел представление BLOB-объектов в репозитории. Новый файл file.txt никогда не добавлялся в индекс, поэтому Git никогда не создавал большой двоичный объект для этого файла. Поскольку у него нет большого двоичного объекта для файла, он никоим образом не может сохранить файл в тайнике. Вы предлагаете Git начать отслеживать файл (создав для него большой двоичный объект), который никогда не добавлялся явно. Это просто не то, как это предназначено для работы. - person Dan Moulding; 10.04.2010
comment
Прочитайте документы еще раз. Предполагается, что git stash сохранит ваши локальные изменения перед выполнением reset --hard. Удаление файла, а затем повторное добавление файла с другим содержимым, безусловно, является локальным изменением. git stash; git stash apply должен быть неактивным (хорошо, строго сброс git). Ваш аргумент о том, что это не капля, явно фальшивка. Если у вас есть неустановленные локальные изменения файлов, которые не были удалены из индекса, git stash создает новые большие двоичные объекты для измененных файлов. git stash уже выполняет git add -u, так почему бы ему не создавать большие двоичные объекты для всех файлов, которые он удаляет? - person CB Bailey; 11.04.2010
comment
Он сохраняет локальные изменения только в отслеживаемых файлах. Он создает новые большие двоичные объекты для файлов, которые уже отслеживаются. Нигде больше Git не создает большие двоичные объекты для файлов, которые не отслеживаются (и никогда не отслеживались). И не должно. В этом случае пользователь хотел сохранить файл, который был перезаписан. Но что, если пользователь этого не хочет? Если stash сделал то, о чем вы просите, то теперь в репозитории есть данные, которые пользователь никогда не просил быть там. Git не должен этого делать. Суть такова: если вы хотите git stash сохранить новый файл .txt, вам нужно сначала git add его. Это слишком много? - person Dan Moulding; 11.04.2010
comment
Существует большая разница между тем, чтобы не сохранять изменения в неотслеживаемом файле, потому что он останется в покое, и не сохранять изменения в неотслеживаемом файле, а затем стереть его. Вы серьезно говорите, что команда, задокументированная как сохранение локальных модификаций, должна при некоторых обстоятельствах стирать их? - person CB Bailey; 11.04.2010
comment
Что такое локальная модификация? Это когда вы вносите изменения в файл, который Git уже отслеживает, вот что. Создание совершенно нового файла не является модификацией. Итак, да, я утверждаю, что git stash ведет себя именно так, как рекламируется. Ни в коем случае ни в коем случае он не должен стирать изменения. Но если он собирается стереть файл, о котором он ничего не знает (неотслеживаемый файл), то я лично думаю, что было бы неплохо, если бы он выдал предупреждение (так же, как git checkout делает в тех же обстоятельствах). Я абсолютно не согласен с тем, что git stash должен сохранять неотслеживаемые файлы при любых обстоятельствах. - person Dan Moulding; 11.04.2010
comment
git stash отменяет изменения индекса и в рабочем дереве. Если файл находится в коммите HEAD, удален из индекса и повторно добавлен в рабочее дерево, то да, это должна быть локальная модификация. Если вы согласны с тем, что он ни в коем случае не должен стирать модификации, вы говорите, что рекламируемое поведение должно измениться? Считаете ли вы, что git stash должен дать сбой, если у вас есть неотслеживаемый файл, который будет изменен при сбросе? - person CB Bailey; 11.04.2010
comment
Если бы это было правдой, то git status показал бы новый файл как измененный, но не обновленный. Но это не так. Это ясно показывает, что Git считает этот файл неотслеживаемым. Кажется, вы утверждаете, что сломано не только git stash, но и git status. - person Dan Moulding; 11.04.2010
comment
Изменено, но не обновлено будет означать рабочие изменения дерева, но не изменения индекса, с файлом, удаленным из индекса, но в версии HEAD, что явно не так. Я не думаю, что статус git нарушен, но я не понимаю, что вы, кажется, интерпретируете из его вывода. Опять же, вы думаете, что тайник должен дать сбой, если у вас есть неотслеживаемый файл, который будет изменен при сбросе, или вы думаете, что поведение тайника нормально и так? - person CB Bailey; 11.04.2010
comment
Нет. Вы можете изменить файл, добавить его в индекс, изменить еще немного, и файл будет указан как измененный, но не обновленный. Это рабочие изменения дерева и изменения индекса. Если в статусе указано, что файл не отслеживается, я интерпретирую это как означающее, что файл не отслеживается. Как вы это интерпретируете? Кроме того, если тайник не сохраняет неотслеживаемые файлы, как вы думаете, почему он должен сохранять файл, помеченный как неотслеживаемый по статусу? Я думаю, что общее текущее поведение неоптимально. stash делает то, что должен, но тот факт, что git reset --hard удалит неотслеживаемые файлы без предупреждения, вызывает сомнения. - person Dan Moulding; 11.04.2010
comment
Просто сомнительно? Каким, на ваш взгляд, было бы оптимальное поведение git stash в этой ситуации? - person CB Bailey; 11.04.2010
comment
Проще говоря, это то, что я думаю, должно произойти. git stash должен отменить все изменения между HEAD и текущим рабочим деревом, а также сбросить индекс на HEAD. git stash, за которым сразу следует git stash apply, должно сбросить рабочее дерево до состояния, в котором оно было до тайника. Для меня они являются ключевыми и неизбежно приводят к тому, что если HEAD содержит файл с определенным содержимым, а версия рабочего дерева имеет другое содержимое, то состояние файла рабочего дерева должно быть сохранено независимо от того, содержит ли индекс этот файл или нет. версия этого файла или нет. - person CB Bailey; 11.04.2010

Это похоже на серьезную (т.е. потерю данных) ошибку в тайнике. Пожалуйста, сообщите об этом. К сожалению, я не верю, что есть способ вернуть новый file.txt.

Теперь эта ошибка исправлена ​​в git >=1.7.1.1.

person CB Bailey    schedule 10.04.2010
comment
@khellll: список рассылки git: [email protected] - person CB Bailey; 10.04.2010
comment
Downvoter: Я считаю, что мой ответ правильный; если я ошибаюсь, покажите, как можно восстановить содержимое файла. - person CB Bailey; 10.04.2010

в будущем используйте git stash -u для хранения незафиксированных файлов (http://www.kernel.org/pub/software/scm/git/docs/git-stash.html). Думаю, вы можете сделать это, начиная с git версии 1.7.

person hajpoj    schedule 01.11.2012

Этот пост предназначен для того, чтобы просто проиллюстрировать процесс воссоздания, не вдаваясь в комментарии. Примечание. Использование Git версии 1.7.0.2.

Чтобы воссоздать:

~/test $ git init
~/test $ echo "hello" > file.txt
~/test $ git add .
~/test $ git commit -m "init commit"

~/test $ git mv file.txt file1.txt
~/test $ echo "new data" > file.txt
~/test $ git stash
~/test $ git stash apply

~/test $ cat file.txt
cat: file.txt: No such file or directory

~/test $ cat file1.txt
hello
person maček    schedule 10.04.2010