Работа с большими репозиториями в Git

В Git можно эффективно отслеживать эволюцию базы кода и вести разработку совместно с коллегами. Но как быть, если вы хотите отслеживать огромный репозиторий?
В этой статье я предлагаю несколько решений для такой проблемы.
Две категории больших репозиториев
Репозитории могут сильно разрастаться по одной из двух главных причин:
В них накапливается очень длинная история (за долгосрочным проектом тянется большой шлейф данных).
В них находятся огромные двоичные файлы, которые нужно отслеживать и сопоставлять с кодом.
А иногда бывает и то, и другое.
Вторая проблема иногда может усугубляться тем, что из репозитория вовремя не удаляют устаревшие двоичные артефакты. Решение здесь довольно простое, хотя и утомительное (см. далее).
Методы и обходные пути будут разными в каждом сценарии, но в некоторых случаях они могут и дополнять друг друга. Я опишу каждый по отдельности.
Клонирование репозиториев с очень длинной историей
По-настоящему крупные репозитории — редкость, но это не отменяет того, что клонировать их очень сложно. А избежать длинной истории удается не всегда: порой репозиторий нельзя сократить из-за нормативных или юридических требований.
Простое решение: поверхностное клонирование git
Первый способ быстрого клонирования, позволяющий сэкономить время разработчиков и системы, а также место на диске, заключается в том, чтобы копировать только недавние версии. При поверхностном клонировании Git из истории репозитория извлекается только заданное количество последних коммитов.
Как это сделать? Просто используйте параметр –depth. Вот несколько примеров.
git clone --depth [глубина] [удаленный-url]
Допустим, что репозиторий содержит историю проекта длиной в 10 лет или больше. Например, мы перенесли в Git базу кода Jira, накопившуюся за 11 лет. Экономия времени для таких репозиториев в сумме может давать ощутимую цифру.
Размер полного клона Jira составляет 677 МБ, а рабочего каталога, содержащего более 47 000 коммитов, — еще 320 МБ. Поверхностное клонирование репозитория займет 29,5 секунды, в то время как полное клонирование с копированием всей истории отнимет 4 минуты 24 секунды. И экономия времени возрастает пропорционально количеству двоичных файлов, накопившихся в проекте.
Совет: поверхностное клонирование с той же пользой можно применять и в системах сборки, подключенных к репозиторию Git.
Поскольку раньше некоторые операции имели скудную поддержку, поверхностное клонирование в Git применялось ограниченно. С последними версиями (1.9 и новее) дело обстоит гораздо лучше: теперь полноценная загрузка содержимого и его отправка в репозиторий возможна даже для поверхностных клонов.
Ювелирная точность: git filter-branch
Для наведения порядка в огромных репозиториях с большим количеством созданного по ошибке двоичного «мусора» и старых, ненужных файлов отлично подойдет команда git filter-branch. Она шаг за шагом проверяет всю историю проекта, фильтруя, изменяя или пропуская файлы по заданному алгоритму.
Это очень мощный инструмент, с которым вам потребуется лишь определить, какую часть репозитория нужно проредить. Это несложно, поскольку есть доступные скрипты, помогающие обнаружить крупные объекты.
Синтаксис имеет следующий вид:
git filter-branch --tree-filter 'rm -rf [/path/to/spurious/asset/folder]'При всей пользе у команды git filter-branch есть незначительный недостаток: фактически переписывается вся история проекта, то есть изменяются все идентификаторы коммитов. После этого каждому разработчику потребуется клонировать обновленный репозиторий.
Если вы собираетесь выполнить очистку с помощью git filter-branch, предупредите об этом команду и приостановите работу на время выполнения операции, а затем попросите коллег создать новый клон репозитория.
Совет: подробнее о команде git filter-branch можно узнать в этой статье, посвященной тому, как разделить репозиторий Git на части.
Альтернатива git shallow-clone: клонирование только одной ветки
Начиная с версии 1.7.10, в Git также можно клонировать отдельные ветки, тем самым ограничивая объем копируемой истории:
git clone [remote url] --branch [branch_name] --single-branch [folder]Этот специальный прием полезен, когда вы работаете с долгосрочными ветками, в которых накопилось много изменений, или когда у вас много веток, но работаете вы только с некоторыми из них. Если у вас мало веток и отличия между ними незначительны, этот способ не даст особых преимуществ.
Управление репозиториями с огромными двоичными файлами
Другой тип больших репозиториев характеризуется двоичными файлами огромных размеров. С ними сталкиваются самые разные команды разработчиков ПО (и не только). Разработчики игр манипулируют крупными 3D-моделями, веб-разработчики отслеживают необработанные изображения, а команды САПР работают с двоичными продуктами и отслеживают их состояние.
Среда Git неплохо приспособлена для работы с двоичными файлами, но особой эффективности ожидать не стоит. По умолчанию Git сжимает и сохраняет все последующие полные версии двоичных файлов, что не оптимально, если файлов много.
Ситуацию можно улучшить с помощью нескольких простых настроек. Так, можно запустить сборщик мусора git gc или настроить использование разностных коммитов для некоторых типов двоичных файлов в .gitattributes.
Но для того чтобы выбрать успешный подход, важно учесть характер двоичных файлов в проекте. Вот несколько подсказок.
Разностное сжатие, вероятнее всего, окажется бесполезным, если значительно изменяются сами двоичные файлы, а не просто заголовки метаданных. Укажите для таких файлов -delta, чтобы не выполнять разностное сжатие при переупаковке, когда это нецелесообразно.
В приведенном выше сценарии файлы вряд ли подходят и для сжатия с помощью zlib, поэтому имеет смысл отключить сжатие, указав core.compression 0 или core.loosecompression 0. Это глобальная настройка, поэтому поместите двоичные файлы в отдельный репозиторий, если возможность сжатия нужно сохранить для других файлов, для которых она более актуальна.
Важно помнить, что команда git gc упаковывает «повторяющиеся» свободные объекты в один файл, однако такой пакет не сэкономит много места без сжатия файлов внутри.
Попробуйте изменить параметр core.bigFileThreshold. По умолчанию любые файлы размером более 512 МБ не подвергаются разностному сжатию (без внесения изменений в .gitattributes). Возможно, корректировка порогового значения позволит сэкономить ресурсы.
Решение для больших деревьев папок: git sparse-checkout
Смягчить проблему двоичных файлов помогает выборочное переключение — sparse-checkout (доступно начиная с Git 1.7.0). Этот метод позволяет явно указать папки, которые нужно заполнить, и не засорять рабочий каталог. К сожалению, использование sparse-checkout никак не повлияет на общий размер локального репозитория, но зато облегчит работу с «ветвистым» деревом папок.
Для этого можно использовать, например, следующие команды.
Клонируйте полный репозиторий один раз: git clone
Активируйте функцию: git config core.sparsecheckout true
Добавьте нужные папки явным образом, игнорируя папки с файлами:
echo src/ › .git/info/sparse-checkout
Считайте дерево таким образом:
git read-tree -m -u HEAD
После этих действий вы сможете вернуться к выполнению обычных команд Git, но рабочий каталог будет содержать только заданные выше папки.
Решение для управления обновлением больших файлов: подмодули
[ОБНОВЛЕНИЕ] …А еще можно забыть все, что вы прочитали выше, и воспользоваться Git LFS
Если вы регулярно работаете с большими файлами, лучшим выходом может стать поддержка больших файлов (LFS) — решение, разработанное Atlassian совместно с GitHub в 2015 году. (Да, все верно: мы объединились с GitHub, чтобы реализовать проект Git с открытым исходным кодом.)
Расширение Git LFS позволяет хранить в репозитории не сами большие файлы, а только (естественно!) указатели на них. Сами же файлы хранятся на удаленном сервере. Нетрудно догадаться, что благодаря этому время клонирования репозитория значительно сокращается.

Bitbucket, как и GitHub, тоже поддерживает Git LFS, так что технология вам, скорее всего, уже доступна. Она особенно полезна для команд, в которых трудятся дизайнеры, видеооператоры, музыканты или пользователи САПР.
Заключение
Не отказывайтесь от потрясающих возможностей Git только из-за большой истории репозитория или огромных файлов. Обе эти проблемы можно эффективно решить.
Перейдите по ссылкам в этом тексте, чтобы прочитать другие статьи и узнать больше о подмодулях, зависимостях проектов и о решении Git LFS. А если хотите освежить в памяти команды или рабочий процесс, вам доступно множество учебных руководств на нашем микросайте, посвященном Git. Удачного программирования!