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


110

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


@MarkLongair классный человек! Вы только что вернули мою работу! Я написал сценарий Python для создания файлов всего вывода! Я добавлю сценарий в качестве ответа
Мальчик

4
Не «глупо» .. а «наивно» ... потому что я сделал ТО ЖЕ!
Росди Касим

Все еще может быть глупо ;-)
Дункан МакГрегор

Вот отличная статья о том, как это исправить. Это потребует некоторой ручной работы.
Ян Сварт,

@MarkLongair `` `` найти .git / objects / -type f -printf '% TY-% Tm-% Td% TT% p \ n' | sort '' работал у меня. даты тоже появляются, начните проверять капли с конца.
ZAky 08

Ответы:


175

Вы должны иметь возможность восстанавливать любые файлы, которые вы добавили в индекс (например, как в вашей ситуации 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 «s комментарий ниже:

Если вы обнаружите, что у вас есть много коммитов и деревьев, перечисленных в выходных данных этой команды, вы можете удалить из выходных данных любые объекты, на которые есть ссылки из коммитов без ссылок. (Обычно вы все равно можете вернуться к этим коммитам через 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

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


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

3
Разве нельзя посмотреть на метки времени объектов для самых последних объектов, которые позже, чем ваша последняя фиксация? или я что-то упускаю.
Филип Окли,

1
@Philip Oakley: спасибо за это предложение, я добавил предложение найти самые последние измененные объекты в базе данных объектов.
Марк Лонгэр

2
Чувак, это круто! Это спасло мои @ $$ только что. И я изучил некоторую мерзкую науку. Лучше смотри, что добавляешь в индекс ...
Bowsersenior

2
чтобы здесь никого не украсть. Но просто примечание, поскольку я только что столкнулся с той же проблемой и подумал, что потерял всю свою работу. Для тех, кто использует IDE , в IDE обычно есть решение для восстановления в один клик. В случае PHPstorm мне просто нужно было щелкнуть правой кнопкой мыши каталог и выбрать показать локальную историю, а затем вернуться к последнему допустимому состоянию.
Шалом Сэм

58

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

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


5
Спасибо, спасибо, спасибо. Благодаря этому ответу удалось восстановить дневную работу
Даце

22
Вероятно, стоит упомянуть, что вы можете увидеть эти хэши, используя git reflog, например, git reset --hard-> git reflog(глядя на хэш HEAD @ {1}) и, наконец,git cherry-pick COMMIT_HASH
Хавьер Лопес

2
Ребята, читающие это, имейте в виду, что это работает только для одного хэша фиксации, если было более одной фиксации, это не решит проблему сразу!
Ain Tohvri 05

4
Фактически можно сбросить "вперед". Поэтому, если вы сбросили несколько коммитов, выполнение еще одного 'git reset --hard <commit>' должно восстановить всю цепочку коммитов до этого момента. (при условии, что они еще не попали в ГК)
акимско

6
Один вкладыш для выздоравливающих фиксаций утерянных с git reset --hard(при условии , что вы ничего другого после этой команды не сделали) этоgit reset --hard @{1}
Ajedi32

13

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

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

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

1
Да! Это именно то, что мне нужно. Мне тоже нравится скрипт Python для воссоздания всех файлов. Другие ответы заставили меня нервничать из-за потери данных при сборке мусора, поэтому сброс файлов - это победа для меня :)
Эрик Олсон

1
Я написал этот сценарий на основе этого ответа. Работает из коробки: github.com/pendashteh/git-recover-index
Alexar

1
Я считаю , что проще автоматизировать это следующим образом : 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
Матия Налис

6

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

git reset --hard @{1}

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


У меня было несколько незакрепленных файлов. Затем я зафиксировал 1 файл и сделал git reset --hard, что испортило мое репо каким-либо неизвестным образом. @ Дункан, что делает @ {1}? и какой комментарий вы имеете в виду? Это сброс git reset?
alpha_989

Я думаю, что @ {1} является относительной ссылкой на предпоследнюю фиксацию, но я не эксперт, просто сообщаю, что сработало для меня
Дункан МакГрегор

3

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

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

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


1
Точно такая же ситуация, пропустили добавление в индекс и сделали полный сброс, и ни одно из решений не сработало все выше. Это был SMART ход, спасибо, что я сделал Ctrl + Z и спас мне день. Мой редактор: SublimeText. Один с моей стороны!
Вивек

1

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

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

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

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


1

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

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

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

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


0

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

Вот шаги:

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

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

  3. В вашей оболочке перейдите к своему undodirusing 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 --reset20 минут назад. Повторите это для всех файлов, которые были выведены из findкоманды. Я уверен, что кто-нибудь сможет написать сценарий, который объединит эти вещи.


0

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

Edit -> reload from disk

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


0

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

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

НТН!


0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.