Як відновити скинуту скриньку в Git?


1737

Я часто використовую git stashта git stash popзберігаю та відновлюю зміни у своєму робочому дереві. Вчора у мене на робочому дереві були деякі зміни, які я сховав і вискакував, а потім я змінив своє робоче дерево. Я хотів би повернутися назад і переглянути вчорашні приховані зміни, але git stash popвидається, щоб видалити всі посилання на пов’язані комісії.

Я знаю, що якщо я використовую git stashтоді .git / refs / stash містить посилання на комісію, яка використовується для створення копії. І .git / logs / refs / stash містить всю скриньку. Але ці посилання вже пішли git stash pop. Я знаю, що фіксація все ще знаходиться десь у моєму сховищі, але я не знаю, що це було.

Чи є простий спосіб відновити вчорашню довідку про прихильність?

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


74
Примітка щодо майбутнього: Якщо ви не хочете щоразу втрачати свої статки git stash pop, можете зробити це git stash applyзамість цього. Це робиться те ж саме, за винятком того, що він не видаляє посилання на застосований приклад.
Кевін

3
Спробував усе тут, не міг знайти сховища, яке вже вискочило. Тож радий за IntelliJ's jetbrains.com/help/idea/local-history.html
Хуан Мендес


У мене була ця проблема. Для того, щоб оновити свій репозиторій, я побіг git stash, git pull -r upstream, git push -f origin, git stash pop, і поп сказав «фатальний: журнал для рефов / схованка порожня». 😲 Я спробував купу цих відповідей, нічого не вийшло. Коли я заглянув у .git / refs / stash , SHA був там. Можливо, проблема із позначенням мережевого диска Windows для синхронізації в режимі офлайн? 🤷‍♂️
бріанарій

Відповіді:


2784

Як тільки ви дізнаєтеся хеш скрипту, який ви скинули, можете застосувати його як скрипт:

git stash apply $stash_hash

Або ви можете створити для нього окрему гілку

git branch recovered $stash_hash

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

Пошук хешу

Якщо ви лише щойно вискакували його і термінал все ще відкритий, ви все одноgit stash pop будете друкувати на екрані хеш-значення (спасибі, Долда).

В іншому випадку ви можете знайти його за допомогою Linux, Unix або Git Bash для Windows:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

... або за допомогою Powershell для Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}

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

Найпростіший спосіб знайти приховану комісію - це, мабуть, передати цей список gitk:

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

... або подивіться відповідь emragins, якщо використовуєте Powershell для Windows.

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

Ви можете замінити gitkтам щось на кшталт, git log --graph --oneline --decorateякщо ви віддаєте перевагу приємний графік на консолі над окремим додатком GUI.

Щоб виявити скриньки, знайдіть повідомлення такої форми:

        WIP на деяких галузях : commithash Якесь старе повідомлення про вчинення

Примітка . Повідомлення про фіксацію буде в цій формі (починаючи з "WIP увімкнено"), якщо ви не надіслали повідомлення git stash.


49
Джейдел вийняла слова з моїх уст. Ця публікація врятувала мою роботу :) Я хотів би лише додати - запам'ятовування дати, коли ви працювали над тим, що ви втратили, полегшує перегляд gitk для того, що ви шукаєте.
Шрідхар Сарнобат

4
@Codey: Тому що PowerShell. Я не знаю, чи MsysGit доставляє бінарний файл AWK. Гуглінг каже мені, що щось подібне %{ $_.Split(' ')[2]; }повинно виконувати еквівалент команди {print $3}в цій програмі awkв PowerShell, але у мене немає системи Windows для перевірки цього, і вам все одно потрібен еквівалент для /dangling commit/частини. У будь-якому випадку просто запустіть git fsck --no-reflogі подивіться на вихід. Ви хочете, щоб хеші з рядків "висячий фіксатор <commitID>".
Арістотель Пагальціс

7
Варто зазначити, що повідомлення про фіксацію буде мати рядок "WIP", лише якщо ви не надали власне повідомлення під час перенесення (тобто, виконуючи git stash save "<message>").
Самір Агіяр

