Как восстановить упавший штрих в 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, чтобы отменить его.
Может быть, это помогает кому-то;)