Как восстановить упавший штрих в Git?

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

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

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

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

Ответы

Ответ 1

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

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];}

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

Самый простой способ найти нужный вам stash коммит - это, вероятно, передать этот список gitk:

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

... или посмотрите ответ от emragins при использовании Powershell для Windows.

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

Вы можете заменить gitk на что-то вроде git log --graph --oneline --decorate, если вы предпочитаете красивый график на консоли, а не отдельное приложение с графическим интерфейсом.

Чтобы обнаружить тайные коммиты, ищите коммитные сообщения этой формы:

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

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

Ответ 2

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

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

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

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

git stash apply tmp
git stash

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

Ответ 3

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

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

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

Ответ 4

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

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.

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Как сказал Аристотель Пагалтзис, 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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

git fsck --unreachable

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

git show hash

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

git stash apply hash

Ответ 11

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

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

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

Ответ 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}

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

Ответ 13

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

Ответ 14

Я не мог получить ответы на работу в 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)

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

Ответ 15

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

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

Ответ 16

То, что я здесь искал, - это то, как на самом деле вернуть кошелек обратно, независимо от того, что я проверил. В частности, я что-то спрятал, затем проверил более старую версию, а затем вынул ее, но в тот более ранний момент тайник был не-оператором, поэтому тарелка исчезла; Я не мог просто сделать git stash, чтобы вернуть его обратно в стек. Это сработало для меня:

$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/[email protected]{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.

Ответ 17

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

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

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

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

    git cherry-pick -m 1 $stash_hash_code

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

    git mergetool

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

  • Используйте жесткий reset для предыдущей фиксации, а затем подтвердите это изменение.
  • Вы также можете сохранить изменения, переустановить и подтвердить.

Ответ 18

Мой фаворит это одна строка:

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

Это в основном та же идея, что и в этом ответе, но гораздо короче. Конечно, вы все еще можете добавить --graph, чтобы получить древовидное отображение.

Когда вы найдете коммит в списке, нажмите

git stash apply THE_COMMIT_HASH_FOUND

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

Запустите его на git bash, когда вы находитесь под Windows.

Кредиты: Детали вышеупомянутых команд взяты из https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf

Ответ 19

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

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

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

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

Готово!

Ответ 20

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

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