12
Якщо ви знаєте, коли випадок трапився, ви можете скористатися цим одностроєм, щоб отримати список звисаючих комітетів, збільшуючи час: git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sortостанній запис, мабуть, той, який ви хочете зробити stash apply.
ris8_allo_zen0

3
git stash apply {ref}відновлено скинуту скриньку! gitнастільки чудово, що це повинно бути незаконно!
Том Расселл

707

Якщо ви не закрили термінал, просто подивіться на вихід з git stash popі ви отримаєте ідентифікатор об'єкта скинутого сховища. Зазвичай це виглядає так:

$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Зверніть увагу, що git stash dropтакож створюється той самий рядок.)

Щоб повернути цю скриньку, просто запустіть її git branch tmp 2cae03e, і ви отримаєте її як гілку. Щоб перетворити це у сховище, запустіть:

git stash apply tmp
git stash

Наявність його як гілки також дозволяє вільно маніпулювати нею; наприклад, щоб його вибрати або злити.


54
Ви також можете зробити git stash apply commitidте , git stashщоб отримати новий схованку.
Метью Флашен

32
Зауважте, що якщо git автоматично зливає скрипт і має конфлікти, він не покаже вам хеш.
Джеймс

31
@James: Знову ж таки, якщо ці конфлікти є результатом запущених ситуацій git stash pop, він також не скине приховування , так що це зазвичай не проблема.
Dolda2000

2
У моєму складному поп-виході не було SHA. :(
Викинути рахунок

2
@Honey: У цьому справа git stash pop. Якщо ви хочете застосувати копію, не скидаючи її, скористайтеся git stash applyзамість цього. Крім того, якщо ви хочете застосувати зміну до декількох гілок, ви також можете замість цього вибрати команду.
Dolda2000

271

Просто хотілося згадати це доповнення до прийнятого рішення. Мені це було не відразу очевидно, коли я вперше спробував цей метод (можливо, це і мав бути), але щоб застосувати скриньку з хеш-значення, просто використовуй "git stash apply":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Коли я був новачок у git, мені це було не зрозуміло, і я намагався різних комбінацій "git show", "git apply", "patch" тощо.


3
Зверніть увагу, що це стосується (так!) Сховища до поточного робочого дерева. Якщо дерево забруднене, ви можете спочатку скористатися тимчасовою гілкою або сховати, застосуйте приховану скриньку від SHA-1, сховайте її ще раз, а потім виведіть другу для останнього сховища (називається приховано @ {1}).
musiKk

111

Щоб отримати список сховищ, які все ще знаходяться у вашому сховищі, але вже недоступні:

git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

Якщо ви дали назву вашому сховищу, замініть "WIP" в -grep=WIPкінці команди частиною вашого повідомлення, наприклад -grep=Tesselation.

Команда прив'язується до "WIP", оскільки за замовчуванням повідомлення про фіксацію прихованої скриньки є у формі WIP on mybranch: [previous-commit-hash] Message of the previous commit.


1
echo 'git fsck - недосяжний | grep фіксування | вирізати -d "" -f3 | xargs git log --merges --no-walk --grep = WIP '> / usr / local / bin / git-stashlog; chmod a + rx / usr / local / bin / git-stashlog # git stashlog
Ерік Мартіно

Або ви можете додати це до свого .gitconfig як псевдонім (перед командою a !).
асмеурер

Збережено мій бекон - ну не дуже, але врятував мене перекодування днів роботи - оцінив - враховуючи, що я нещодавно відкинувся, я тільки що вибрав верхню SHA з виводу вашої команди - тоді .... git stash застосувати SHA ... як згадується в інших відповідях - багато thx
danda74

75

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

for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

У цьому списку перелічуються всі об'єкти у .git / дереві об’єктів, знаходяться ті, які мають тип фіксації, а потім відображається зведення кожного з них. З цього моменту було лише переглядати комітети, щоб знайти відповідний "WIP про роботу: 6a9bb2" ("робота" - це моя галузь, 619bb2 - нещодавнє зобов'язання).

Зауважу, що якщо я використовую "git stash apply" замість "git stash pop", у мене не було б цієї проблеми, і якщо я використовую "git stash save message ", то комісію, можливо, було б легше знайти.

Оновлення: З ідеєю Натана це стає коротшим:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less

