Хорошей новостью является то, что ваши коммиты все еще доступны. Вам нужно будет использовать от git reflog
до find их.
Первое, что нужно знать, это то, что Git не о файлах и не о филиалов либо. Речь идет о коммитах . Каждый коммит содержит файлы, а набор коммитов образует ветвь или набор ветвей, но центральным элементом в Git является сам коммит.
Every Коммит имеет уникальный номер. Это число принимает форму большого уродливого случайного вида га sh ID , например 51ebf55b9309824346a6589c9f3b130c6f371b8f
. Каждый коммит, который вы делаете, сохраняет полный и полный снимок всех ваших файлов - ну, в любом случае, все ваши отслеживаемые *1022* файлы - в режиме «только для чтения», сжатый, «замороженный на все время», Git только формат, который может использовать только Git. Поэтому, когда вы делаете коммит, Git создает новый случайный идентификатор ha sh: новый номер, отличный от любого другого коммита. Вот почему число должно быть таким большим.
Если бы у нас не было компьютера, который бы запомнил их для нас, нам пришлось бы записать каждый из этих случайных идентификаторов ha sh. Но у нас есть компьютер, поэтому у нас он запоминает идентификаторы для нас.
Каждый коммит, который мы делаем, запоминает идентификатор ha sh некоторого более раннего коммита. То есть мы выбираем коммит, например, по некоторому идентификатору ha sh, с git checkout
. Это то, что вы сделали, когда попали в состояние HEAD . Этот коммит - тот, который мы извлекли - это текущий коммит. Затем, когда вы делаете новый коммит, Git делает наш новый коммит и добавляет в него вместе со снимком некоторые метаданные: ваше имя и адрес электронной почты, дату и время, когда вы сделал коммит, ваше лог-сообщение и - что очень важно для Git самого себя - идентификатор ha sh коммита, который вы проверили, что было текущим коммитом.
Этот процесс создания нового коммита приводит к новому уникальному идентификатору ha sh, и теперь этот коммит становится текущим коммитом. То есть Git записывает новый коммит ha sh ID в HEAD
. Так как текущий коммит содержит идентификатор предыдущего коммита, Git может найти путь назад к предыдущему коммиту:
... <--previous <--current <--HEAD
Отсоединенный HEAD не является нормальный способ работы, хотя. Обычно мы git checkout <em>branch-name</em>
, например, git checkout master
. В этом случае имя HEAD
является , прикрепленным к имени ветви. Если мы используем единственные заглавные буквы для обозначения фактических идентификаторов ha sh, а текущая end ветви master
- commit H
, мы можем нарисовать это так:
... <-F <-G <-H <--master(HEAD)
То есть Git сохранил идентификатор ha sh last commit в нашей ветке с именем master
под нашим именем master
. Затем прикрепил специальное имя HEAD
к имени master
. Из HEAD
, Git можно найти имя master
, а оттуда Git может найти га sh идентификатор коммита H
.
Фиксация H
, между тем, содержит идентификатор ha sh предыдущего коммита G
, поэтому от H
Git может работать в обратном направлении до G
. Конечно, G
содержит идентификатор ha sh предыдущего коммита F
, поэтому Git может работать и там, обратно F
... и F
содержит идентификатор ha sh другого более ранний коммит и т. д.
Если вы git checkout master
выполнили некоторую работу, а затем создали новый коммит, Git создаст этот новый коммит с каким-то случайным образом выглядящим ха sh ID, но мы просто назовем его I
. Commit I
будет указывать назад на commit H
:
...--F--G--H <-- ???
\
I <-- ???
Имя master
указывало на H
мгновение go, но потому что HEAD
присоединено к имени master
, Git теперь записывает коммит I
ha sh ID в имя master
, в результате чего master
указывает на I
:
...--F--G--H
\
I <-- master (HEAD)
Как мы найдем коммит H
? Ну, имя master
имеет I
ha sh ID, записанное в нем, так что мы можем легко найти I
. И в commit I
записан идентификатор H
ha sh, записанный внутри него - так мы и найдем H
, начав с HEAD
, чтобы получить master
, чтобы получить I
, чтобы найти H
.
Нам больше не нужен излом на чертеже, поскольку нам не нужен указатель, прямо указывающий на H
. Но на самом деле у нас есть несколько. Один находится в reflog для HEAD
, а другой - в reflog для master
. Git хранит историю каждого коммита, который master
когда-либо называл, и каждого коммита, который HEAD
когда-либо называл, в том числе через имена веток, такие как master
, поэтому на данный момент мы имеем:
...--F--G--H <-- master@{1}, HEAD@{1}
\
I <-- master (HEAD)
Если имя master
ранее указывало на G
, у вас также будет master@{2}
.
Когда вы отсоедините HEAD
- например, git checkout <hash-of-F>
- вы получаете HEAD
, указывающий непосредственно на коммит, потому что вместо имени ветви, HEAD
теперь просто содержит необработанный коммит ha sh ID. Итак, у вас есть это - я нарисую HEAD@{1}
, но не все остальные:
...--F <-- HEAD
\
G--H--I <-- master, HEAD@{1}
Если вы сейчас создадите новый коммит, он получит новый уникальный идентификатор ha sh, который мы ' Позвоните J
:
J <-- HEAD
/
...--F <-- HEAD@{1}
\
G--H--I <-- master, HEAD@{2}
Обратите внимание, что было *1153* HEAD@{1}
сейчас HEAD@{2}
; что было HEAD
сейчас HEAD@{1}
; и HEAD
обозначает новый коммит J
.
Каждый раз, когда вы перемещаете HEAD
или присоединяете его к другой ветви, все числа увеличиваются на единицу, чтобы освободить место для предыдущее значение. Git сохраняет предыдущий га sh ID из HEAD
в reflog для HEAD
и записывает соответствующее имя ветви или ha sh ID в имя HEAD
.
Итак: запустите git reflog HEAD
. Обратите внимание, что он выдает как сокращенные идентификаторы ha sh, так и HEAD@{<em>number</em>}
. Идентификаторы ha sh являются истинными именами коммитов. коммит - вот что такое Git; это то, что он хранит. Имена с @{1}
, @{2}
, et c., Все часто перенумеровываются. Обратите внимание, что Git хранит эти старые значения только некоторое время - по умолчанию от 30 до 90 дней. 1 После этого Git показывает, что вам, вероятно, уже все равно, и начинает стирать старый reflog records.
Как только вы увидите, какие записи reflog или записи являются коммитами, которые вы хотите вернуть, дайте им какое-то имя. Например, вы можете создать новое имя ветви для ссылки на последний коммит в цепочке. Если вы сделали это:
J--K--L <-- HEAD
/
...--F
\
G--H--I <-- master
, сделав три коммита с отсоединенной головой, а затем:
J--K--L <-- HEAD@{1}
/
...--F
\
G--H--I <-- master (HEAD)
, заново прикрепив HEAD
к имени master
, Вы можете сделать это:
git branch lazarus HEAD@{1}
и тогда у вас будет:
J--K--L <-- lazarus, HEAD@{1}
/
...--F
\
G--H--I <-- master (HEAD)
Новое имя lazarus
теперь указывает на воскрешенный коммит. (Возможно, мне следовало сделать четыре из них?)
Подробнее на GIT восстановить последнюю отсоединенную ГОЛОВУ .
1 30-дневный лимит предназначен для недоступных коммитов, а 90-дневный лимит - для достижимых коммитов. Термин достижимый здесь взят из теории графов, а достижимость подразумевает отправную точку. Отправной точкой в этом случае является текущий коммит га sh ID в ссылке. Если сохраненный идентификатор reflog ha sh доступен из значения ref-name, запись имеет более длительный срок действия.
После того, как запись reflog исчезла, у самого коммита есть еще меньше способов достичь его. , Коммиты должны быть доступны из некоторого имени - какого-то имени ветви, или имени тега, или любого другого типа имени - или иным образом Git сборщик мусора , git gc
, будет в конце концов удалите их. Таким образом, эти записи reflog служат для поддержки самих коммитов. В то время как все коммиты замораживаются на все время, сами коммиты будут пожинаться (отбрасываться), если они недоступны, поэтому после того, как запись reflog исчезнет, коммит должен быть доступен из какого-то другого достижимого коммита или назван напрямую каким-либо другим именем.