git - как создать недоступный коммит для git prune? - PullRequest
0 голосов
/ 01 октября 2018

Я пишу учебные материалы для git, и мне нужно продемонстрировать git prune удаление «отсоединенного объекта».Я думал, что мог бы поместить коммит в отдельное состояние, используя git reset, чтобы отсоединить его от истории веток.

Это вызовет git checkout, чтобы увидеть фиксацию как отсоединенную, однако git prune не будет заботиться об этом.

Моя текущая симуляция отсоединенной фиксации настроена следующим образом:

~ $ mkdir git-prune-demo
~ $ cd git-prune-demo/
~/git-prune-demo $ git init .
Initialized empty Git repository in /Users/kev/Dropbox/git-prune-demo/.git/
~/git-prune-demo $ echo "hello git prune" > hello.txt
~/git-prune-demo $ git add hello.txt
~/git-prune-demo $ git commit -am "added hello.txt"
[master (root-commit) 994b122] added hello.txt
 1 file changed, 1 insertion(+)
 create mode 100644 hello.txt
~/git-prune-demo $ echo "this is second line txt" >> hello.txt
~/git-prune-demo $ git commit -am "added another line to hello.txt"
[master 5178bec] added another line to hello.txt
 1 file changed, 1 insertion(+)
~/git-prune-demo $ git reset --hard 994b122045cf4bf0b97139231b4dd52ea2643c7e
HEAD is now at 994b122 added hello.txt
~/git-prune-demo $ git prune -n
~/git-prune-demo $ nothing

Да, я понимаю, git prune обычно не используется как отдельная команда и по сути является дочерним по отношению к git gc.

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

TL; DR

Сначала вам нужно будет запустить git reflog expire --expire-unreachable=now, затем git prune --expire now.Даже тогда все может пойти не так, хотя для этого простого примера этого вполне достаточно.

Long

Я пишу учебные материалы для git и мне нужно продемонстрировать git pruneудаление отдельного коммита.

Это не то, что git prune делает .То, что он может, может произвести этот эффект , но только при определенных условиях.Важно отметить, что detached commit не является четко определенной фразой в Git: в Git есть определение для detached HEAD - мы вернемся к этому через минуту - но сами коммиты либо достижимо или недоступно .Я думаю, вы хотите поговорить о недостижимых коммитах, здесь.

Важно, что git prune имеет дело с объектами , которые являются более общими, чем коммиты.Git имеет четыре типа объектов: коммиты, деревья, капли и аннотированные теги.Git's git prune может удалить любой недоступный объект, при условии соблюдения нескольких других условий.Однако прежде чем мы доберемся до этого, давайте рассмотрим еще несколько элементов.

Исправление неправильных представлений

Я думал, что могу перевести коммит в отключенное состояние, используя git reset, чтобы отсоединить его.из истории веток.

Коммит или любой другой объект Git - по определению достижим, если есть какое-то внешнее имя, которое либо непосредственно именует коммит (или объект), либо эти именанекоторый другой объект, с помощью которого мы можем достичь данного коммита.(Подробнее об этом см. Думайте как (а) Git .) Используя git reset, мы можем сделать коммиты, которые были доступны только через текущее имя ветви, стали недоступными.Например, если фиксация a123456... достижима только через текущее имя ветви, т. Е. Не через какое-либо другое имя ветви, ни через какое-либо имя тега или другую ссылку на имя, не являющееся ветвью, - тогда используйте git reset для настройки текущей ветви.так что исключая a123456... делает этот коммит недоступным.

Это заставит git checkout увидеть коммит как отдельный ...

Я думаю, что здесь вы 'мы говорим о том, что Git называет отсоединенный HEAD.

Отсоединенный HEAD просто означает, что специальная ссылка GIT на Git, хранящаяся в файле с именем .git/HEAD, содержит необработанный хэш-идентификатор коммита.Противоположное условие - которое мы можем назвать присоединенной HEAD , поскольку это очевидный антоним для detached - возникает, когда .git/HEAD содержит имя ветви.В обоих случаях HEAD относится к текущей фиксации;когда HEAD содержит имя ветви, HEAD также относится к текущему имени ветви.Внутренний подход Git к этому заключается в том, что он имеет различные функции и программы для разрешения HEAD либо символически:

$ git symbolic-ref HEAD
refs/heads/master

, либо хэш-идентификатора:

$ git rev-parse HEAD
c05048d43925ab8edcb36663752c2b4541911231

(ДляВ случае отсоединенного HEAD git symbolic-ref выдает ошибку, поскольку имя ветви отсутствует.)

