Скасувати скидання git - твердий із непосланими файлами в області постановки


109

Я намагаюся відновити свою роботу. Я нерозумно робив 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
Не "дурно" .. а "наївно" ... тому що я саме зробив ТЕБЕ!
Росді Касим

Може бути ще дурно ;-)
Данкан МакГрегор

Ось чудова стаття про те, як повернути щось із цього. Це займе ручну роботу.
Jan Swart

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

Відповіді:


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 нижче:

Якщо ви виявите, що у вас є багато комітетів та дерев, перелічених у висновку з цієї команди, ви можете вилучити з виводу будь-які об'єкти, на які посилаються нереференційні коміти. (Зазвичай ви можете повернутися до цих комітетів за допомогою рефлогу в будь-якому випадку - нас просто цікавлять об'єкти, які були додані до індексу, але їх не можна знайти через комісію.)

Спочатку збережіть вихід команди, використовуючи:

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

(Зверніть увагу, що вам потрібно видалити /кінець шляху, щоб отримати ім'я об'єкта.)


перш ніж я опублікував свою відповідь, я розглядав можливість включення саме цих кроків. Однак, спробувавши це за допомогою однієї з моїх місцевих репостів, у мене з’явилися сотні недосяжних, і я не міг підійти адекватного підходу до, скажімо, сортування їх за поданою датою. Тепер це буде дивним
sehe

3
Чи не можливо переглянути позначки часу об'єкта для останніх об'єктів, які пізніше, ніж ваш останній фіксатор? чи я щось пропускаю.
Філіп Оуклі

1
@Philip Oakley: спасибі за цю пропозицію, я додав пропозицію знайти останні змінені об’єкти в базі даних об’єктів.
Марк Лонгейр

2
Чувак, це приголомшливо! Це врятувало мій @ $$ саме зараз. І я навчився певної науки про git. Краще дивіться, що ви додаєте до індексу ...
bowsersenior

1
не красти сюди жодного вапняного світла. Але як замітка, оскільки я просто зіткнувся з тією ж проблемою і подумав, що втратив всю свою роботу. Для тих, хто використовує IDE , IDE зазвичай мають рішення відновлення одним кліком. У випадку PHPstorm мені просто потрібно було клацнути правою кнопкою миші каталог і вибрати пункт Показати локальну історію, а потім повернутися до останнього дійсного стану.
Шалом Сам

58

Я щойно зробив git reset --hardі втратив один вчинок. Але я знав хеш-код, і мені вдалося його git cherry-pick COMMIT_HASHвідновити.

Я зробив це протягом декількох хвилин після втрати комісії, тому це може спрацювати для деяких із вас.


5
Дякую, дякую, дякую, Вам вдалося відновити роботу за день завдяки цій відповіді
Dace

21
Напевно, варто згадати, що ви можете побачити ці хеши, використовуючи git reflog, наприклад git reset --hard-> git reflog(дивлячись на HEAD @ {1} хеш) і нарештіgit cherry-pick COMMIT_HASH
Хав'єр Лопес

2
Хлопці, читаючи це, будьте обережні, що це працює лише для одного хеша фіксації, якщо було більше однієї комісії, це не вирішить проблему відразу!
Ain Tohvri

4
Ви можете фактично скинути "вперед". Отже, якщо ви скинули пропущені кілька комісій, виконуючи ще один "git reset --hard <commit>", слід відновити весь ланцюг комісій до цього моменту. (враховуючи, що вони ще не GC'ed)
акимсько

6
Однокласник для відновлення втрачених git reset --hardgit reset --hard @{1}
комітетів

13

Завдяки Марку Лонгейру я повернув свої речі!

Спочатку я зберегла всі хеші у файл:

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

1
Так! Це саме те, що мені було потрібно. Мені подобається сценарій Python за відтворення всіх файлів. Інші відповіді змусили мене нервувати втрату даних зі збиранням сміття, тому скидання файлів для мене виграш :)
Ерік Олсон

1
Я написав цей сценарій на основі цієї відповіді. Виходить
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
Matija Nalis

