Я часто использую git stash
и git stash pop
для сохранения и восстановления изменений в рабочем дереве. Вчера у меня были некоторые изменения в рабочем дереве, которые я спрятал и выскочил, а затем сделал больше изменений в рабочем дереве. Я хотел бы вернуться и просмотреть вчерашние спрятанные изменения, но git stash pop
появляется, чтобы удалить все ссылки на связанную фиксацию.
Я знаю, что если я использую git stash
, тогда .git/refs/stash содержит ссылку на фиксацию, используемую для создания stash. И .git/logs/refs/stash содержит весь тайник. Но эти ссылки исчезли после git stash pop
. Я знаю, что коммит все еще находится в моем репозитории, но я не знаю, что это было.
Есть ли простой способ восстановить вчерашнюю привязку фиксации stash?
Обратите внимание, что это не важно для меня сегодня, потому что у меня есть ежедневные резервные копии и можно вернуться к вчерашнему рабочему дереву, чтобы получить мои изменения. Я спрашиваю, потому что должен быть более простой способ!
Если вы только что вытащили его, а терминал все еще открыт, вы по- прежнему будете иметь значение хэша, напечатанное 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
для создания ветвей для любых недостижимых коммитов, которые вас интересуют. После этого вы можете делать с ними все, что хотите, со всеми обычными инструментами. Когда вы закончите, просто удалите эти ветки снова.
Если вы не закрыли терминал, просто посмотрите на результат из 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
Наличие его как ветки также позволяет вам свободно манипулировать им; например, чтобы вишнево выбрать или объединить его.
git stash apply commitid
затем git stash
чтобы получить новый stash.
Просто хотел упомянуть это дополнение к принятому решению. Это было сразу не очевидно для меня в первый раз, когда я попробовал этот метод (может быть, он и должен был быть), но чтобы применить stash из значения хэша, просто используйте "git stash apply":
$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Когда я был новичком в git, мне это было непонятно, и я пытался использовать разные комбинации "git show", "git apply", "patch" и т.д.
Я только что построил команду, которая помогла мне найти потерянную фиксацию:
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
Чтобы получить список сохраняемых в вашем репозитории файлов, но больше недоступных:
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.
cut -d
. С одним пробелом cut
не распознает разделитель.
cut
арги еще яснее
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)
Вы можете выполнить еще более быстрый поиск, если ищете только сообщения "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
-d\\
должен быть -d\
(или даже более понятным -d' '
)
Мне понравился подход Аристотеля, но мне не нравилось использовать 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
Эквивалент Windows PowerShell с помощью gitk:
gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })
Возможно, более эффективный способ сделать это в одном канале, но это делает работу.
В 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}
Надеюсь, что это поможет кому-то быстро
Почему люди задают этот вопрос? Потому что они еще не знают или не понимают reflog.
Большинство ответов на этот вопрос дают длинные команды с вариантами, которые почти никто не запомнит. Поэтому люди приходят к этому вопросу и копируют все, что им кажется нужным, и забывают об этом почти сразу после.
Я бы посоветовал всем с этим вопросом просто проверить reflog (git reflog), не намного больше. Как только вы увидите список всех коммитов, существует сто способов узнать, какую фиксацию вы ищете, и вишнево-выбрать или создать ветку из нее. В этом процессе вы узнаете о рефлоге и полезных вариантах для различных основных команд git.
Я хочу добавить к принятому решению еще один хороший способ пройти все изменения, когда у вас либо нет 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", чтобы перейти к следующему разлому.
Принятый ответ Аристотеля покажет все достижимые коммиты, в том числе несташные коммиты. Чтобы отфильтровать шум:
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
Я не мог получить ответы на работу в 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
git stash apply (your hash)
может быть не лучшим решением, но работал у меня
Вы можете перечислить все недостижимые коммиты, написав эту команду в терминале -
git fsck --unreachable
Проверьте недопустимый хеш фиксации -
git show hash
Наконец, примените, если вы найдете спрятанный предмет -
git stash apply hash
Восстановил его, выполнив следующие шаги:
Определите удаленный хэш-код:
gitk --all $(git fsck --no-reflog | awk/dangling commit/{print $3} ')
Вишня Выберите стэш:
git cherry-pick -m 1 $stash_hash_code
Разрешить конфликты, если они используются:
git mergetool
Кроме того, у вас могут возникнуть проблемы с сообщением фиксации, если вы используете gerrit. Пожалуйста, спрячьте свои изменения, прежде чем следовать следующим альтернативам:
То, что я здесь искал, - это то, как на самом деле вернуть кошелек обратно, независимо от того, что я проверил. В частности, я что-то спрятал, затем проверил более старую версию, а затем вынул ее, но в тот более ранний момент тайник был не-оператором, поэтому тарелка исчезла; Я не мог просто сделать 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.
stash apply
.
Я случайно удалил тайник в приложении GitUP. Просто нажмите Ctrl + Z, чтобы отменить его.
Может быть, это помогает кому-то;)
Другой распространенный вариант использования: вы пытались выскакивать на неправильную ветку и возникали конфликты?
Все, что вы хотите, - это отменить поп, но все равно сохранить его в списке, чтобы вы могли его всплыть на правильной ветке.
Чтобы исправить это, выполните следующие действия:
git reset HEAD --hard
git checkout my_correct_branch
git stash pop
Готово!
git stash pop
, вы можете вместо этогоgit stash apply
. Он делает то же самое, за исключением того, что не удаляет ссылку на примененный тайник.