41

git fsck --unreachable | grep commit Потрібно показати sha1, хоча список, який він повертає, може бути досить великим. git show <sha1>покаже, чи є це зобов'язання, яке ви хочете.

git cherry-pick -m 1 <sha1> приєднає комітет до поточної гілки.


37

Еквівалент Windows PowerShell за допомогою gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

Напевно, є більш ефективний спосіб зробити це в одній трубі, але це робить свою роботу.


1
Я дуже вдячний за вашу відповідь
Виталий Шебаниц

32

Якщо ви хочете перезавантажити втрачену скриньку, вам потрібно спочатку знайти хеш втраченого сховища.

Як запропонував Арістотель Пагальцис: git fsck слід допомогти.

Особисто я використовую свій log-allпсевдонім, який показує мені кожне зобов'язання (повернення зобов'язань), щоб мати кращий погляд на ситуацію:

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)

Ви можете зробити ще швидший пошук, якщо шукаєте лише повідомлення "WIP on".

Як тільки ви знаєте ваш sha1, ви просто зміните свій рефлекторний засіб, щоб додати старий приклад:

git update-ref refs/stash ed6721d

Ви, мабуть, віддасте перевагу мати пов’язане повідомлення, так -m

git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d

І ви навіть хочете використовувати це як псевдонім:

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1

2
Однак -d\\ має бути -d\ (або навіть зрозуміліше -d' ')
joeytwiddle

Отримала помилку: "fatal: неоднозначний аргумент" звисаючий ": невідома редакція чи шлях не в робочому дереві."
Даніель Райан

вам також потрібно загорнути підкоманду цитатами git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721
Андрій Шостик

18

Мені сподобався підхід Арістотеля, але він не любив використовувати GITK ... як я звик використовувати GIT з командного рядка.

Натомість я взяв звисаючі коміти та вивів код у файл DIFF для ознайомлення у своєму редакторі коду.

git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff

Тепер ви можете завантажити отриманий файл diff / txt (його у домашній папці) у свій редактор txt і побачити фактичний код та отриманий SHA.

Тоді просто використовуйте

git stash apply ad38abbf76e26c803b27a6079348192d32f52219

17

Ви можете перерахувати всі недоступні зобов'язання, записавши цю команду в термінал -

git fsck --unreachable

Перевірте недоступний хеш комісій -

git show hash

Нарешті застосуйте, якщо знайдете прихований предмет -

git stash apply hash

15

Чому люди задають це питання? Тому що вони ще не знають про релог і не розуміють його.

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

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


1
Привіт Робі. Це актуально, якщо ви працювали, займалися стороною і вам потрібно було забрати місце, де ви залишилися пару тижнів тому, щоб дізнатися, що не можете знайти свою приховану роботу - вона, ймовірно, загубилася десь у тому, що ви робили. reflog чудовий, якщо це нещодавня історія, але не на тривалий пробіл.
emragins

1
Ей, emragins, я погоджуюся, але це був саме такий варіант використання ОП. Я не знаю напевно, як будуть поводитися інші команди, розміщені тут, але мої гейси полягали б у тому, що вони також перестануть працювати, як тільки поправка на його приховану комісію очиститься.
RobbyD

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

15

В OSX з git v2.6.4 я просто запускаю git stash drop випадково, тоді я виявив це, пройшовши покроково нижче кроків

Якщо ви знаєте ім'я скриньки, тоді використовуйте:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

інакше ви знайдете ідентифікатор результату вручну за допомогою:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Потім, коли ви знайдете ідентифікатор набору, просто натисніть на git stash, застосуйте {commit-id}

Сподіваюсь, це швидко допоможе комусь


12

Я хочу додати до прийнятого рішення ще один хороший спосіб пройти всі зміни, коли у вас немає доступних gitk або немає X для виведення.

git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits

for h in `cat tmp_commits`; do git show $h | less; done

Потім ви отримуєте всі відмінності для тих хешей, що відображаються один за одним. Натисніть "q", щоб перейти до наступного розл.


12

