Как восстановить сброшенный тайник в Git?
Я часто использую git stash
а также git stash pop
сохранить и восстановить изменения в моем рабочем дереве. Вчера у меня были некоторые изменения в моем рабочем дереве, которые я спрятал и вытолкнул, а затем я сделал больше изменений в своем рабочем дереве. Я хотел бы вернуться и просмотреть вчерашние скрытые изменения, но git stash pop
кажется, чтобы удалить все ссылки на связанный коммит.
Я знаю, что если я использую git stash
затем .git / refs / stash содержит ссылку на коммит, использованный для создания тайника. И .git / logs / refs / stash содержит весь тайник. Но эти ссылки исчезли после git stash pop
, Я знаю, что коммит все еще находится в моем хранилище где-то, но я не знаю, что это было.
Есть ли простой способ восстановить вчерашнюю ссылку на коммит?
Обратите внимание, что сегодня это не критично для меня, потому что у меня ежедневные резервные копии и я могу вернуться к вчерашнему рабочему дереву, чтобы получить свои изменения. Я спрашиваю, потому что должен быть более легкий путь!
29 ответов
После того, как вы узнаете хэш потерянного коммита, вы можете применить его как тайник:
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
если вы предпочитаете хороший график на консоли, а не отдельное приложение с графическим интерфейсом.
Чтобы обнаружить коммиты на stash, ищите сообщения коммитов этой формы:
WIP на somebranch: 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.
Эквивалент Windows PowerShell с использованием gitk:
gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })
Вероятно, есть более эффективный способ сделать это в одной трубе, но это делает работу.
Я только что создал команду, которая помогла мне найти потерянный коммит:
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" ("work" - это моя ветка, 619bb2 - недавний коммит).
Я заметил, что если бы я использовал "git stash apply" вместо "git stash pop", у меня не возникло бы этой проблемы, и если бы я использовал "git stash save message", то фиксация могла бы быть легче найти.
Обновление: с идеей Натана, это становится короче:
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)
Вы можете выполнить еще более быстрый поиск, если ищете только сообщения "WIP on".
Как только вы узнаете свой sha1, вы просто измените свой список хранения stash, чтобы добавить старый stash:
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
Мне больше всего нравится этот однострочник:
git log --oneline $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )
Это в основном та же идея, что и этот ответ, но намного короче. Конечно, вы все еще можете добавить--graph
чтобы получить отображение в виде дерева.
Когда вы найдете коммит в списке, примените его с помощью
git stash apply THE_COMMIT_HASH_FOUND
Для меня использование --no-reflogs
обнаружил потерянную запись в тайнике, но --unreachable
(как было найдено во многих других ответах) нет.
Запустите его на git bash, когда вы находитесь под Windows.
Кредиты: подробности приведенных выше команд взяты из https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf
Вы можете перечислить все недостижимые коммиты, написав эту команду в терминале -
git fsck --unreachable
Проверьте недостижимый хеш коммита -
git show hash
Наконец примените, если вы найдете спрятанный предмет -
git stash apply hash
Мне понравился подход Аристотеля, но мне не нравилось использовать 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
Затем, когда вы найдете commit-id, просто нажмите git stash apply {commit-id}
Надеюсь, это поможет кому-то быстро
Я не смог получить ни одного ответа для работы в 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)
возможно, не лучшее решение, но сработало для меня
Почему люди задают этот вопрос? Потому что они еще не знают или не понимают рефлог.
Большинство ответов на этот вопрос дают длинные команды с параметрами, которые почти никто не запомнит. Таким образом, люди приходят к этому вопросу, копируют и вставляют все, что, по их мнению, им нужно, и почти сразу забывают об этом.
Я бы посоветовал всем с этим вопросом просто проверить reflog (git reflog), не намного более того. Как только вы видите этот список всех коммитов, есть сотни способов узнать, какой коммит вы ищете, и выбрать его или создать из него ветку. В процессе вы узнаете о 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", чтобы перейти к следующему различию.
Вы можете добиться этого за 2 простых шага
Список потерянных тайников -> запустите эту команду для проекта, в котором все тайники были уничтожены:
git fsck --unreachable | grep commit | вырезать -d ' ' -f3 | xargs git log --merges --no-walk
Отправьте потерянный тайник туда, откуда он был -> Давайте использовать хеш фиксации второго тайника:
git update-ref refs / stash 4b3fc45c94caadcc87d783064624585c194f4be8 -m "Мой восстановленный тайник"
Принятый ответ Аристотеля покажет все достижимые коммиты, включая коммиты, не похожие на тайники. Чтобы отфильтровать шум:
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
Это сработало для меня (в 2022 году) с восстановлением моего случайно удаленного тайника в git из среды Windows.
Эти шаги описывают, как восстановить любые удаленные тайники или ветки git (при условии, что они не были окончательно удалены сборщиком мусора).
Перейдите в каталог, где находится ваш проект.
Введите команду:
git fsck --no-reflogs | find "dangling commit"
Появится список хэшей для оборванных коммитов. Они будут состоять из веток и тайников, которые были удалены. Начните с копирования и вставки хэшей в конце списка, чтобы найти свой тайник или ветку. Например, используйте команду:
git log -1 [hash]
Если соответствующий хэш соответствует тому, что вы пытаетесь восстановить, используйте следующую команду для его восстановления:
git stash apply [hash]
Чтобы увидеть коммиты в терминале, мы можем использовать только те, которые нам нужны:
git log --oneline --all --grep="^WIP on .*: [a-f0-9]\+" --grep="^On [^ ]*:" $( env LANG=C git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
Это основано на ответе Аристотеля Пагальциса.
Чтобы увидеть только коммиты тайника, куда они прикреплены и каково их содержимое
образец результата
Checking object directories: 100% (256/256), done.
2022-08-31 10:20:46 +0900 8d02f61 WIP on master: 243b594 add css
A favicon.ico
команда
git fsck --dangling | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ct %h" | sort | awk '{print $2}' | { while read hash; do status=$(git stash show $hash --name-status 2>/dev/null); if (( $? == 0 )); then git show $hash -s --format="%C(green)%ci %C(yellow)%h %C(blue)%B"; echo "$status"; fi; done; }
- Чтобы увидеть полный хэш, измените
%h
к%H
- Чтобы сократить время, хвост fsck как
git fsck --dangling | tail -100 | awk ...
Что я пришел сюда в поисках, так это как вернуть тайник, независимо от того, что я проверил. В частности, я спрятал что-то, затем проверил более старую версию, а затем вытащил ее, но тайник был недоступен в тот более ранний момент времени, поэтому тайник исчез; Я не мог просто сделать 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.
Восстановить его, выполнив следующие действия:
Определите удаленный хэш-код:
gitk --all $ (git fsck --no-reflog | awk '/ dangling commit / {print $ 3}')
Вишневый козлёк:
git cherry-pick -m 1 $ stash_hash_code
Разрешить конфликты, если они есть, используя:
git mergetool
Кроме того, у вас могут возникнуть проблемы с сообщением фиксации, если вы используете gerrit. Пожалуйста, сохраните ваши изменения, прежде чем использовать следующие альтернативы:
- Используйте hard reset к предыдущему коммиту и затем подтвердите это изменение.
- Вы также можете спрятать изменения, перебазировать и подтвердить.
git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP
Попробуй один раз
Зная примерное имя файла и его местоположение, я смог найти удаленные файлы тайников, выбирая оборванные коммиты для пути.
for i in $(git fsck --no-reflogs | awk '/dangling commit/ {print $3}'); do
if git log -5 --name-only -u $i | grep -q "<path-to-files>/.*<partial-file-name>.*"; then
echo "found something in commit $i";
fi;
done
Гитхаб рабочий стол
Если вы спрятали свои изменения с помощью Github Desktop и случайно удалили спрятанные изменения, вы можете запустить следующую команду для поиска висячей фиксации тайника (на основе этого ответа ):
Примечание . Если вы используете Windows, запускайте команды с помощью Git Bash (который устанавливается вместе с установкой git).
git log --graph --oneline --decorate --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) | grep 'GitHub_Desktop'
Вы получите список коммитов, например:
| * b7f1b023 On main: !!GitHub_Desktop<main>
| * 7e949012 On main: !!GitHub_Desktop<main>
| * 7de9b9ca On main: !!GitHub_Desktop<main>
Получите коммит первого в списке. В приведенном выше примере это будетb7f1b023
и запустите следующее:
git stash apply b7f1b023
Это восстановит ваши изменения. Если это не так, вы можете попробовать другие коммиты в выходных данных.
Не совсем ответ, чтобы получить тайник, но если цель состоит в том, чтобы получить незафиксированные изменения, которые сначала были спрятаны, а затем выскочили в другую ветку, но предназначены для обоих и выполняются следующим образом:
- Внесите изменения в
-
git stash
- Сделать ветку_b из
-
git stash apply
Затем, чтобы восстановить незафиксированные изменения вbranch_a
:
-
git checkout branch_a
-
git merge branch_b
-
git reset HEAD~1
Еще один типичный пример использования: вы попытались зайти в неправильную ветку и получили конфликты?
Все, что вам нужно, это отменить всплывающее окно, но при этом сохранить его в списке тайников, чтобы вы могли выложить его в нужную ветку.
Чтобы это исправить, сделайте следующее:
git reset HEAD --hard
git checkout my_correct_branch
git stash pop
Готово!
Вы можете шаг за шагом следовать приведенному ниже процессу:
1- используйте ниже, чтобы перечислить все недостижимые коммиты git fsck --unreachable
2- показать недостижимый хеш фиксации с помощью git show hash
3- скопируйте весь журнал, вы можете увидеть журнал, недоступный blob, фиксацию, дерево.
4- применить git stash с журналом, имеющим хеш фиксации git stash apply [заменить хеш]
Я случайно удалил тайник в приложении GitUP. Просто нажмите Ctrl+Z, чтобы отменить его.
Может, это кому-нибудь поможет;)