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

Вот некоторые реальные результаты для меня:

  1. В небольшом репозитории документации, в основном с уценкой..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
    
  2. В репозитории большего размера размером около 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
    
  3. В совершенно новом каталоге размером 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
    

    Более подробную информацию можно найти в моем более подробном ответе здесь:

Смотрите также:

  1. Каковы ограничения на файлы в Git (количество и размер)?
    1. Каковы ограничения на файлы в Git (количество и размер)?мой ответ
Другие вопросы по тегам