Отменить git reset --hard с незафиксированными файлами в области подготовки - PullRequest
102 голосов
/ 10 сентября 2011

Я пытаюсь восстановить свою работу.Я тупо сделал 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 в этой ситуации?

Ответы [ 9 ]

160 голосов
/ 11 сентября 2011

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

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

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

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

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... и для каждого из этих BLOB-объектов вы можете выполнить:

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

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

59 голосов
/ 27 августа 2013

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

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

13 голосов
/ 08 января 2014

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

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

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

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

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
5 голосов
/ 11 января 2016

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

git reset --hard @{1}

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

3 голосов
/ 30 сентября 2016

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

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

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

1 голос
/ 12 декабря 2018

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

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

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

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

1 голос
/ 03 декабря 2014

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

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

  2. Как уже упоминал акимско, вы, скорее всего, НЕ захотите вишенку если вы не потеряли только один коммит, то вам следует запустить 'git reset --hard

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

0 голосов
/ 07 мая 2019

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

Edit -> reload from disk

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

0 голосов
/ 21 марта 2018

Указанные выше решения могут работать, однако существуют более простые способы восстановления после это вместо прохождения git -s сложной отмены. Я думаю, что большинство git-сброс происходит на небольшом количестве файлов, и если вы уже используете 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». Это из-за причуды находки, где нижняя граница является включающей, а верхняя граница является исключительной. https://unix.stackexchange.com/a/70404/242983

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

...