Я не зміг отримати жодної відповіді для роботи в Windows у простому командному вікні (у моєму випадку Windows 7). awk, grepі Select-stringне були визнані командами. Тому я спробував інший підхід:

  • перший запуск: git fsck --unreachable | findstr "commit"
  • скопіюйте вихід у блокнот
  • знайти замінити "недосяжну комісію" на start cmd /k git show

буде виглядати приблизно так:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • збережіть як .bat файл і запустіть його
  • скрипт відкриє купу вікон команд, показуючи кожну команду
  • якщо ви знайшли потрібного, запустіть: git stash apply (your hash)

можливо, не найкраще рішення, але працював для мене


Ви можете використовувати git bash навіть у Windows. У git bash у вас є всі необхідні інструменти командного рядка (unixoid).
Адріан Ш

10

Прийнята відповідь Арістотеля покаже всі доступні зобов’язання, включаючи нестабільні коміти. Щоб відфільтрувати шум:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3

Це стосуватиметься лише комітетів, у яких є рівно 3 батьківських комітету (у яких зберігатиметься схована скринька) та повідомлення яких включає "WIP on".

Майте на увазі, що якщо ви зберегли сховище повідомленням (наприклад, git stash save "My newly created stash" ), це змінить стандартне повідомлення "WIP on ...".

Ви можете відображати більше інформації про кожне введення, наприклад, відображати повідомлення про фіксацію або передавати його git stash show:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3 | \
xargs -n1 -I '{}' bash -c "\
  git log -1 --format=medium --color=always '{}'; echo; \
  git stash show --color=always '{}'; echo; echo" | \
less -R

6

Мій улюблений такий одноколісний:

git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )

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

Коли ви знайшли комісію в списку, зверніться за допомогою

git stash apply THE_COMMIT_HASH_FOUND

Для мене, використовуючи, --no-reflogsбуло виявлено втрачений прихований запис, але --unreachable(як це знайдено у багатьох інших відповідях), це не було.

Запустити його на git bash, коли ви перебуваєте під Windows.

Кредити: подробиці вищезазначених команд взяті з https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf


5

Відновлено його, виконавши наступні кроки:

  1. Визначте видалений хеш-код:

    gitk --all $ (git fsck --no-reflog | awk '/ висячий фіксатор / {print $ 3}')

  2. Вишня Виберіть скриньку:

    git cherry-pick -m 1 $ stash_hash_code

  3. Вирішуйте конфлікти, якщо такі використовуються:

    git mergetool

Крім того, у вас можуть виникнути проблеми з повідомленням про фіксацію, якщо ви використовуєте gerrit. Будь ласка, приховайте свої зміни перед наступними альтернативами:

  1. Скористайтеся жорстким скиданням до попередньої фіксації та повторіть цю зміну.
  2. Ви також можете сховати зміни, перезавантажити та повторно.

@ miva2 ваша редакція видалила посилання на найбільш правильну відповідь у цьому питанні. Додавання посилання назад в коментарі stackoverflow.com/questions/89332 / ...
Abhijeet

4

Що я сюди прийшов шукати - це як насправді повернути сховку назад, незалежно від того, що я перевірив. Зокрема, я щось приховав, потім перевірив старішу версію, потім перекинув її, але сховище не було в той час, тому сховище зникла; Я не міг просто зробити, git stashщоб відсунути його назад на стек. Це працювало для мене:

$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179)
$ git checkout 27f6bd8ba3c
$ git reset HEAD^    # Make the working tree differ from the parent.
$ git stash # Put the stash back in the stack.
Saved working directory and index state WIP on (no branch): c2be516 Some message.
HEAD is now at c2be516 Some message.
$ git checkout somethingOld # Now we are back where we were.

В ретроспективі я мав би використовувати git stash applyне git stash pop. Я робив bisectі мав невеликий пластир, який хотів застосувати на кожному bisectкроці. Зараз я роблю це:

$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.

це відповідь чи продовження питання?
Алекс Браун

Трохи обох. Я знайшов цю сторінку, бо втратив сховище і намагався її повернути. У випадку використання для мене є бісектриса, де я хочу застосувати зміни перед тестуванням на кожному кроці. Я дізнався важкий шлях, що ви не можете просто поп, тестувати, приховувати, розбивати, тому що це може залишити інший комітет на сховищі, отже stash apply.
Бен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.