Как восстановить упавший тайник в Git?
Я часто использую git stash
и git stash pop
для сохранения и восстановления изменений в моем рабочем дереве. Вчера у меня были некоторые изменения в моем рабочем дереве, которые я спрятал и выскочил, а затем я сделал еще больше изменений в моем рабочем дереве. Я хотел бы вернуться и просмотреть вчерашние спрятанные изменения, но git stash pop
удаляет все ссылки на связанную фиксацию.
Я знаю, что если я использую git stash
затем .git / refs / stash содержит ссылка фиксации, используемая для создания припрятывать. И .git / logs/refs / stash содержит весь притон. Но эти ссылки ушли после git stash pop
. Я знаю, что фиксация все еще находится где-то в моем репозитории, но я не знаю, что это было.
есть ли простой способ восстановить вчерашнюю ссылку на фиксацию тайника?
обратите внимание, что это не критично для меня сегодня, потому что у меня есть ежедневные резервные копии и я могу вернуться к вчерашнему рабочему дереву, чтобы получить мои изменения. Я спрашиваю, потому что там должно быть проще путь!
19 ответов:
если вы только что выскочил его и терминал все еще открыт, вы будете все еще есть хэш-значение, напечатанное
git stash pop
на экране (спасибо, долда).в противном случае, вы можете найти его с помощью этого для Linux и Unix:
git fsck --no-reflog | awk '/dangling commit/ {print }'
и для 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 }' )
это запустит браузер репозитория, показывающий вам каждая фиксация в репозитории когда-либо, независимо от того, доступна она или нет.
вы можете заменить
gitk
там с чем-то вродеgit log --graph --oneline --decorate
если вы предпочитаете хороший график на консоли над отдельным графическим интерфейсом приложение.чтобы определить фиксацию тайника, найдите сообщения фиксации этой формы:
WIP on somebranch:commithash какое-то старое сообщение фиксации
Примечание: сообщение фиксации будет только в этой форме (начиная с "WIP on"), если вы не предоставили сообщение, когда вы сделали
git stash
.после того, как вы знаете хэш коммита, который вы хотите, вы можете применить его в качестве тайника:
git stash apply $stash_hash
или вы можете использовать контекстное меню
gitk
для создания ветвей для любых недостижимых коммитов, которые вас интересуют. После этого вы можете делать с ними все, что захотите, со всеми обычными инструментами. Когда вы закончите, просто сдуйте эти ветки снова.
если вы не закрыли терминал, просто посмотрите на выход из
git stash pop
и у вас будет идентификатор объекта сброшенного тайника. Обычно это выглядит так:$ git stash pop [...] Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)
(обратите внимание, что
git stash drop
также производит такую же линию.)чтобы вернуть этот тайник, просто бегите
git branch tmp 2cae03e
, и вы получите его как ветку. Чтобы преобразовать это в тайник, запустите:git stash apply tmp git stash
наличие его в качестве ветви также позволяет вам свободно манипулировать им; например, для вишневого выбора или слияния оно.
просто хотел сказать, что это дополнение к принятому решению. Это было не сразу очевидно для меня в первый раз, когда я попробовал этот метод (возможно, это должно было быть), но чтобы применить тайник из хэш-значения, просто используйте "git stash apply":
$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219
когда я был новичком в git, мне это было непонятно, и я пробовал разные комбинации "git show", "git apply", "patch" и т. д.
Я только что построил команду, которая помогла мне найти мой потерянный stash commit:
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, а затем показывает сводку каждого из них. С этого момента это был просто вопрос просмотра коммитов, чтобы найти соответствующий "НЗП на работе: 6a9bb2" ("работа" - это моя ветвь, 619bb2-недавняя фиксация).
Я отмечаю, что если я использую "git stash apply "вместо" git stash pop", у меня бы не было эта проблема, и если я использую " git stash save тогда коммит было бы легче найти.
обновление: с идеей Натана это становится короче:
for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
чтобы получить список тайников, которые все еще находятся в вашем репозитории, но больше не доступны:
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.
git fsck --unreachable | grep commit
должен показывать sha1, хотя список, который он возвращает, может быть довольно большим.git show <sha1>
покажет, если это совершить необходимо.
git cherry-pick -m 1 <sha1>
объединит фиксацию в текущей ветке.
если вы хотите восстановить потерянный тайник, вам нужно сначала найти хэш вашего потерянного тайника.
как Аристотель Пагальцис предложил
git fsck
должны помочь вам.лично я использую
log-all
псевдоним, который показывает мне каждый коммит (восстанавливаемые коммиты), чтобы иметь лучшее представление о ситуации:git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)
вы можете сделать еще более быстрый поиск, если вы ищете только "НЗП на" сообщения.
как только вы узнаете свой sha1, вы просто измените свой тайник reflog, чтобы добавить старую заначку :
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' ) refs/stash
мне понравился подход Аристотеля, но не понравилось использовать GITK... как я привык использовать GIT из командной строки.
вместо этого я взял оборванные коммиты и вывел код в файл DIFF для просмотра в моем редакторе кода.
git show $( git fsck --no-reflog | awk '/dangling commit/ {print }' ) > ~/stash_recovery.diff
теперь вы можете загрузить полученный файл diff/txt (его в вашей домашней папке) в свой редактор txt и посмотреть фактический код и результирующий SHA.
тогда просто используйте
git stash apply ad38abbf76e26c803b27a6079348192d32f52219
в 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>
в противном случае вы найдете ID от результата вручную с:
$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show
затем, когда вы найдете commit-id, просто нажмите git stash apply {commit-id}
надеюсь, это поможет кому-то быстро
эквивалент Windows PowerShell с использованием gitk:
gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })
вероятно, есть более эффективный способ сделать это в одном канале, но это делает работу.
Я хочу добавить к принятому решению еще один хороший способ пройти через все изменения, когда у вас либо нет gitk, либо нет X для вывода.
git fsck --no-reflog | awk '/dangling commit/ {print }' > tmp_commits for h in `cat tmp_commits`; do git show $h | less; done
затем вы получаете все различия для этих хэшей, отображаемых один за другим. Нажмите 'q', чтобы перейти к следующему diff.
Почему люди задают этот вопрос? Потому что они не знают или понимают reflog.
большинство ответов на этот вопрос дают длинные команды с опциями, которые почти никто не запомнит. Поэтому люди приходят в этот вопрос и копируют вставку, что они думают, что им нужно, и забывают об этом почти сразу после этого.
Я бы посоветовал всем с этим вопросом, чтобы просто проверить reflog (в Git reflog), не более того. Как только вы увидите этот список всех коммитов там есть сто способов узнать, что вы ищете, и выбрать вишню или создать из нее ветку. В процессе вы узнали о reflog и полезных опций для различных основных команд Git.
принятый ответ Аристотеля покажет все достижимые коммиты, в том числе не-stash-подобные коммиты. Чтобы отфильтровать шум:
git fsck --no-reflog | \ awk '/dangling commit/ {print }' | \ 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..." сообщение.вы можете отобразить дополнительную информацию о каждом commit, например, отобразить сообщение о фиксации или передать его в
git stash show
:git fsck --no-reflog | \ awk '/dangling commit/ {print }' | \ 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
Я не мог получить ни одного из ответов для работы с 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
- сохранить как .летучая мышь файл и запустить его
- скрипт откроет кучу командных окон, показывая каждую фиксацию
- если вы нашли тот, который вы ищете, запустите:
git stash apply (your hash)
может быть, не лучшее решение, но работал для меня
то, что я пришел сюда искать, как на самом деле получить тайник обратно, независимо от того, что я проверил. В частности, я припрятал что-то, затем проверил более старую версию, а затем вытащил ее, но тайник был не-op в тот более ранний момент времени, поэтому тайник исчез; я не мог просто сделать
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
и было немного патч, который я хотел применить на каждом
извлекают его с помощью следующих шагов:
определите удаленный хэш-код тайника:
gitk --all $( git fsck --no-reflog | awk ' / dangling commit / {print $3}')
черри забрать тайник:
git cherry-pick-m 1 $stash_hash_code
разрешить конфликты, если таковые имеются с помощью:
git mergetool
кроме того, у вас могут возникнуть проблемы с сообщением фиксации, если вы используете gerrit. Пожалуйста, спрячьте свои изменения перед следующими альтернативами:
- используйте жесткий сброс для предыдущей фиксации, а затем повторите это изменение.
- вы также можете спрятать изменения, перебазировать и вновь.
вы можете перечислить все недостижимые коммиты, написав эту команду в терминале -
git fsck --unreachable
Проверьте недостижимый хэш фиксации -
git show hash
наконец применить, если вы найдете спрятанный элемент -
git stash apply hash
еще один распространенный случай использования:вы пытались выскочить на неправильную ветку и получили конфликты?
все, что вы хотите, чтобы отменить поп, но по-прежнему держать его в списке тайников, так что вы можете вытащить его на правильной ветке.
чтобы исправить это, сделайте следующее:
git reset HEAD --hard git checkout my_correct_branch git stash pop
готово!