Отменить git reset --hard с незафиксированными файлами в промежуточной области

Я пытаюсь восстановить свою работу. Я тупо делал git reset --hard, а до этого делал только get add . и не делал git commit. Пожалуйста помоги! Вот мой журнал:

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

Можно ли в этой ситуации отменить git reset --hard?


person eistrati    schedule 10.09.2011    source источник
comment
@MarkLongair классный мужик! Вы только что вернули мою работу! Я написал сценарий Python для создания файлов всего вывода! В ответ добавлю скрипт   -  person Boy    schedule 08.01.2014
comment
Не «глупо» .. а «наивно» ... потому что я сделал ТО ЖЕ!   -  person Rosdi Kasim    schedule 07.03.2014
comment
Можно еще тупо ;-)   -  person Duncan McGregor    schedule 11.01.2016
comment
Вот отличная статья о том, как кое-что из этого изменить. Это потребует некоторой ручной работы.   -  person Jan Swart    schedule 22.10.2016
comment
@MarkLongair `` `` найти .git / objects / -type f -printf '% TY-% Tm-% Td% TT% p \ n' | sort '' работал у меня. даты тоже появляются, начните проверять капли с конца.   -  person ZAky    schedule 08.04.2018
comment
Как восстановить текст после git show 907b308...? Это дает мне разницу, а не файл.   -  person Danijel    schedule 18.01.2019


Ответы (13)


Вы должны иметь возможность восстанавливать любые файлы, которые вы добавили в индекс (например, как в вашей ситуации, с помощью git add .), хотя это может потребовать некоторой работы. Чтобы добавить файл в индекс, git добавляет его в базу данных объектов, что означает, что он может быть восстановлен до тех пор, пока не произошла сборка мусора. Пример того, как это сделать, приведен в Ответ Якуба Наребски здесь:

Однако я попробовал это в тестовом репозитории, и возникла пара проблем - --cached должно быть --cache, и я обнаружил, что на самом деле это не создает каталог .git/lost-found. Однако у меня сработали следующие шаги:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

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

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... и для каждой из этих капель вы можете:

git show 907b308

Для вывода содержимого файла.


Слишком много вывода?

Обновление в ответ на комментарий sehe ниже:

Если вы обнаружите, что у вас есть много коммитов и деревьев, перечисленных в выходных данных этой команды, вы можете удалить из выходных данных любые объекты, на которые есть ссылки из коммитов без ссылок. (Обычно вы все равно можете вернуться к этим коммитам через reflog - нас просто интересуют объекты, которые были добавлены в индекс, но никогда не могут быть найдены с помощью коммита.)

Сначала сохраните вывод команды с помощью:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

Теперь имена объектов этих недостижимых коммитов можно найти с помощью:

egrep commit all | cut -d ' ' -f 3

Таким образом, вы можете найти только деревья и объекты, которые были добавлены в индекс, но не зафиксированы в какой-либо момент, с помощью:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

Это значительно сокращает количество объектов, которые вам нужно учитывать.


Обновление: Филип Окли ниже предлагает другой способ сократить количество объектов для рассмотрения , который должен учитывать только самые последние измененные файлы в .git/objects. Вы можете найти их с помощью:

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(Я обнаружил, что find вызов здесь.) Конец этого списка может выглядеть так:

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

В этом случае вы можете увидеть эти объекты с помощью:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(Обратите внимание, что вам нужно удалить / в конце пути, чтобы получить имя объекта.)

