Во-первых, давайте выясним, что такое HEAD и что он означает, когда он отсоединен.
HEAD - это символическое имя для текущего извлеченного коммита. Когда HEAD не отсоединен («нормальная» 1 ситуация: у вас есть ветвь извлечена), HEAD фактически указывает на «ref» ветки, а ветвь указывает на коммит. Таким образом, HEAD «привязан» к ветке. Когда вы делаете новый коммит, ветка, на которую указывает HEAD, обновляется, чтобы указывать на новый коммит. HEAD следует автоматически, так как он просто указывает на ветку.
git symbolic-ref HEAD
доходность refs/heads/master
Ветвь с именем «мастер» извлечена.
git rev-parse refs/heads/master
yield 17a02998078923f2d62811326d130de991d1a95a
Этот коммит является текущим наконечником или «головой» главной ветви.
git rev-parse HEAD
также дает 17a02998078923f2d62811326d130de991d1a95a
это то, что значит быть «символическим реф». Он указывает на объект через какую-то другую ссылку.
(Символические ссылки изначально были реализованы как символические ссылки, но позже были заменены простыми файлами с дополнительной интерпретацией, чтобы их можно было использовать на платформах, которые не имеют символических ссылок.)
У нас есть HEAD
→ refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
Когда HEAD отсоединен, он указывает непосредственно на коммит, а не косвенно указывает на один через ветвь. Вы можете думать об отделенной голове как о неназванной ветви.
git symbolic-ref HEAD
не удается с fatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
доходность 17a02998078923f2d62811326d130de991d1a95a
Поскольку это не символьная ссылка, он должен указывать непосредственно на сам коммит.
У нас есть HEAD
→17a02998078923f2d62811326d130de991d1a95a
Важно помнить с отсоединенным HEAD, что если коммит, на который он указывает, в противном случае не имеет ссылки (ни один другой реф не может его достичь), то он станет «висящим», когда вы извлекаете какой-то другой коммит. В конечном итоге такие оборванные коммиты будут удалены в процессе сборки мусора (по умолчанию они хранятся не менее 2 недель и могут храниться дольше, если на них ссылается reflog HEAD).
1
Совершенно нормально выполнять «обычную» работу с отдельным HEAD, вам просто нужно следить за тем, что вы делаете, чтобы не выискивать выпавшую историю из reflog.
Промежуточные шаги интерактивной перебазировки выполняются с помощью отдельного HEAD (частично, чтобы не загрязнять рефлог активной ветки). Если вы завершите полную операцию перебазировки, она обновит вашу исходную ветвь с совокупным результатом операции перебазирования и снова присоединит HEAD к исходной ветке. Я предполагаю, что вы никогда полностью не завершили процесс ребазирования; это оставит вас с отключенным HEAD, указывающим на коммит, который был недавно обработан операцией rebase.
Чтобы выйти из вашей ситуации, вы должны создать ветку, которая указывает на коммит, на который в данный момент указывает ваша отдельная голова:
git branch temp
git checkout temp
(эти две команды могут быть сокращены как git checkout -b temp
)
Это присоединит вашу ГОЛОВУ к новой temp
ветке.
Затем вы должны сравнить текущий коммит (и его историю) с обычной веткой, над которой вы ожидали работать:
git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp
(Возможно, вы захотите поэкспериментировать с параметрами журнала: добавить -p
, отменить, --pretty=…
чтобы просмотреть все сообщения журнала и т. Д.)
Если ваша новая temp
ветка выглядит хорошо, вы можете обновить (например), master
чтобы указать на нее:
git branch -f master temp
git checkout master
(эти две команды могут быть сокращены как git checkout -B master temp
)
Затем вы можете удалить временную ветку:
git branch -d temp
Наконец, вы, вероятно, захотите добавить восстановленную историю:
git push origin master
Возможно, вам придется добавить --force
в конец этой команды команду push, если удаленная ветвь не может быть «быстро перенаправлена» к новому коммиту (т. Е. Вы удалили или переписали какой-либо существующий коммит или иным образом переписали некоторый фрагмент истории).
Если вы выполняли операцию перебазирования, вам, вероятно, следует ее очистить. Вы можете проверить, была ли перебазировка в процессе поиска по каталогу .git/rebase-merge/
. Вы можете вручную очистить выполняющуюся перебазировку, просто удалив этот каталог (например, если вы больше не помните цель и контекст активной операции перебазировки). Обычно вы использовали бы git rebase --abort
, но это делает некоторую дополнительную перезагрузку, которую вы, вероятно, хотите избежать (она перемещает HEAD обратно в исходную ветвь и сбрасывает ее обратно в исходную фиксацию, что отменит часть работы, которую мы сделали выше).