git head detached at что значит
Машина времени в git
В последнее время мои коллеги начинают знакомство с git’ом. И один из интересующих их вопросов — как откатиться до определённой ревизии. В интернете можно найти набор команд, но хочется, чтобы было понимание каждой из них. Баловство с комадами git’а без понимания может привести к потере истории разработки.
Здесь кружочками обозначены коммиты. Чем правее коммит, тем он новее. Коммит с хэшем 6e04e..-это самый первый коммит. Одно из основных понятий, которое стоит уяснить себе новичку, — это указатели на коммиты, а точнее некоторое «прозвище» того или иного коммита. Их тьма тьмущая, например: HEAD, master, FETCH_HEAD, ORIG_HEAD и т.д. Это я перечислил крупицу стандартных прозвищ. Их можно создавать и самим, но об этом впереди.
Заострим наше внимание на двух указателях: master и HEAD. master указывает на самый старший коммит в ветке под названием master (эта ветка создаётся при инициализации репозитория). HEAD указывает на указатель master (читай, текущее состояние файлов). После появления первого коммита в репозитории, HEAD и master указывают на один и тот же коммит. И так будет продолжать до тех пор, пока не переключимся на другую ветку, не откатимся по истории, либо не совершим ряд необдуманных действий. Итак, проиллюстрируем нашу историю с указателями:
Перенос указателя HEAD ( git checkout )
Откат по истории коммитов:
Неужели мы потеряли всю историю? Как узнать самый «новый» коммит? Это не проблема — есть выход, и их несколько:
Для прояснения механизма git checkout создадим новую ветку devel:
Заметим, что указатель HEAD указывает на вершину ветки devel.
Породим в новой ветке несколько коммитов. История репозитория будет выглядеть следующим образом:
Возвращение в ветку master происходит также безболезненно:
2 :
2 :
ORIG_HEAD полезен для редактирования неверных коммитов на локальной машине (!). Предположим, что мы хотим объединить два последних коммита в единый. Для этого, сохраняя текущее состояние файлов, переводим указатель master на два коммита назад:
Посмотрим на изменения:
Ну а теперь сделаем трюк — объединяем коммиты
Вводим сообщение, сохраняемся. Теперь наша история выглядит вот так:
Важное замечание — ORIG_HEAD по-прежнему указывает на коммит d79fb… Если мы сейчас выполним команду git checkout ORIG_HEAD, то мы получим так называемое состояние detach HEAD. Оно характеризуется тем, что HEAD указывает не на вершину ветки, а просто на коммит. HEAD всегда должен указывать только на вершину какой-либо ветки!
Удачных вам путешествий по истории своего репозитория!
При подготовке материала использовались следующие источники:
Самый лучший manual — книга: ProGit
Наглядная справка по git: A Visual Git Reference (Русская версия)
UPD:
В комментариях посоветовали ещё один полезный ресурс по git`у: githowto
Git: наглядная справка
Если вы не видите иллюстраций, попробуйте переключиться на версию со стандартными картинками (без SVG).
SVG изображения были отключены. (Включить SVG изображения)
На этой странице представлена краткая наглядная справка для наиболее часто используемых команд git. Если у вас уже есть небольшой опыт работы с git, эта страница поможет вам закрепить ваши знания. Если вам интересно, как был создан этот сайт, загляните на мой репозиторий на GitHub.
Содержание
Основные команды
Следующие четыре команды предназначены для копирования файлов между рабочей директорией, сценой, также известной как «индекс», и историей, представленной в форме коммитов.
Также можно перепрыгнуть через сцену и сразу же получить файлы из истории прямо в рабочую директорию или сделать коммит, минуя сцену.
Соглашения
Иллюстрации в этой справке выдержаны в единой цветовой схеме.
Коммиты раскрашены зелёным цветом и подписаны 5-ти буквенными идентификаторами. Каждый коммит указывает на своего родителя зелёной стрелочкой. Ветки раскрашены оранжевым цветом; ветки указывают на коммиты. Специальная ссылка HEAD указывает на текущую ветку. На иллюстрации вы можете увидеть последние пять коммитов. Самый последний коммит имеет хеш ed489. main (текущая ветка) указывает на этот коммит, stable (другая ветка) указывает на предка main-ового коммита.
Подробно о командах
Есть много способов посмотреть изменения между коммитами. Ниже вы увидите несколько простых примеров. К каждой из этих команд можно добавить имена файлов в качестве дополнительного аргумента. Так мы выведем информацию об изменениях только для перечисленных файлов.
Commit
Когда вы делаете коммит, git создаёт новый объект коммита, используя файлы со сцены, а текущей коммит становится родителем для нового. После этого указатель текущей ветки перемещается на новый коммит. Вы это видите на картинке, где main — это текущая ветка. До совершения коммита main указывал на коммит ed489. После добавления нового коммита f0cec, родителем которого стал ed489, указатель ветки main был перемещён на новый коммит.
То же самое происходит, если одна ветка является предком другой ветки. Ниже показан пример нового коммита 1800b в ветке stable, которая является предком ветки main. После этого ветка stable уже больше не является предком ветки main. И в случае необходимости объединения работы, проделанной в этих разделённых ветках, вам следует воспользоваться командой merge (что более предпочтительно) или rebase.
Четвертый случай коммита из состояния «detached HEAD» будет рассмотрен далее.
Checkout
Команда checkout используется для копирования файлов из истории или сцены в рабочую директорию. Также она может использоваться для переключения между ветками.
foo.c копирует файл foo.c из коммита HEAD
(предка текущего коммита) в рабочую директорию и на сцену. Если имя коммита не указано, то файл будет скопирован со сцены в рабочую директорию. Обратите внимание на то, что при выполнении команды checkout позиция указателя текущей ветки (HEAD) остаётся прежней, указатель никуда не перемещается.
В том случае, если мы не указываем имя файла, но указываем имя локальной ветки, то указатель HEAD будет перемещён на эту ветку, то есть мы переключимся на эту ветку. При этом сцена и рабочая директория будут приведены в соответствие с этим коммитом. Любой файл, который присутствует в новом коммите (a47c3 ниже), будет скопирован из истории; любой файл, который был в старом коммите (ed489), но отсутствует в новом, будет удалён; любой файл, который не записан ни в одном коммите, будет проигнорирован.
В том случае, если мы не указываем имя файла, и не указываем имя локальной ветки, а указываем тег, дистанционную (remote) ветку, SHA-1 хеш коммита или что-то вроде main
Коммит из состояния «Detached HEAD»
Когда мы находимся в состоянии оторванной головы (Detached HEAD), коммит совершается по тем же правилам, что и обычно, за исключением одной маленькой особенности: ни один указатель ветки не будет изменён или добавлен к новому коммиту. Вы можете представить эту ситуацию как работу с анонимной веткой.
Если после такого коммита вы переключитесь в ветку main, то коммит 2eecb, совершённый из состояния «Detached HEAD», потеряется и попросту будет уничтожен очередной сборкой мусора только потому, что нет ни одного объекта, который бы на него ссылался: ни ветки, ни тега.
Reset
Команда reset перемещает указатель текущей ветки в другую позицию и дополнительно может обновить сцену и рабочую директорию. Эту команду можно также использовать для того, чтобы скопировать файл из истории на сцену, не задевая рабочую директорию.
Merge
Команда merge (слияние) создает новый коммит на основе текущего коммита, применяя изменения других коммитов. Перед слиянием сцена должна быть приведена в соответствие с текущим коммитом. Самый простой случай слияния — это когда другой коммит является предком текущего коммита: в этом случае ничего не происходит. Другой простой случай слияния — когда текущий коммит является предком другого коммита: в этом случае происходит быстрая перемотка (fast-forward). Ссылка текущей ветки будет просто перемещена на новый коммит, а сцена и рабочая директория будут приведены в соответствие с новым коммитом.
Во всех других случаях выполняется «настоящее» слияние. Вы можете изменить стратегию слияния, но по умолчанию будет выполнено «рекурсивное» слияние, для которого будет взят текущий коммит (ed489 ниже на схеме), другой коммит (33104) и их общий предок (b325c); и для этих трех коммитов будет выполнено трёхстороннее слияние. Результат этого слияния будет записан в рабочую директорию и на сцену, и будет добавлен результирующий коммит со вторым родителем (33104).
Cherry Pick
Команда cherry-pick («вишенка в тортике») создаёт новый коммит на основе только одного сладкого «коммита-вишенки», применив все его изменения и сообщение.
Rebase
Перебазирование (rebase) — это альтернатива слиянию для задач объединения нескольких веток. Если слияние создаёт новый коммит с двумя родителями, оставляя нелинейную историю, то перебазирование применяет все коммиты один за одним из одной ветки в другую, оставляя за собой линейную историю коммитов. По сути это автоматическое выполнение нескольких команд cherry-pick подряд.
На схеме выше вы видите как команда берёт все коммиты, которые есть в ветке topic, но отсутствуют в ветке main (коммиты 169a6 and 2c33a), и воспроизводит их в ветке main. Затем указатель ветки перемещается на новое место. Следует заметить, что старые коммиты будут уничтожены сборщиком мусора, если на них уже ничего не будет ссылаться.
Технические заметки
Содержание файлов не хранится в индексе (.git/index) или в объектах коммитов. Правильнее было бы сказать, что каждый файл хранится в базе данных объектов (.git/objects) в двоичном представлении; найти этот файл можно по его SHA-1 хешу. В файле индекса записаны имена файлов, их хеши и дополнительная информация. В информации о коммитах вы встретите тип данных дерево, для идентификации которого также используется SHA-1 хеш. Деревья описывают директории в рабочей директории, а также содержат информацию о других деревьях и файлах, принадлежащих обозначенному дереву. Каждый коммит хранит идентификатор своего верхнего дерева, которое содержит все файлы и другие деревья, изменённые в этом коммите.
Copyright © 2010, Mark Lodato. Russian translation © 2012, Alex Sychev.
Это произведение доступно по лицензии Creative Commons Attribution-NonCommercial-ShareAlike (Атрибуция — Некоммерческое использование — С сохранением условий) 3.0 США.
Git head detached at что значит
Существует так же множество других указателей и один из них HEAD. Это очень важный указатель. И вот о нем мы и поговорим.
Итак, у нас есть история коммитов:
То же самое можно увидеть в Git:
Оба указателя master и HEAD указывают на коммит С4 (efaaf18).
Теперь передвинем указатель HEAD на коммит C2 командой
$ git checkout 7bbbd68
На диаграмме это можно изобразить так:
И Git порекомендовал нам создать новую ветку, а так же сообщил хэш коммита на который сейчас указывает HEAD.
Все это можно увидеть в Git:
Посмотреть историю перемещения головы можно командой git reflog :
На скрине мы так же посмотрели состояние Git и он нам (аж красным) сообщил, что башка отсоединена на коммит 7bbbd68.
Указатель HEAD по существу указывает на тот коммит, после которого будет сделан следующий коммит. И если в состоянии перемещенного HEAD мы сейчас сделаем еще один коммит, то у нас будет шанс потерять его, не в смысле что он будет не доступен, а что если мы не будем помнить хэш этого коммита, то мы ни когда не сможем на него переключится.
Давайте сделаем коммит и посмотрим что будет. Изменим файлик и посмотрим статус
Ну и делаем коммит
Из лога коммитов видно что сейчас HEAD указывает на коммит С5 (84b361c), но на этот коммит не указывает ни какая ветка, верней сказать ни какой указатель ветки.
Графически это можно изобразить так:
Мы по прежнему находимся в состоянии detached HEAD, о чем нам все время напоминает Git. Еще раз напомню что это означает что HEAD указывает не на вершину какой-либо ветки, а просто на коммит. В нашем случае на коммит С5 (84b361c).
Выйти из состояния detached HEAD очень легко, для этого надо переключится на какую-либо ветку или создать новую ветку.
Давайте переключимся на ветку master командой git checkout master
При переключении на ветку master, Git заботливо нас предупредил, что мы оставляем 1 commit, который не присоединен ни к какой ветке. Сообщил нам имя этого коммита – С5 и его хэш – 84b361c. И посоветовал, что возможно уже самое время создать новую ветку командой:
git branch new_branch_name 84b361c
Поскольку если мы сейчас не создадим ветку (указатель) на этом коммите, то мы можем его потерять.
Если сейчас дать команду просмотра лога коммитов, то мы коммит С5 в логе не увидим:
Таким образом мы можем “потерять” коммит С5, если забудем его хэш. Конечно, как уже говорилось, что в Git какой-либо сделанный коммит сложно потерять (но можно). И в данном случае мы можем посмотреть историю перемещения HEAD:
и в ней мы можем увидеть наш потерянный коммит, хотя мы его и не видели в истории коммитов (логе коммитов).
Теперь дадим команду
$ git branch lost_branch 84b361c
Теперь, все хорошо. На коммит С5 указывает ветка lost_branch
Графически это выглядит так:
Теперь мы легко можем переключится на ветку lost_branch и состояния HEAD deatached уже не будет, поскольку HEAD уже будет указывать на вершину ветки lost_branch.
Сейчас переключение произошло безболезненно, поскольку это вполне штатная работа в Git.
Ну и покажу это состояние графически:
На этом, с отсоединенным указателем HEAD пока все. Хотя есть еще варианты по отделению указателя HEAD в Git, но об этом как-нибудь в другой раз.
How do I fix a Git detached head?
I was doing some work in my repository and noticed a file had local changes. I didn’t want them anymore so I deleted the file, thinking I can just checkout a fresh copy. I wanted to do the Git equivalent of
Using git pull didn’t seem to work. Some random searching led me to a site where someone recommended doing
( src is the directory containing the deleted file).
Now I find out I have a detached head. I have no idea what that is. How can I undo?
25 Answers 25
Detached head means you are no longer on a branch, you have checked out a single commit in the history (in this case the commit previous to HEAD, i.e. HEAD^).
If you want to delete your changes associated with the detached HEAD
You only need to checkout the branch you were on, e.g.
Next time you have changed a file and want to restore it to the state it is in the index, don’t delete the file first, just do
This will restore the file foo to the state it is in the index.
If you want to keep your changes associated with the detached HEAD
If you have changed files you don’t want to lose, you can push them. I have committed them in the detached mode and after that you can move to a temporary branch to integrate later in master.
A solution without creating a temporary branch.
How to exit (“fix”) detached HEAD state when you already changed something in this mode and, optionally, want to save your changes:
Commit changes you want to keep. If you want to take over any of the changes you made in detached HEAD state, commit them. Like:
Discard changes you do not want to keep. The hard reset will discard any uncommitted changes that you made in detached HEAD state:
(Without this, step 3 would fail, complaining about modified uncommitted files in the detached HEAD.)
Check out your branch. Exit detached HEAD state by checking out the branch you worked on before, for example:
Take over your commits. You can now take over the commits you made in detached HEAD state by cherry-picking, as shown in my answer to another question.
Detached head means:
If you have no changes: you can switch to master by applying the following command
If you have changes that you want to keep:
In case of a detached HEAD, commits work like normal, except no named branch gets updated. To get master branch updated with your committed changes, make a temporary branch where you are (this way the temporary branch will have all the committed changes you have made in the detached HEAD), then switch to the master branch and merge the temporary branch with the master.
HEAD is a pointer, and it points — directly or indirectly — to a particular commit:
Attached HEAD means that it is attached to some branch (i.e. it points to a branch).
Detached HEAD means that it is not attached to any branch, i.e. it points directly to some commit.
To better understand situations with attached / detached HEAD, let’s show the steps leading to the quadruplet of pictures above.
We begin with the same state of the repository (pictures in all quadrants are the same):
Now we want to perform git checkout — with different targets in the individual pictures (commands on top of them are dimmed to emphasize that we are only going to apply those commands):
This is the situation after performing those commands:
As you can see, the HEAD points to the target of the git checkout command — to a branch (first 3 images of the quadruplet), or (directly) to a commit (the last image of the quadruplet).
The content of the working directory is changed, too, to be in accordance with the appropriate commit (snapshot), i.e. with the commit pointed (directly or indirectly) by the HEAD.
So now we are in the same situation as in the start of this answer:
You will have your uncommited changes and normal «attached» HEAD, like nothing happened.
Here’s what I just did after I realized I was on a detached head and had already made some changes.
I committed the changes.
I remembered the hash (1fe56ad) of the commit. Then I checked out the branch I should have been on.
Finally I applied the changes of the commit to the branch.
I think this is a bit easier than creating a temporary branch.
If you have made changes to a particular file and you simply want to discard them, you can use the checkout command like this:
This will discard any uncommitted changes and revert the file to whatever state it has in the head of your current branch. If you want to discard changes that you have already committed, you may want to use the reset command. For example, this will reset the repository to the state of the previous commit, discarding any subsequent changes:
Try to just git checkout your-branch
To further clarify @Philippe Gerber’s answer, here it is:
Detached head means you have not checked out your branch properly or you have just checked out a single commit.
If you encounter such an issue then first stash your local changes so that you won’t lose your changes.
After that. checkout your desired branch using the command:
Let’s say you want branch MyOriginalBranch:
Being in «detached head» means that HEAD refers to a specific unnamed commit (as opposite to a named branch) (cf: https://git-scm.com/docs/git-checkout section Detached head). In reality, this means that you have checked out a commit but there is no branch name associated with it.
You may choose to only create a new branch associated with your commit by
This allows you to save your current state in a new branch.
Or you may want to come back to the previous state and then to do this, you need to select the branch that was selected before by
Addendum
If the branch to which you wish to return was the last checkout that you had made, you can simply use checkout @ <-1>. This will take you back to your previous checkout.
Git told me how to do it.
Normally HEAD points to a branch. When it is not pointing to a branch instead when it points to a commit hash like 69e51 it means you have a detached HEAD. You need to point it two a branch to fix the issue. You can do two things to fix it.
HEAD must point to a branch, not a commit hash is the golden rule.
This approach will potentially discard part of the commit history, but it is easier in case the merge of the old master branch and the current status is tricky, or you simply do not mind losing part of the commit history.
To simply keep things as currently are, without merging, turning the current detached HEAD into the master branch:
Credit: adapted from this Medium article by Gary Lai.
When you’re in a detached head situation and created new files, first make sure that these new files are added to the index, for example with:
But if you’ve only changed or deleted existing files, you can add (-a) and commit with a message (-m) at the the same time via:
Then you can simply create a new branch with your current state with:
You’ll have a new branch and all your adjustments will be there in that new branch. You can then continue to push to the remote and/or checkout/pull/merge as you please.
(Assuming everything is committed (working tree is «clean»))
Save my commit messages: git log > /tmp/log
Revert to master : git checkout master
I wanted to keep my changes so, I just fix this doing.
worked for me. It was just about giving remote and branch name explicitly.
The detached HEAD means that you are currently not on any branch. If you want to KEEP your current changes and simply create a new branch, this is what you do:
Afterwards, you potentially want to merge this new branch with other branches. Always helpful is the git «a dog» command:
It wouldn’t let me push because I wasn’t considered to be on the branch I thought I was on.
The solution was just to do the following:
You need to do the checkout even though HEAD and myStuckBranch are now pointing at the same thing because you are still considered to be in the detached head state (not on a branch)
I’m not an expert with git (having mostly used mercurial which would never create this weird situation) but my understanding of this command is that it just says «change myStuckBranch to point at HEAD».
It’s a bit annoying to have to manually have to do that all the time but still better than having to change your working directory just to update another branch in order to merge in changes from it.