person Mark Longair    schedule 11.09.2011
comment
прежде чем я опубликовал свой ответ, я подумал о том, чтобы включить именно эти шаги. Однако, попробовав это с одним из моих локальных репозиториев, я получил сотни недостижимых объектов и не смог найти адекватного подхода, например, для их сортировки по дате отправки. Теперь это было бы круто - person sehe; 11.09.2011
comment
@sehe: Я обновил свой ответ, предлагая, как сократить количество выводимых объектов. Я пробовал это в существующем репозитории, и тогда выход был вполне управляемым. - person Mark Longair; 11.09.2011
comment
+1 за некоторые потенциально полезные усовершенствования моих обычных процедур! - person sehe; 11.09.2011
comment
Разве нельзя посмотреть на метки времени для самых последних объектов, которые позже, чем ваша последняя фиксация? или я что-то упускаю. - person Philip Oakley; 12.09.2011
comment
@Philip Oakley: спасибо за это предложение, я добавил предложение найти самые последние измененные объекты в базе данных объектов. - person Mark Longair; 13.09.2011
comment
Чувак, это круто! Это спасло мои @ $$ только что. И я узнал некоторую мерзкую науку. Лучше смотри, что добавляешь в индекс ... - person bowsersenior; 28.07.2012
comment
@PhilipOakley У меня есть метки времени с %tT вместо %TT - person Steven Pribilinskiy; 31.07.2014
comment
чтобы здесь никого не украсть. Но просто примечание, поскольку я только что столкнулся с той же проблемой и подумал, что потерял всю свою работу. Для тех, кто использует IDE, в IDE обычно есть решение для восстановления в один клик. В случае PHPstorm мне просто нужно было щелкнуть правой кнопкой мыши каталог и выбрать показать локальную историю, а затем вернуться к последнему допустимому состоянию. - person Shalom Sam; 22.09.2014
comment
Если вы внесли изменения в тот же день и у вас было какое-то ключевое слово для поиска по всем каплям: в папке ./git/objects я сделал find -maxdepth 2 -type f -mtime -1 | sed -r 's/[\.\/]//g' | xargs -n 1 -I {} -i bash -c 'git show {} | grep -q YOUR_KEYWORD && echo {}' - person dojuba; 30.06.2015
comment
Отличный подход @MarkLongair, я получил резервную копию в blob и преобразовал ее в файл .txt. - person Sunil kumar; 31.05.2016
comment
Моя версия find не поддерживала этот синтаксис (и мне было лень придумать, как его изменить), но ls -ltr ~/.git/objects/*/* у меня отлично сработал. - person user1071847; 28.06.2016
comment
Потрясающий! ты сэкономил мне время. - person Kondal Kolipaka; 15.07.2016
comment
Как предлагает @PhilipOakley, я считаю, что просмотр .git/objects и поиск недавно измененных файлов - безусловно, лучший способ восстановить вашу работу. Это означает, что git fsck у меня не сработало, однако find .git/objects/ ... сработало очень хорошо. - person Danijel; 21.01.2019
comment
Все еще копит задницы 19 лет спустя. Я действительно думал, что сейчас сильно облажался. Все удалось восстановить. - person Neil; 27.04.2020
comment
Здорово ! Это спасло мне день;) - person halogenr; 04.03.2021

Я только что сделал git reset --hard и потерял одну фиксацию. Но я знал хеш фиксации, поэтому смог сделать git cherry-pick COMMIT_HASH, чтобы восстановить его.

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

person Richard Saunders    schedule 27.08.2013
comment
Спасибо, спасибо, спасибо. Благодаря этому ответу удалось восстановить дневную работу. - person Dace; 11.11.2013
comment
Вероятно, стоит упомянуть, что вы можете увидеть эти хэши с помощью git reflog, например. git reset --hard - ›git reflog (глядя на хэш HEAD @ {1}) и, наконец, git cherry-pick COMMIT_HASH - person Javier López; 16.11.2013
comment
Это самый простой и быстрый способ исправить это. Потрясающий! - person Lotus; 29.05.2014
comment
отличный ответ! коротко и просто! - person Technotronic; 03.06.2014
comment
Ребята, читающие это, имейте в виду, что это работает только для одного хэша коммита, если было более одного коммита, это не решит проблему сразу! - person Ain Tohvri; 05.06.2014
comment
Практически потерял 5 дней работы. Используя git reflog, я смог выполнить полный сброс до одного из хэшей оттуда и вернуть все обратно! - person Senica Gonzalez; 18.06.2014
comment
Вы действительно можете сбросить вперед. Поэтому, если вы сбросили несколько коммитов, выполнение еще одного «git reset --hard ‹commit›» должно восстановить всю цепочку коммитов до этого момента. (учитывая, что они еще не GC'ed) - person akimsko; 16.09.2014
comment
Спасибо, это действительно помогло. ;) - person James Wilkins; 08.12.2014
comment
Однострочник для восстановления коммитов, потерянных с git reset --hard (при условии, что вы ничего не сделали после этой команды) - git reset --hard @{1} - person Ajedi32; 15.01.2015

Благодаря Марку Лонгару я вернул свои вещи!

Сначала я сохранил все хеши в файл:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

затем я помещаю их все (удаляя «недоступный blob») в список и помещаю данные все в новые файлы ... вам нужно выбрать свои файлы и переименовать их снова, что вам нужно ... но мне нужно было только несколько файлы .. надеюсь, это поможет кому-то ...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1
person Boy    schedule 08.01.2014
comment
Да! Это именно то, что мне нужно. Мне тоже нравится скрипт Python для воссоздания всех файлов. Другие ответы заставили меня нервничать из-за потери данных из-за сборки мусора, поэтому выгрузка файлов - это победа для меня :) - person Eric Olson; 19.11.2014
comment
Я написал этот сценарий на основе этого ответа. Работает "из коробки": github.com/pendashteh/git-recover-index - person Alexar; 31.01.2016
comment
Мне проще автоматизировать это так: mkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh. Файлы будут в lost/*.blob - person Matija Nalis; 25.01.2018
comment
Это недавно сэкономило мне 3 дня работы - person lakhassane; 09.06.2021

Решение @Ajedi32 в комментариях сработало для меня именно в этой ситуации.

git reset --hard @{1}

Обратите внимание, что все эти решения полагаются на отсутствие git gc, и некоторые из них могут вызвать его, поэтому я бы заархивировал содержимое вашего каталога .git, прежде чем пытаться что-либо, чтобы у вас был снимок, к которому можно вернуться, если он не не работает для вас.

person Duncan McGregor    schedule 11.01.2016
comment
У меня было несколько незакрепленных файлов. Затем я зафиксировал 1 файл и сделал git reset --hard, который каким-то неизвестным образом испортил мое репо. @ Дункан, что делает @ {1}? и какой комментарий вы имеете в виду? Это сброс git reset? - person alpha_989; 21.03.2018
comment
Я думаю, что @ {1} является относительной ссылкой на предпоследнюю фиксацию, но я не эксперт, просто сообщаю, что у меня сработало. - person Duncan McGregor; 21.03.2018

Решили ту же проблему, но не добавили изменений в индекс. Таким образом, все приведенные выше команды не вернули мне желаемых изменений.

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

В отчаянии я попытался нажать CTRL-Z в своем редакторе (LightTable) один раз на каждой открытой вкладке - это, к счастью, восстановило файл на этой вкладке до его последнего состояния до git reset --hard. HTH.

person olange    schedule 30.09.2016
comment
Точно такая же ситуация, пропустили добавление в индекс и сделали полный сброс, и ни одно из решений не сработало все выше. Это был SMART-ход, спасибо, что я сделал Ctrl + Z и спас мне день. Мой редактор: SublimeText. Один с моей стороны! - person Vivek; 22.12.2017

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

  1. Как упоминалось chilicuil, запустите git reflog, чтобы определить там хеш фиксации, к которому вы хотите вернуться.

  2. Как упомянул акимский, вы, скорее всего, НЕ захотите выбирать вишню, если только вы не потеряли только одну фиксацию, поэтому вам следует запустить git reset --hard <hash-commit-you-want>

Примечание для пользователей egit Eclipse: я не смог найти способ выполнить эти шаги в Eclipse с помощью egit. Закрытие Eclipse, выполнение приведенных выше команд из окна терминала, а затем повторное открытие Eclipse сработали для меня отлично.

person Lolo    schedule 03.12.2014
comment
Ты меня очень спас, большое тебе спасибо - person Deus chami; 02.01.2021

Если вы редактируете свои файлы в среде IDE, она также может хранить свою историю независимо от Git. В некоторых случаях гораздо проще полностью обойти Git и использовать IDE. Например, в IntelliJ IDEA и Eclipse есть такой вид автоматического локального контроля версий, который они называют локальной историей. В IntelliJ легко получить целый пакет потерянных изменений во многих файлах: на панели «Проект» вы можете щелкнуть правой кнопкой мыши весь проект или дерево каталогов и выбрать «Показать историю» в подменю «Локальная история». git reset --hard должен появиться в списке как внешнее изменение (т. Е. Инициированное извне IDE), и вы можете использовать кнопку возврата или пункт контекстного меню, чтобы вернуть все в состояние непосредственно перед тем, как произошло это внешнее изменение.

person abyrd    schedule 26.08.2020

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

Я поставил несколько файлов и сделал git reset --hard, немного испугался, а затем заметил, что мой статус показывает, что все мои файлы все еще находятся в стадии постановки, а также все их удаления неустановлены.

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

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

person ddoty    schedule 12.12.2018

Если вы недавно просмотрели git diff, то есть другой способ восстановления после подобных инцидентов, даже если вы еще не внесли изменения: если результат git diff все еще находится в буфере вашей консоли, вы можете просто прокрутить вверх, скопировать- вставьте разницу в файл и используйте инструмент patch, чтобы применить разницу к вашему дереву: patch -p0 < file. Такой подход меня несколько раз спасал.

person Confusion    schedule 07.08.2020

Вышеупомянутые решения могут работать, однако есть более простые способы исправить это, вместо того, чтобы выполнять сложную отмену git-s. Я предполагаю, что большинство git-reset происходит с небольшим количеством файлов, и если вы уже используете VIM, это может быть наиболее эффективным решением по времени. Предостережение заключается в том, что вы уже должны использовать ViM's persistent-undo, который вы должны использовать в любом случае, потому что он дает вам возможность отменять неограниченное количество изменений.

Вот шаги:

  1. В vim нажмите : и введите команду set undodir. Если в вашем .vimrc включен persistent undo, он покажет результат, аналогичный undodir=~/.vim_runtime/temp_dirs/undodir.

  2. В вашем репо используйте git log, чтобы узнать дату и время последней фиксации.

  3. В своей оболочке перейдите к своему undodir, используя cd ~/.vim_runtime/temp_dirs/undodir.

  4. В этом каталоге используйте эту команду, чтобы найти все файлы, которые вы изменили с момента последней фиксации.

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    Здесь «2018-03-20 11:24:44» - это дата и время последней фиксации. Если дата, когда вы сделали git reset --hard, - «2018-03-22», тогда используйте «2018-03-22», затем используйте «2018-03-23». Это из-за причуды find, где нижняя граница является включающей, а верхняя граница является исключительной. https://unix.stackexchange.com/a/70404/242983

  5. Затем перейдите к каждому из файлов, откройте их в vim и выполните «20 минут раньше». Вы можете найти более подробную информацию о «ранее», используя «h ранее». Здесь earlier 20m означает возврат к состоянию файла на 20 минут назад, при условии, что вы выполнили git hard --reset, 20 минут назад. Повторите это для всех файлов, которые были выведены из команды find. Я уверен, что кто-нибудь сможет написать сценарий, который объединит эти вещи.

person alpha_989    schedule 21.03.2018

Я использую IntelliJ и могу просто просмотреть каждый файл и сделать:

Edit -> reload from disk

К счастью, я только что сделал git status прямо перед тем, как стереть свои рабочие изменения, поэтому я точно знал, что мне нужно перезагрузить.

person Forrest    schedule 07.05.2019

Запоминание иерархии файлов и использование техники Марка Лонгаира с модификацией Фила Окли дает впечатляющие результаты.

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

HTH!

person ainthu    schedule 27.09.2019

Мне не удалось восстановить файл с помощью команд git. В моем случае это был файл CPP. Я знал в нем уникальную строку, и это помогло. Команда такая:

sudo grep -a -B[number of rows before the text being searched] -A[number of rows after the text being searched] '[some unique text in the lost file]' /dev/sda3 > test.cpp

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

person AntonDudov    schedule 01.09.2020