Команда git checkout присоединяет HEAD (к некоторому указанному имени ветви), когда:

  • вы даете ему имя, которое является именем ветви, или
  • вы используете его для создания, а затем присоединяете к новому имени ветви.

Он отсоединяет HEAD, когда:

  • вы даете ему что-то, что разрешает хеш-идентификатор, но не имя ветки (например, необработанный хеш-идентификатор или имя для удаленного отслеживания, например origin/master), или
  • вы используете флаг --detach, чтобы принудительно отключить HEAD, даже если бы он обычно подключал HEAD.

Detached HРежим EAD не означает, что вы работаете с недоступным коммитом.Фактически, отсоединение HEAD от коммита, недоступного в противном случае, делает этот коммит неожиданно достижимым , потому что теперь это коммит HEAD.Другими словами, отсоединение HEAD от любого коммита добавляет еще один способ достижения коммита, но что касается сокращения, интересным вопросом является не , сколько имен достигают рассматриваемого объекта, а просто числоненулевойОдно имя, два имени, десять имен или миллионы имен: все они одинаковы для git prune.Когда я говорю здесь names , я имею в виду больше, чем просто ссылочные имена плюс возможные отдельные HEAD, но мы начнем с этих имен, прежде чем добавим следующую усложнение.

Объектная модель Git и ссылки на объекты

Think Like (a) В Git есть хорошее описание того, как ссылки делают коммиты достижимыми.Тем не менее, здесь не упоминается, что в общем случае ссылки могут указывать хеш-идентификатор любого объекта, а не только коммитов.Это связано с тем, что он связан с ветвями, а не только с любым старым объектом, и имена ветвей (refs/heads/*) и имена удаленного отслеживания (refs/remotes/*) ограничены указанием только на коммиты.В нем также не рассматриваются детали того, что находится внутри коммита, т.е. как Git хранит файлы и имена файлов.Вот где появляются объекты дерева и блоба.

Каждый коммит содержит хэш-идентификатор одного объекта дерева.Древовидный объект содержит серию трехзначных элементов: mode, name и hash-ID.Режим указывает, предназначена ли эта запись дерева для файла, для поддерева или для одного из более экзотических элементов (символических ссылок и ссылок).Имя дает имя представляемой сущности, например README.txt или subdir или file.ext.Идентификатор хеша, как правило, является идентификатором объекта BLOB-объекта или другого объекта дерева: если запись предназначена для файла, подобного README.txt, это хэш BLOB-объекта, а если для поддерева, такого как subdir, это хешИдентификатор поддерева.

Если мы вытянем все это для одного коммита, начиная с названия ветви справа от самой верхней строки, мы получим что-то вроде этого:

... <-  commit a1234...   <-- branchname
               |
               v
        tree 07f39...: (100644, README.txt, 531c2...); (040000, subdir, ...)
                                               |                         |
                                               v                         |
                                blob 531c2...: data for README.txt       |
                                                                         |
                                                                         v
                                                               tree ...: ...

Аннотированным теговым объектам разрешено указывать на любой другой объект (включая другие аннотированные теговые объекты), хотя чаще всего они просто указывают на фиксацию объектов.Таким образом, добавляя аннотированные теги к этому изображению, мы, как правило, просто видим ссылку на тег, например refs/tags/v1.0, указывающую на аннотированный объект тега с некоторым идентификатором хэша, где затем аннотированный объект тега указывает, например, на коммит a1234...,Это дало бы этому коммиту еще одну ссылку.Если мы не создали никаких тегов, нам не нужно об этом беспокоиться, но они важны для полной картины.

Как и в случае коммитов, на любой объект ссылаются, если есть какой-то путь, ведущий от какого-то внешнегоname - или для больших двоичных объектов - внутренние ссылки, хранящиеся в Git index , - которые ведут к этим объектам.Индекс может ссылаться только на большие двоичные объекты, поэтому, когда нас интересуют только коммиты, мы можем игнорировать ссылки индекса, но, как и теги, они важны для полной картины.

В любом случае, на приведенной выше диаграммемы видим, что имя branchname делает коммит a1234... достижимым.Commit a1234... делает дерево 07f39... достижимым, что делает доступным BLOB-объект и другое поддерево и т. Д.Поскольку все они доступны, git prune определенно не будет сокращать их.

Важно, что каждое ссылочное имя, а также специальное HEAD имя, имеют необязательный reflog , который хранит, для этой ссылки, предыдущие значения ссылки.Эти сохраненные значения остаются действительными в течение некоторого времени, пока не истечет срок их действия.Команда, которую Git использует для истечения срока действия устаревших сохраненных значений, - git reflog expire, с использованием двух разных параметров командной строки, --expire=<em>when</em> и --expire-unreachable=<em>when</em>.

Если вы хотите показать git prune удаление объекта, вам нужно убедиться, что объект полностью не имеет ссылки.Это означает, что вам нужно будет удалить все записи reflog, которые запоминают, напрямую (фиксирует) или косвенно (деревья и большие двоичные объекты) его хэш-идентификатор.Простой, хотя и довольно разрушительный, метод для этого заключается в использовании:

git reflog expire --expire-unreachable=now --all

(Мы можем добавить --expire=now, но мы можем предположить, что текущее значение ссылки не достигает значения reflog, поэтомупараметр --expire-unreachable будет применяться.)

Это устанавливает условие обязательно ;теперь пришло время вернуться к самому git prune.

После всего этого давайте вернемся к git prune

Команда git prune работает со всеми четырьмя типамиобъектов.Его работа заключается в удалении несвязанных объектов.Исходя из вышеизложенного, мы знаем, что мы должны убедиться, что фиксация не имеет ссылок, путем истечения срока действия любых записей журнала регистрации, которые могли бы помнить его, после использования такой команды, как git branch -f или git branch -D или git reset, чтобы убедиться, что нетИмена ветвей помнят это либо.

Но теперь нам нужно знать еще две вещи об объектах Git:

  • они могут быть либо Свободные или упакованы и
  • имеют возраст, скорее как записи reflog.

Объект, который является свободным , хранится в отдельном файле в файлесистема.Это позволяет Git легко манипулировать им, но означает, что он минимально сжат.Git по команде (или автоматически через git gc) упаковывает много отдельных объектов вместе в один упаковочный файл .На этом этапе один файл в файловой системе содержит много объектов: десятки, возможно, или миллионы, или что-то среднее.

Команда prune не никогда удалит упакованный объект,потому что это слишком сложно.Упакованный объект может быть частью цепочки дельта-сжатия в файле пакета.Так что вместо этого git prune будет смотреть только на потерянные объекты.Отдельная программа - git repack - повторно выполнит упаковку объектов и может превратить упакованные объекты, на которые нет ссылок, обратно в незакрепленные объекты (или полностью их отбросить).

Как правило, объекты не упаковываются немедленно, так что недавно созданный объект, скорее всего, будет свободным.Если объект был упакован, и теперь на него нет ссылок, вам нужно будет запустить git repack.

Между тем, в качестве защиты от конкурирующих процессов Git, git prune также проверяет отметку времени на незакрепленных предметах.Эта метка времени должна быть достаточно старой, чтобы git prune мог удалить объект.Причина этого в том, что когда Git создает новые объекты, включая новые коммиты, он записывает эти объекты в базу данных хранилища по одному (или только нескольким) за раз.Git должен написать самые глубокие поддеревья с их хэшами больших двоичных объектов, а затем написать деревья следующего уровня, используя поддеревья и их хеши, вместе с любыми хэшами больших двоичных объектов, которые находятся в этих деревьях.Как только Git записал все деревья и получил хеш дерева верхнего уровня, чтобы войти в новый коммит, только тогда Git может написать объект коммита.До этого момента все эти деревья не имеют ссылок.Даже после записи коммита , это , также не имеет ссылки, пока текущее имя ветви (или отдельное HEAD) не будет обновлено, чтобы указывать на вновь созданный коммит.

Этот процесс занимаетвремя.Git дает себе 14 дней по умолчанию, чтобы завершить процесс.Если для git commit требуется более 14 дней, git prune может удалить некоторые из его объектов, но 14 дней должно быть достаточно.

Если вы знаете, что не выполняете какие-либо другие команды Gitвы можете вручную переопределить значение по умолчанию:

git prune --expire now

означает, что любые несвязанные, незакрепленные объекты должны быть удалены независимо от того, насколько они новые.Поэтому все, что вам нужно сделать, это убедиться, что ваш коммит не имеет ссылок, а затем удалить его с истечением «сейчас».

0 голосов
/ 01 октября 2018

Чернослив обычно не удаляет объекты, которые были доступны из любой ссылки в течение последнего ... Я забыл, месяц?- это проверяет местные reflogs для этого.Пройдите --expire now, чтобы отключить защиту при удалении объектов, но если вы пишете документы, предположите, что это действительно очень плохая привычка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...