Отменить 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
в этой ситуации?
7 ответов
Вы сможете восстановить любые файлы, которые вы добавили в индекс (например, как в вашей ситуации, с 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)
Это значительно сокращает количество объектов, которые вы должны рассмотреть.
Обновление: Philip Oakley ниже предлагает другой способ сократить количество рассматриваемых объектов, а именно просто рассмотреть самые последние измененные файлы в .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
(Обратите внимание, что вы должны удалить /
в конце пути, чтобы получить имя объекта.)
Я только что сделал git reset --hard и потерял один коммит. Но я знал хеш коммита, поэтому я смог сделать git cherry-pick COMMIT_HASH, чтобы восстановить его.
Я сделал это в течение нескольких минут после потери коммита, так что это может сработать для некоторых из вас.
Благодаря Марку Лонгару я получил свои вещи обратно!
Сначала я сохранил все хэши в файл:
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
Решение @Ajedi32 в комментариях сработало для меня именно в этой ситуации.
git reset --hard @{1}
Обратите внимание, что все эти решения основаны на отсутствии git gc, и некоторые из них могут вызывать его, поэтому я бы заархивировал содержимое вашего каталога.git, прежде чем пытаться что-либо сделать, чтобы у вас был моментальный снимок, к которому можно вернуться, если он не не работает для вас.
Столкнулся с той же проблемой, но не добавил изменения в индекс. Таким образом, все приведенные выше команды не вернули мне желаемых изменений.
После всех вышеперечисленных сложных ответов это наивный намек, но, может быть, он спасет кого-то, кто не думал об этом первым, как я.
В отчаянии я пытался нажать CTRL-Z в моем редакторе (LightTable), один раз на каждой открытой вкладке - к счастью, файл на этой вкладке был восстановлен до самого последнего состояния до git reset --hard
, НТН.
Боже мой, я дернул себя за волосы, пока не столкнулся с этим вопросом и его ответами. Я полагаю, что правильный и краткий ответ на заданный вопрос доступен только в том случае, если вы соберете два комментария выше, так что здесь все в одном месте:
Как упомянуто chilicuil, запустите 'git reflog', чтобы определить там хеш коммита, к которому вы хотите вернуться
Как уже упоминалось Акимско, вы, скорее всего, НЕ захотите выбрать вишню, если только вы не потеряли только один коммит, поэтому вам следует запустить 'git reset --hard
Примечание для пользователей Egipse egit: я не смог найти способ сделать эти шаги в Eclipse с помощью egit. Закрытие Eclipse, запуск команд выше из окна терминала, а затем повторное открытие Eclipse работало для меня просто отлично.
Если вы редактируете свои файлы с помощью IDE, она также может хранить свою собственную историю независимо от Git. В некоторых случаях гораздо проще полностью обойти Git и использовать IDE. Например, и IntelliJ IDEA, и Eclipse имеют такой вид автоматического локального контроля версий, который они называют "локальной историей". В IntelliJ легко получить целый пакет потерянных изменений во многих файлах: на панели "Проект" вы можете щелкнуть правой кнопкой мыши весь проект или дерево каталогов и выбрать "Показать историю" в подменю "Локальная история". Вgit reset --hard
должен появиться в списке как "внешнее изменение" (т. е. инициированное извне IDE), и вы можете использовать кнопку возврата или пункт контекстного меню, чтобы вернуть все в состояние непосредственно перед тем, как произошло это внешнее изменение.
Это, вероятно, очевидно для профессионалов git, но я хотел это поднять, так как во время моих отчаянных поисков я не увидел, что это поднято.
Я установил некоторые файлы и сделал git reset --hard, немного взволновался, а затем заметил, что мой статус показывает, что все мои файлы все еще находятся в стадии подготовки, а также все их удаления не установлены.
На этом этапе вы можете зафиксировать эти поэтапные изменения, если вы не ставите их удаления. После этого вам нужно только набраться смелости, чтобы выполнить "git reset --hard" еще раз, что вернет вас к тем изменениям, которые вы поставили и теперь только что совершили.
Опять же, это, вероятно, не является чем-то новым для большинства, но я надеюсь, что, поскольку это помогло мне, и я не нашел ничего, что подсказывало бы это, это могло бы помочь кому-то еще.
Если вы недавно просмотрели git diff
то есть другой способ восстановиться после подобных инцидентов, даже если вы еще не внесли изменения: если вывод git diff
все еще находится в буфере вашей консоли, вы можете просто прокрутить вверх, скопировать и вставить разницу в файл и использовать patch
инструмент для применения различий к вашему дереву: patch -p0 < file
. Такой подход меня несколько раз спасал.
Вышеприведенные решения могут работать, однако, есть более простые способы исправить это, чем проходить через git
Сложная отмена. Я предполагаю, что большинство git-сбросов происходит с небольшим количеством файлов, и если вы уже используете VIM, это может быть наиболее эффективным решением. Предостережение в том, что вы уже должны использовать ViM's
постоянная отмена, которую вы должны использовать в любом случае, потому что она дает вам возможность отменить неограниченное количество изменений.
Вот шаги:
В вим прессе
:
и введите командуset undodir
, Если у вас естьpersistent undo
включен в вашем.vimrc
, он покажет результат, аналогичныйundodir=~/.vim_runtime/temp_dirs/undodir
,В вашем репо
git log
чтобы узнать последнюю дату / время, когда вы сделали последний коммитВ вашей оболочке перейдите к вашему
undodir
с помощьюcd ~/.vim_runtime/temp_dirs/undodir
,В этом каталоге используйте эту команду, чтобы найти все файлы, которые вы изменили с момента последнего коммита
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Затем перейдите к каждому из файлов, откройте их в vim и выполните "ранее 20м". Вы можете найти более подробную информацию о "ранее", используя "ч раньше". Вот
earlier 20m
значит вернуться к состоянию файла 20 минут назад, предполагая, что вы сделалиgit hard --reset
20 минут назад. Повторите это для всех файлов, которые были выложены изfind
команда. Я уверен, что кто-то может написать сценарий, который будет сочетать эти вещи.
Мне не удалось восстановить файл с помощью команд 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
Обыскивает весь диск, но, в конце концов, находит содержимое потерянного файла.
Я использую IntelliJ и могу просто просмотреть каждый файл и сделать:
Edit -> reload from disk
К счастью, я только что сделал git status
прямо перед тем, как стереть свои рабочие изменения, поэтому я точно знал, что мне нужно перезагрузить.
Запоминание иерархии файлов и использование техники Марка Лонгаира с модификацией Фила Окли дает впечатляющие результаты.
По сути, если вы хотя бы добавили файлы в репо, но не зафиксировали его, вы можете восстановить их в интерактивном режиме, используя git show
, проверьте журнал и используйте перенаправление оболочки для создания каждого файла (запоминая интересующий вас путь).
HTH!