6

@ Рішення Ajedi32 в коментарях працювало для мене саме в цій ситуації.

git reset --hard @{1}

Зауважте, що всі ці рішення розраховують на відсутність git gc, і деякі з них можуть спричинити одне, тому я застебнув вміст вашого каталогу .git перед тим, як щось спробувати, щоб у вас з'явився знімок, до якого потрібно повернутися, якщо він не Я не працюю для вас.


У мене була низка некомплектних файлів. Тоді я запустив 1 файл, і зробив файл git reset --hard, який переплутав мою репо в будь-який невідомий спосіб. @Duncan, що робить @ {1}? і на який коментар ви звертаєтесь? Це скидання git reset?
alpha_989

Я думаю, що @ {1} є відносним посиланням на останнє, але одне зобов’язання, але я не експерт, я лише повідомляю про те, що працювало на мене
Дункан Макгрегор

3

Зустрівся з тим же номером, але не додав змін до індексу. Тому всі команди, наведені вище, не повернули мені бажаних змін.

Після всіх викладених вище відповідей, це наївний натяк, але, можливо, це врятує того, хто спочатку не замислювався над цим, як я.

У відчаї я намагався натиснути CTRL-Z у своєму редакторі (LightTable) один раз на кожній відкритій вкладці - це, на щастя, відновило файл на цій вкладці, до останнього стану перед git reset --hard. HTH.


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

1

Це, мабуть, очевидно для фахівців з git там, але я хотів це викласти, оскільки під час мого несамовитого пошуку я не бачив цього.

Я інсценізував деякі файли і зробив це git reset --hard, трохи злякався, а потім помітив, що в моєму стані показано, що всі мої файли все ще зберігаються, а також всі їх видалення не встановлені.

На цьому етапі ви можете здійснити ці поетапні зміни, якщо ви не здійснюєте їх видалення. Після цього вам залишається лише набратися сміливості зробити ще git reset --hardодин раз, що поверне вас до тих змін, які ви здійснили і тепер лише здійснили.

Знову ж таки, це, мабуть, для більшості нічого нового, але я сподіваюся, що оскільки мені це допомогло, і я не знайшов нічого, що це підказує, це може допомогти комусь іншому.


1

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

  1. Як згадує chilicuil, запустіть git reflogідентифікуйте там хеш- коміт, до якого ви хочете повернутися

  2. Як згадував акімсько, ви, швидше за все, НЕ захочете вишневого вибору, якщо ви не втратили лише одного зобов'язання, тож вам слід запустити git reset --hard <hash-commit-you-want>

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


0

Вищезазначені рішення можуть спрацювати, однак існують більш прості способи відновити це замість того, щоб пройти gitскладні скасування. Я б здогадався, що більшість git-скидів відбувається у невеликій кількості файлів, і якщо ви вже використовуєте VIM, це може бути найбільш ефективним часом рішення. Застереження полягає в тому, що ви вже повинні використовувати ViM'sстійкий-скасувати, який ви повинні використовувати в будь-якому випадку, оскільки це дає можливість скасувати необмежену кількість змін.

Ось такі кроки:

  1. Натисніть vim :і введіть команду set undodir. Якщо ви persistent undoввімкнули свій .vimrc, це покаже результат, подібний до 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, і зробіть "раніше 20 м". Ви можете знайти більш детальну інформацію про "раніше", скориставшись "h раніше". Тут earlier 20mмається на увазі повернутися до стану файлу 20 хвилин назад, припускаючи, що ви зробили git hard --reset, 20 хвилин назад. Повторіть це над усіма файлами, які були виписані з findкоманди. Я впевнений, що хтось може написати сценарій, який поєднає ці речі.


0

Я використовую IntelliJ і міг просто пройти кожен файл і зробити:

Edit -> reload from disk

На щастя, я щойно зробив git statusправо перед тим, як знищив свої робочі зміни, тому я точно знав, що мені потрібно перезавантажити.


0

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

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

HTH!


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.