Почему HEAD в моем рабочем дереве указывает на верхний коммит в master? - PullRequest
0 голосов
/ 01 мая 2019

Я создал рабочее дерево, используя git worktree add, и он работал, как и ожидалось, в течение нескольких дней. Сегодня я хотел проверить какой-то код в предыдущей ревизии, поэтому я временно отключил HEAD:

git checkout head^

После того, как я нашел то, что мне было нужно, я попытался вернуться к концу моей ветки рабочего дерева:

git checkout <branchname>

Но теперь HEAD указывает на верхний коммит в моей локальной ветке master. Например:

git show head

показывает самый последний коммит в master.

Есть идеи, почему это произошло или как это исправить?


EDIT

Я также вижу странное, непоследовательное поведение. Например, это:

> git reset --hard
HEAD is now at 4f1666467017
> git rev-parse head
e4fab17872cce2f6f37f5b596c7ed2739b135f3f

1 Ответ

0 голосов
/ 01 мая 2019

(Примечание: некоторые из них являются общими.)

Дополнительная странность здесь, вероятно, связана с тем, что у каждого рабочего дерева есть свой собственный HEAD файл. Различные операции могут находить разные файлы HEAD путем сворачивания регистра: есть файл .git/worktrees/<em>worktree</em>/HEAD для добавленного рабочего дерева и файл .git/HEAD для основного рабочего дерева. Если операция A выбирает .git/HEAD для совпадения head, а операция B выбирает .git/worktrees/<em>worktree</em>/HEAD, вы увидите именно это поведение. Использование прописных букв HEAD должно заставить Git делать правильные вещи.

(В частности, git reset --hard внутренне использует HEAD во всех столицах, что будет относиться к дереву для каждой работы HEAD, в то время как git rev-parse head использует строчные буквы head, которые относятся к .git/HEAD файл.)


Вы можете проверить любой исторический коммит по его необработанному хеш-идентификатору:

git checkout a123456

Результатом является то, что Git называет обособленной HEAD . Обычно специальное имя HEAD (заглавными буквами) , прикрепленное , к названию филиала:

...--A--B--C--D   <-- master (HEAD)
         \
          E--F--G   <-- branch

(заглавные буквы здесь обозначают фактические хеш-идентификаторы коммитов). При таком присоединении HEAD означает master, что означает фиксацию D. Таким образом, ваш текущий коммит теперь соответствует реальному хэш-идентификатору D. Если вы запустите:

git checkout branch

вы получите:

...--A--B--C--D   <-- master
         \
          E--F--G   <-- branch (HEAD)

, что означает, что ваша текущая ветвь равна branch, а ваш текущий коммит теперь равен G.

Когда вы выбираете коммит по хеш-идентификатору, хотя, скажем, коммит E, вы HEAD указываете непосредственно на этот коммит:

...--A--B--C--D   <-- master
         \
          E   <-- HEAD
           \
            F--G   <-- branch

Это то, что Git называет «отделенной головой»; ваш текущий коммит это коммит E.

Теперь вы не использовали необработанный хэш-идентификатор. Вместо этого вы набрали head^. Но есть много способов назвать коммиты. Необработанный хеш-идентификатор является способом самого низкого уровня: хеш-идентификатор всегда работает и всегда означает , что один конкретный коммит , но вы также можете сделать так, чтобы Git нашел parent некоторого коммита. Вот что означает HEAD^: выберите родителя текущего коммита. Предположим, текущий коммит G, потому что вы на branch; затем HEAD^ имена совершают F, а после:

git checkout HEAD^

у вас будет:

...--A--B--C--D   <-- master
         \
          E--F   <-- HEAD
              \
               G   <-- branch

Строчная версия head работает в Windows и MacOS, но не в Linux. Это побочный эффект того факта, что Git иногда сохраняет эти вещи в файлах (например, .git/HEAD и .git/refs/heads/master). Используя head вместо HEAD, Git может попытаться открыть .git/refs/heads/head, который не существует, но когда Git пытается открыть .git/head, он получает .git/HEAD, который действительно существует , Так что на этих машинах , git checkout head^ означает то же самое, что и git checkout HEAD^: найти текущий коммит, вернуться к его первому родителю и проверить , что commit.

Чтобы заново прикрепить HEAD к какой-либо ветке, используйте git checkout <em>branch-name</em>.

Если вы не используете Windows или MacOS (или даже если это так), обратите внимание, что вы можете также создать ветвь или тег с именем head. Если вы это сделаете, все станет немного скрупулезно, особенно в Windows и MacOS, поскольку теперь есть два способа разрешения head: в виде ветви или тега, и как .git/HEAD. Лучше не делать этого.

...