Несмотря на то, что GIT НЕ хранит файловые дельты, вы все равно можете выполнить откат к предыдущим версиям файлов (неограниченное количество раз?)

14

Я читал, что Git не хранит файловые дельты. Если это правда, как он поддерживает откат файла к предыдущим версиям? Если он хранит весь файл, пространство хранилища на диске должно увеличиться до неуправляемо большого размера. Поддерживает ли Git откат файлов и diff (s) до версии файла 1? Поддерживает ли оно даже концепцию управления версиями в отношении файлов? Это (я считаю) важно для моего понимания VCS / DVCS и моих потребностей. Мне нужно сравнить то, что я собираюсь проверить, с предыдущими версиями.

Пит Элвин
источник

Ответы:

44

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 выбросить коммиты «переписав историю» и с помощью таких команд, как git reset , но даже в этих случаях Git «зависает» на недавно отмененных коммитах на некоторое время, на тот случай, если вы решите, что они вам нужны. Смотрите git reflog и git prune .

Крис Джонсен
источник
3
+1 только за количество и детализацию информации, которую вы предоставили.
Тамара Вийсман
3
Кроме того, поскольку Git использует моментальные снимки файлов, а не дельт, возвращаться к долгому пути в истории на самом деле проще. Представьте, что вам нужно увидеть файл из 20 коммитов назад. С дельтами вам нужно отменить 20 наборов изменений; со снимками вы просто получаете правильный снимок. Чем дольше ваша история, тем больше преимущество. И если вы хотите увидеть разницу между текущей версией и той, это просто один раз, а не решать, что было сделано, отменено, переделано и т. Д.
Натан Лонг,
Крис, ты, кажется, хорошо разбираешься в Git. Есть ли шанс, что вы могли бы поиграть в это? stackoverflow.com/questions/5176225/…
Натан Лонг
@ChrisJohnsen Пожалуйста, помогите мне понять это. Исходя из того, что вы сказали, может ли Git получить аналогичную (или лучшую) эффективность хранения, чем Subversion? Я знаю, что если я фиксирую файл с небольшими изменениями несколько раз, данные объемом 1 ГБ могут быть сохранены в 100 МБ. Может ли Git сделать то же самое?
Алиреза Нури
@AlirezaNoori: Все зависит от характера данных и зафиксированных изменений (размер файла, сжимаемость файла, размер и расположение изменений и т. Д.). Нечто подобное должно быть возможно (в зависимости от специфики). В общем, файлы пакета Git могут опираться на больший выбор базисов для его дельта-сжатия по сравнению со строго обратными хронологическими дельтами, которые используют серверы SVN (используется? Я не слежу за развитием SVN…). Если у вас есть какой-то конкретный вопрос, вам следует подумать о том, чтобы задать новый вопрос, который включает в себя все соответствующие детали.
Крис Джонсен
1

Это можно прочитать на той же странице:

...

Следовательно, Git явно не записывает отношения редакции файла на любом уровне ниже дерева исходного кода.

...

Немного дороже изучить историю изменений одного файла, чем весь проект. Чтобы получить историю изменений, влияющих на данный файл, Git должен просмотреть глобальную историю и затем определить, изменило ли каждое изменение этот файл. Однако этот метод изучения истории позволяет Git с одинаковой эффективностью создавать единую историю, показывающую изменения в произвольном наборе файлов. Например, подкаталог исходного дерева и связанный глобальный заголовочный файл - очень распространенный случай.

...

Таким образом, вы можете вернуться к предыдущим версиям файла и сравнить два файла.

Тамара Вийсман
источник
1

Git на самом деле сохраняет дельты файлов, но сохраняет их как дельту всего дерева файлов.

Чтобы увидеть различия между версиями, выполните одно из следующих действий:

  1. git diff - показывает различия между последней зарегистрированной версией и файлами, которые были изменены, но не были git addзапущены на них.
  2. git diff --cached - показывает различия между предыдущей версией и тем, что все файлы были git addзапущены, но не были зафиксированы
  3. git diff commitid - показать различия между текущим рабочим каталогом и предыдущим коммитом, как указано в commitid
  4. git diff commita..commitb - показывает различия между двумя коммитами, a и b. Коммиты также могут быть символическими именами, такими как ветви или теги.
edgester
источник
Этот ответ не совсем правильный. Все эти команды могут быть применены к произвольному набору файлов, а также ко всему дереву - просто добавьте имена файлов в конце ...
naught101