Когда именно git удаляет объекты: почему «git gc» не удаляет коммиты?

Я работаю над git курсом и хотел бы упомянуть, что потерянные реферы действительно не теряются до запускаgit gc, Но проверив это, я обнаружил, что это не так. Даже после бегаgit gc --prune=all --aggressive потерянные рефери все еще там.

Я явно что-то не так понял. И прежде чем сказать что-то неправильное в курсе, я хочу разъяснить свои факты! Вот пример сценария, иллюстрирующий эффект:

 #!/bin/bash

 git init

 # add 10 dummy commits
 for i in {1..10}; do
     date > foo.txt
     git add foo.txt
     git commit -m "bump" foo.txt
     sleep 1
 done;

 CURRENT=$(git rev-parse HEAD)
 echo HEAD before reset: ${CURRENT}

 # rewind
 git reset --hard HEAD~5

 # add another 10 commits
 for i in {1..10}; do
     date > foo.txt
     git add foo.txt
     git commit -m "bump" foo.txt
     sleep 1
 done;

Этот скрипт добавит 10 фиктивных коммитов, сбросит до 5 коммитов в прошлом и добавит еще 10 коммитов. Непосредственно перед сбросом он напечатает хеш текущего заголовка.

я мог быожидать потерять объект вCURRENT после запускаgit gc --prune=all, Тем не менее, я все еще могу бежатьgit show на этот хэш.

Я понимаю, что после запускаgit reset и добавив новые коммиты, я по сути создал новую ветку. Но моя оригинальная ветка больше не имеет ссылки, поэтому она не отображается вgit log --all, Полагаю, он также не будет перенесен на любой пульт.

Мое пониманиеgit gc было то, что удаляет эти объекты. Это не похоже на случай.

Зачем? А такжекогда точнееgit gc удалить объекты?

 torek09 июн. 2016 г., 21:10
Тебе нужно--expire=all --allили запустить его на обоихHEAD (по умолчанию) иmaster, Или вы можете вручную удалить определенные записи (или см. Ответ ниже).
 twalberg09 июн. 2016 г., 20:13
Ваш reflog по-прежнему содержит ссылки на коммиты, которые вы «удалили». Пока эти тайм-ауты или вы явно не истечете, они не будут сокращены.
 exhuma09 июн. 2016 г., 21:06
Интересно. Я посмотрел наgit-scm.com/docs/git-reflog и побежалgit reflog --expire=all, После чего объект былеще там. Далее я побежал другойgc и это было все еще там. Даже другойgit gc --aggressive --prune=all не помогло.

Ответы на вопрос(1)

Решение Вопроса

два критерии. Один из них связан с датой / временем: он должен быть создан1 достаточно давно, чтобы созреть для сбора. Часть "достаточно давно" - это то, с чем вы устанавливаете--prune=all: вы переопределяете нормальную настройку "минимум две недели".

Вторым критерием является то, где ваш эксперимент идет не так. Чтобы быть обрезанным, объект должентакже бытьнедостижимый, Какtwalberg отметил в комментариина каждый из ваших якобы заброшенных коммитов (и, следовательно, их соответствующих деревьев и BLOB-объектов) фактически ссылаются через записи Git «reflog».

Для каждого такого коммита есть две записи reflog: одна дляHEADи один для имени ветви, к которойHEAD ссылается на момент совершения (в этом случаеrefs/heads/masterт. е. филиалmaster). Каждая запись reflog имеет свою собственную отметку времени, иgit gc также истекает срок действия записей reflog для вас, хотя с более сложным набором правил, чем простое «14 дней» по умолчанию для срока действия объекта.2

Следовательно,git gc мог сначала удалите все записи reflog, которые хранят старый объект,затем обрезать объект. Это просто не происходит здесь.

Чтобы просмотреть или даже удалить записи журнала вручную, используйтеgit reflog, Обратите внимание, чтоgit reflog дисплеи записи, запустивgit log с-g / --walk-reflogs опция (плюс некоторые дополнительные опции форматирования дисплея). Вы можете запуститьgit reflog --all --expire=all чтобы очистить все, хотя это дубинка, когда скальпель может быть более подходящим. использование--expire-unreachable для немного большей избирательности. Подробнее об этом смотритеgit log документация и конечноgit reflog документация.

1Некоторые файловые системы Unix-y вообще не хранят время создания файла («рождение»):st_ctime полеstat структура являетсявремя смены инода, а не время создания. Если есть время создания, оно находится вst_birthtime или жеst_birthtimespec.3 Однако каждый объект Git доступен только для чтения, поэтому время создания файла также является временем его изменения. следовательноst_mtime, которыйявляется всегда доступен, дает время создания объекта.

2Точные правила описаны вgit gc документация, но я думаюПо умолчанию 30 дней для недоступных коммитов и 90 дней для достижимых коммитов это достойное резюме. Определениедостижимый тут что-то необычное: значитдостижимый из текущего значения ссылки, для которой этот reflog содержит старые значения. То есть, если мы смотрим на рефлог дляmasterмы находим коммит, которыйmaster идентифицирует (например,1234567), а затем посмотреть, если каждая запись reflog дляmaster (например.,[email protected]{27}) достижимоиз этого конкретного коммита (1234567 снова).

3Эта конкретная путаница с именами преподносится вам специалистами по стандартизации POSIX. :-)st_birthtimespec поле являетсяstruct timespec, который записывает как секунды, так и наносекунды.

 torek19 июн. 2017 г., 22:13
@ RoryO'Kane: верно; Я оставил это ссылкам на документацию, но, возможно, я должен упомянуть об этом прямо в ответе?
 Kamafeather24 июл. 2018 г., 10:47
Знаете ли вы, еслиprune удалит также отдельные коммиты, на которые ссылаются в сообщениях коммитов? Или эта ссылка (если она интерпретируется как единая) делает ихдостижимый?
 torek24 июл. 2018 г., 18:24
@Kamafeather: текст сообщения о фиксации не сканируется на наличие хеш-идентификаторов. Если идентификатор хэша появляется в чем-то, чтоявляется при сканировании объект Git будет сохранен, но если идентификатор хэша появится только в сообщении фиксации, объект будет иметь право на сборку мусора.
 Rory O'Kane19 июн. 2017 г., 21:59
Обратите внимание, что записи reflog в конечном итоге тоже собираются сборщиком мусора. Какgit gc документация говорит, что необязательная переменная конфигурацииgc.reflogExpire по умолчанию 90 дней, иgc.reflogExpireUnreachable по умолчанию 30 дней. Доступные и недоступные записи в журнале будут удалены, если они старше, чем эти переменные, когдаgit gc это запустить.
 Rory O'Kane19 июн. 2017 г., 22:34
Да, я думаю, что было бы полезно обратиться к названию вопроса более прямо, сказав, чтоgit gc иногда удаляет коммиты. Это также позволило бы избежатьgit reflog это единственная команда, которая удаляет записи reflog. Однако записать это в свой ответ нетот важно, учитывая, что читатели могут получить ту же информацию из этих комментариев.

Ваш ответ на вопрос