Git не дублирует файлы?
Если мой репозиторий содержит несколько копий одних и тех же файлов с небольшими изменениями (не спрашивайте почему), позволит ли git сэкономить место, сохранив только различия между файлами?
3 ответа
Может, но очень сложно сказать, будет ли это. Есть ситуации, когда гарантировано, что этого не произойдет.
Чтобы понять этот ответ (и его ограничения), мы должны посмотреть, как git хранит объекты. Там есть хорошее описание формата "git objects" (как хранится в .git/objects/
) в этом ответе stackru или в книге Pro Git.
При хранении "незакрепленных объектов", как это - что делает git для того, что мы могли бы назвать "активными" объектами, - они дефлированы по zlib, как говорится в книге Pro Git, но не сжимаются иначе. Таким образом, два разных (не битовых идентичных) файла, хранящиеся в двух разных объектах, никогда не сжимаются друг против друга.
С другой стороны, в конечном итоге объекты могут быть "упакованы" в "упаковочный файл". См. Другой раздел книги Pro Git для получения информации о файлах пакета. Объекты, хранящиеся в файлах пакета, "дельта-сжимаются" по отношению к другим объектам в том же файле. Какие именно критерии использует git для выбора того, какие объекты сжимаются, а какие другие объекты совершенно неясны. Вот фрагмент из книги Pro Git снова:
Когда Git упаковывает объекты, он ищет файлы с одинаковыми именами и размерами и сохраняет только дельты от одной версии файла к другой. Вы можете заглянуть в файл пакета и посмотреть, что Git сделал для экономии места. Сантехническая команда git verify-pack позволяет вам увидеть, что было упаковано [...]
Если git решает выполнить дельта-сжатие "записи пакета для большого файла A" vs "записи пакета для большого файла B", то - и только тогда - может git сэкономить место так, как вы просили.
Git делает файлы-архивы каждый раз git gc
проходит (или точнее, через git pack-objects
а также git repack
; операции более высокого уровня, в том числе git gc
, запустите их для вас, когда это необходимо / уместно). В это время git собирает незакрепленные объекты и / или взрывает и переупаковывает существующие пакеты. Если в этот момент ваши близкие, но не совсем идентичные файлы дельта-сжимаются друг против друга, вы можете столкнуться с очень большой экономией места.
Однако если вы затем измените файлы, вы будете работать с развернутыми и несжатыми версиями в вашем рабочем дереве, а затем git add
результат. Это создаст новый "незакрепленный объект", и по определению он не будет дельта-сжиматься ни с чем (ни с другим незакрепленным объектом, ни с пакетом).
Когда вы клонируете репозиторий, обычно git создает пакеты (или даже "тонкие пакеты", которые являются пакетами, которые не являются автономными) из объектов, которые нужно перенести, так, чтобы то, что отправляется через Intertubes, было как можно меньше. Так что здесь вы можете получить преимущество дельта-сжатия, даже если объекты в исходном репозитории свободны. Опять же, вы потеряете преимущество, как только начнете работать с этими файлами (превратив их в незакрепленные объекты), и восстановите его только в том случае, если и когда незакрепленные объекты снова будут упакованы и эвристика git сжимает их друг против друга.
Реальный вывод здесь заключается в том, что, чтобы узнать, вы можете просто попробовать это, используя метод, описанный в книге Pro Git.
Git сэкономит место, сохраняя только различия между файлами?
Да, git может упаковать файлы в сжатый формат.
У вас есть два почти идентичных объекта 4K на вашем диске. Разве не было бы неплохо, если бы Git мог сохранить один из них полностью, а затем второй объект только как дельту между ним и первым?
Оказывается, что может. Исходный формат, в котором Git сохраняет объекты на диске, называется свободным форматом объектов. Однако иногда Git упаковывает несколько таких объектов в один двоичный файл, называемый packfile, для экономии места и повышения эффективности. Git делает это, если у вас слишком много свободных объектов, если вы запускаете
git gc
Команда вручную, или если вы нажимаете на удаленный сервер. Чтобы увидеть, что происходит, вы можете вручную попросить Git упаковать объекты, вызвавgit gc
команда:
Да, оно может. Бег – это волшебство, которое может сделать это возможным. См. . @torek также упоминает кое-что из этого .
См., в частности, эту ссылку: 10.4 Git Internals - Packfiles: в дополнение к , например, ответ @Emil Davtyan здесьцитате в этом ответе здесь (выделено мной):
Что круто, так это то, что хотя объекты на диске до того, как вы запустили
gc
в совокупности имели размер около 15 КБ, а размер нового пак-файла составляет всего 7 КБ. Вы сократили использование диска вдвое , упаковав объекты.Как Git это делает? Когда Git упаковывает объекты, он ищет файлы с одинаковыми именами и размерами и сохраняет только различия от одной версии файла к другой.
Как опробовать его самостоятельно и посмотреть, сколько места вы сможете сэкономить
cd path/to/my_repo
# check the size of your repo's .git folder
du -sh .git
# try compressing your repo by running "git garbage collection"
time git gc
# re-check the size of your repo's .git folder
du -sh .git
Вот некоторые реальные результаты для меня:
В небольшом репозитории документации, в основном с уценкой.
.md
текстовые документы:1,7М --> 288К:
$ du -sh .git 1.7M .git $ git gc Enumerating objects: 182, done. Counting objects: 100% (182/182), done. Delta compression using up to 20 threads Compressing objects: 100% (178/178), done. Writing objects: 100% (182/182), done. Total 182 (delta 103), reused 4 (delta 0), pack-reused 0 $ du -sh .git 288K .git
В репозитории большего размера размером около 150 МБ с кодом и некоторыми двоичными файлами сборки:
50М --> 48М:
$ du -sh .git 50M .git $ time git gc Enumerating objects: 8449, done. Counting objects: 100% (8449/8449), done. Delta compression using up to 20 threads Compressing objects: 100% (2872/2872), done. Writing objects: 100% (8449/8449), done. Total 8449 (delta 5566), reused 8376 (delta 5524), pack-reused 0 real 0m1.603s user 0m2.098s sys 0m0.167s $ du -sh .git 48M .git
В совершенно новом каталоге размером 107 ГБ с 2,1 млн (2,1 миллиона) файлов из полудублирующихся данных за 25 лет, когда кто-то просто копировал одну и ту же папку размером 300 МБ снова и снова (сотни раз) в качестве своей системы контроля версий:
11 ГБ после первоначального процесса упаковки, который автоматически выполнялся после первого запуска для добавления всех файлов.
заняло 11 минут на ноутбуке очень высокого класса с очень высокоскоростным SSD.
Итак, поскольку
git gc
только что запустился автоматически послеgit commit
, изменений не видно, но очень впечатляет то, что 2,1 млн файлов размером 107 ГБ были упакованы всего до 11 ГБ!:Папка .git объемом 11 ГБ
$ du -sh .git 11G .git $ time git gc Enumerating objects: 190027, done. Counting objects: 100% (190027/190027), done. Delta compression using up to 20 threads Compressing objects: 100% (60886/60886), done. Writing objects: 100% (190027/190027), done. Total 190027 (delta 124418), reused 190025 (delta 124417), pack-reused 0 real 0m43.456s user 0m34.286s sys 0m6.565s $ du -sh .git 11G .git
Более подробную информацию можно найти в моем более подробном ответе здесь: