Как восстановить сброшенный тайник в Git?

1434

Я часто использую git stash и git stash pop для сохранения и восстановления изменений в рабочем дереве. Вчера у меня были некоторые изменения в рабочем дереве, которые я спрятал и выскочил, а затем сделал больше изменений в рабочем дереве. Я хотел бы вернуться и просмотреть вчерашние спрятанные изменения, но git stash pop появляется, чтобы удалить все ссылки на связанную фиксацию.

Я знаю, что если я использую git stash, тогда .git/refs/stash содержит ссылку на фиксацию, используемую для создания stash. И .git/logs/refs/stash содержит весь тайник. Но эти ссылки исчезли после git stash pop. Я знаю, что коммит все еще находится в моем репозитории, но я не знаю, что это было.

Есть ли простой способ восстановить вчерашнюю привязку фиксации stash?

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

  • 55
    Примечание на будущее: если вы не хотите терять свои тайники каждый раз, когда вы используете git stash pop , вы можете вместо этого git stash apply . Он делает то же самое, за исключением того, что не удаляет ссылку на примененный тайник.
  • 3
    Здесь все перепробовал, не смог найти тайник, который уже выскочил. Так рад за IntelliJ's jetbrains.com/help/idea/local-history.html
Показать ещё 1 комментарий
Теги:
git-stash
recovery

19 ответов

2326
Лучший ответ

Если вы только что вытащили его, а терминал все еще открыт, вы по- прежнему будете иметь значение хэша, напечатанное git stash pop на экране (спасибо, Dolda).

В противном случае вы можете найти его, используя это для 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 если вы предпочитаете хороший график на консоли в отдельном графическом приложении.

Чтобы выявить фиксации закладок, найдите сообщения фиксации этой формы:

WIP на somebranch: commithash Некоторое старое сообщение коммита

Примечание. Сообщение фиксации будет только в этой форме (начиная с "WIP on"), если вы не представили сообщение, когда вы сделали git stash.

Как только вы узнаете хэш коммита, который вы хотите, вы можете применить его как кэш:

git stash apply $stash_hash

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

  • 22
    Джейдел вынул слова изо рта. Этот пост спас мою работу :) Я просто хотел бы добавить - запоминание даты, когда вы работали над тем, что вы потеряли, облегчает поиск gitk для того, что вы ищете.
  • 0
    Любая причина, по которой я получаю следующее сообщение при попытке выполнить эту команду ?: awk: Термин «awk» не распознается как имя командлета, функции, файла сценария или работающей программы. Проверьте правильность написания имени или, если путь был указан, проверьте правильность пути и повторите попытку. В строке: 1 символ: 24
Показать ещё 18 комментариев
626

Если вы не закрыли терминал, просто посмотрите на результат из git stash pop, и вы получите идентификатор объекта упавшего файла. Обычно это выглядит так:

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

(Обратите внимание, что git stash drop также создает ту же строку.)

Чтобы вернуть этот штамп, просто запустите git branch tmp 2cae03e, и вы получите его как ветку. Чтобы преобразовать это в stash, запустите:

git stash apply tmp
git stash

Наличие его как ветки также позволяет вам свободно манипулировать им; например, чтобы вишнево выбрать или объединить его.

  • 48
    Вы также можете сделать git stash apply commitid затем git stash чтобы получить новый stash.
  • 3
    это также работает для восстановления "git stash drop"
Показать ещё 8 комментариев
240

Просто хотел упомянуть это дополнение к принятому решению. Это было сразу не очевидно для меня в первый раз, когда я попробовал этот метод (может быть, он и должен был быть), но чтобы применить stash из значения хэша, просто используйте "git stash apply":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Когда я был новичком в git, мне это было непонятно, и я пытался использовать разные комбинации "git show", "git apply", "patch" и т.д.

  • 2
    Обратите внимание, что это относится (дух!) Тайник к текущему рабочему дереву. Если дерево загрязнено, вы можете сначала использовать временную ветвь или тайник, применить тайник из SHA-1, снова спрятать, а затем вставить второй в последний тайник (который называется stash @ {1}).
70

Я только что построил команду, которая помогла мне найти потерянную фиксацию:

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/objects, найдите те, которые имеют тип commit, а затем показывает сводку каждого из них. С этой точки зрения было всего лишь рассмотрением коммитов, чтобы найти подходящий "WIP по работе: 6a9bb2" ( "работа" - это моя ветка, 619bb2 - недавняя фиксация).

Я замечаю, что если я использую "git stash apply" вместо "git stash pop", у меня не было бы этой проблемы, и если я использую "git сообщение о сохранении зашифрованного сообщения", тогда коммит может иметь было легче найти.

Обновление: с идеей Натана это становится короче:

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

Чтобы получить список сохраняемых в вашем репозитории файлов, но больше недоступных:

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

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

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

  • 0
    Это отлично сработало для меня - но мне пришлось отредактировать ответ так, чтобы отображался двойной пробел после cut -d . С одним пробелом cut не распознает разделитель.
  • 0
    Я отредактировал, чтобы cut арги еще яснее
Показать ещё 3 комментария
38

git fsck --unreachable | grep commit должен показать sha1, хотя список, который он возвращает, может быть довольно большим. git show <sha1> покажет, является ли это фиксацией, которую вы хотите.

git cherry-pick -m 1 <sha1> объединит фиксацию в текущей ветке.

  • 0
    Ах, это, безусловно, улучшает мой метод! Сейчас у меня 130 недостижимых коммитов, поэтому мне все еще нужна последняя половина моего решения.
23

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

Как сказал Аристотель Пагалтзис, 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
  • 1
    Однако -d\\ должен быть -d\ (или даже более понятным -d' ' )
  • 0
    Получил ошибку: «роковой: неоднозначный аргумент« висящий »: неизвестная ревизия или путь не в рабочем дереве».
Показать ещё 1 комментарий
15

Мне понравился подход Аристотеля, но мне не нравилось использовать 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
13

Эквивалент Windows PowerShell с помощью gitk:

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

Возможно, более эффективный способ сделать это в одном канале, но это делает работу.

12

В OSX с git v2.6.4, я просто запускаю git случайное падение, тогда я нашел его, пробираясь ниже шагов

Если вы знаете имя кошелька, используйте:

$ 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

Затем, когда вы обнаружите, что commit-id просто ударил приложение git stash apply {commit-id}

Надеюсь, что это поможет кому-то быстро

10

Почему люди задают этот вопрос? Потому что они еще не знают или не понимают reflog.

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

Я бы посоветовал всем с этим вопросом просто проверить reflog (git reflog), не намного больше. Как только вы увидите список всех коммитов, существует сто способов узнать, какую фиксацию вы ищете, и вишнево-выбрать или создать ветку из нее. В этом процессе вы узнаете о рефлоге и полезных вариантах для различных основных команд git.

  • 1
    Привет, Робби. Это актуально, если вы работали, попали в тупик и вам нужно вернуться туда, где вы остановились пару недель назад, только чтобы узнать, что вы не можете найти свою скрытую работу - она, вероятно, потерялась где-то в этом другом материале, который вы делали. Reflog хорош, если это недавняя история, но не для длительных промежутков времени.
  • 1
    Эй, Эмрагинс, я согласен, но это был именно тот случай использования ОП. Я не знаю наверняка, как поведут себя другие команды, размещаемые здесь, но моя проблема в том, что они также перестанут работать после очистки ссылки на его скрытый коммит.
Показать ещё 1 комментарий
10

Я хочу добавить к принятому решению еще один хороший способ пройти все изменения, когда у вас либо нет 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", чтобы перейти к следующему разлому.

9

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

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
8

Я не мог получить ответы на работу в Windows в простом командном окне (Windows 7 в моем случае). awk, grep и Select-string не были распознаны как команды. Поэтому я попробовал другой подход:

  • первый запуск: git fsck --unreachable | findstr "commit"
  • скопировать вывод в блокнот
  • найдите замену "unreachable 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 файл и запустить его
  • script откроет кучу окон команд, показывая каждую фиксацию
  • если вы нашли тот, который ищете, запустите: git stash apply (your hash)

может быть не лучшим решением, но работал у меня

5

Вы можете перечислить все недостижимые коммиты, написав эту команду в терминале -

git fsck --unreachable

Проверьте недопустимый хеш фиксации -

git show hash

Наконец, примените, если вы найдете спрятанный предмет -

git stash apply hash
4

Восстановил его, выполнив следующие шаги:

  • Определите удаленный хэш-код:

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

  • Вишня Выберите стэш:

    git cherry-pick -m 1 $stash_hash_code

  • Разрешить конфликты, если они используются:

    git mergetool

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

  • Используйте жесткий reset для предыдущей фиксации, а затем подтвердите это изменение.
  • Вы также можете сохранить изменения, переустановить и подтвердить.
  • 0
    @ miva2 ваши изменения удалили ссылку на самый правильный ответ в этом вопросе. Добавление ссылки обратно в комментарий stackoverflow.com/questions/89332/…
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 not 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.
  • 0
    это ответ или продолжение вопроса?
  • 0
    Немного и того, и другого. Я нашел эту страницу, потому что потерял тайник и пытался вернуть его. Вариант использования для меня - это деление пополам, где я хочу применить изменения перед тестированием на каждом этапе. Я усвоил сложный способ, которым нельзя просто вытолкнуть, протестировать, спрятать, разделить пополам, потому что это может оставить другой коммит в тайнике, следовательно, stash apply .
0

Я случайно удалил тайник в приложении GitUP. Просто нажмите Ctrl + Z, чтобы отменить его.

Может быть, это помогает кому-то;)

0

Другой распространенный вариант использования: вы пытались выскакивать на неправильную ветку и возникали конфликты?

Все, что вы хотите, - это отменить поп, но все равно сохранить его в списке, чтобы вы могли его всплыть на правильной ветке.

Чтобы исправить это, выполните следующие действия:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

Готово!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню