Я читал, что Git не хранит файловые дельты. Если это правда, как он поддерживает откат файла к предыдущим версиям? Если он хранит весь файл, пространство хранилища на диске должно увеличиться до неуправляемо большого размера. Поддерживает ли Git откат файлов и diff(файлы) до версии файла 1? Поддерживает ли оно даже концепцию управления версиями в отношении файлов? Это (я считаю) важно для моего понимания VCS/DVCS и моих потребностей. Мне нужно сравнить то, что я собираюсь проверить, с предыдущими версиями.
3 ответа
Git не выбрасывает информацию самостоятельно *. Все предыдущие версии каждого файла всегда доступны для возврата, различий, проверок и так далее.
Целое дерево против отдельных файлов
Возможно, вы пытаетесь согласовать идею доступа к старой версии отдельного файла по сравнению с тем фактом, что модель истории Git сфокусирована на всем дереве. Управление версиями всего дерева требует немного больше работы, чтобы увидеть (например) версию foo.c
как она существовала десять foo.c
изменилось назад по сравнению с десятью изменениями всего дерева назад:
# 10 foo.c-changes ago
git show $(git rev-list -n 10 --reverse HEAD -- foo.c | head -1):foo.c
# 10 whole-tree-changes ago
git show HEAD~10:foo.c
Преимущества древовидной ориентации, главным образом, возможность просматривать коммиты как единицу взаимозависимых изменений, внесенных в различные части всего дерева, в целом значительно перевешивают дополнительную типизацию (которая может быть уменьшена с помощью псевдонимов, сценариев и т.д.) И время процессора копаться в прошлых коммитах.
Эффективность хранения
Когда в систему попадает новый объект (например, файл с ранее невидимым содержимым), он сохраняется с простым (zlib) сжатием как «свободный объект». Когда накапливается достаточное количество незакрепленных объектов (в зависимости от параметра конфигурации gc.auto
; или когда пользователь запускает git gc или одну из команд упаковки более низкого уровня), Git собирает много разрыхленных объектов в один «файл пакета».
Объекты в файле пакета могут храниться либо в виде простых сжатых данных (так же, как незакрепленный объект, просто связанный с другими объектами), либо в виде сжатых дельт относительно другого объекта. Дельты могут быть объединены в цепочки до настраиваемой глубины (pack.depth
) и могут быть созданы для любого подходящего объекта (pack.window
управляет тем, насколько широко Git ищет лучшую дельта-базу; версия исторически несвязанного файла может использоваться в качестве базы если это сделать, то получится хорошее дельта-сжатие). Широта, которую конфигурации глубины и размера окна дают механизму дельта-сжатия, часто приводят к лучшему дельта-сжатию, чем простое сжатие «diff» в стиле CVS «одна версия против следующей / предыдущей версии».
Именно это агрессивное дельта-сжатие (в сочетании с обычным сжатием zlib) часто позволяет Git-хранилищу (с полной историей и несжатым рабочим деревом) занимать меньше места, чем одна проверка SVN (с несжатым рабочим деревом и нетронутой копией).
См., Как Git хранит объекты и разделы Packfile в Git Community Book. А также справочная страница git pack-objects.
* Вы можете сказать Git выбросить коммиты «переписав историю» и используя такие команды, как git reset, но даже в этих случаях Git «зависает» на недавно отмененных коммитах на некоторое время, на тот случай, если вы решите, что они вам нужны. Смотрите git reflog и git prune.
Git на самом деле сохраняет дельты файлов, но сохраняет их как дельту всего дерева файлов.
Чтобы увидеть различия между версиями, выполните одно из следующих действий:
- git diff - показывает различия между последней зарегистрированной версией и файлами, которые были изменены, но на них не запускался
git add
. - git diff --cached - показывает различия между предыдущей версией и тем, что запускали все файлы, которые имели
git add
, но не были зафиксированы - git diff commitid - показать различия между текущим рабочим каталогом и предыдущим коммитом, как указано в commitid
- git diff commita..commitb - показывает различия между двумя коммитами, a и b. Коммиты также могут быть символическими именами, такими как ветви или теги.
Это можно прочитать на той же странице:
...
Следовательно, Git явно не записывает отношения редакции файла на любом уровне ниже дерева исходного кода.
...
Изучить историю изменений одного файла немного дороже, чем весь проект. Чтобы получить историю изменений, затрагивающих данный файл, Git должен просмотреть глобальную историю и затем определить, изменило ли каждое изменение этот файл. Однако этот метод изучения истории позволяет Git с одинаковой эффективностью создавать единую историю, показывающую изменения в произвольном наборе файлов. Например, подкаталог исходного дерева и связанный глобальный заголовочный файл - очень распространенный случай.
...
Таким образом, вы можете вернуться к предыдущим версиям файла и сравнить два файла.