Вопрос

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

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

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

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

Это было полезно?

Решение

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

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 если вы предпочитаете красивый график на консоли отдельному приложению с графическим интерфейсом.

Чтобы обнаружить коммиты в тайнике, найдите сообщения о коммитах такого вида:

НЗП включен какая-то ветка: commithash Какое-то старое сообщение о коммите

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

Другие советы

Если вы не закрывали терминал, просто посмотрите на вывод 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» и т. д.

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

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.

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

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 должен показать 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, вы просто измените свой журнал тайника, чтобы добавить старый тайник:

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

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

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

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

Мне понравился подход Аристотеля, но не понравилось использовать GITK...как я привык использовать GIT из командной строки.

Вместо этого я взял висячие коммиты и вывел код в файл DIFF для проверки в своем редакторе кода.

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

Теперь вы можете загрузить полученный файл diff/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>

в противном случае вы найдете идентификатор из результата вручную с помощью:

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

Затем, когда вы найдете идентификатор фиксации, просто нажмите на git-stash, примените {commit-id}

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

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

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

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

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

git fsck --unreachable

Проверить недостижимый хеш коммита –

git show hash

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

git stash apply hash

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

Вы можете отобразить дополнительную информацию о каждом коммите, например.отобразить сообщение о фиксации или передать его 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

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

возможно, это не лучшее решение, но мне помогло

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

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

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

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

  2. Черри забери тайник:

    git Cherry-Pick -m 1 $stash_hash_code

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

    git mergetool

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

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

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

Может кому-то поможет ;)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top