Git с большими файлами
ситуация
У меня есть два сервера, производство и разработка. На Производственном сервере есть два приложения и несколько (6) баз данных (MySQL), которые мне нужно распространить среди разработчиков для тестирования. Все исходные коды хранятся в GitLab на сервере разработки, и разработчики работают только с этим сервером и не имеют доступа к производственному серверу. Когда мы выпускаем приложение, мастер входит в систему и извлекает новую версию из Git. Базы данных большие (более 500M каждая и считаются), и мне нужно как можно проще распространять их среди разработчиков для тестирования.
Возможные решения
После сценария резервного копирования, который выводит базы данных, каждая в один файл, выполните сценарий, который помещает каждую базу данных в свою собственную ветвь.Разработчик тянет одну из этих веток, если хочет обновить свою локальную копию.Этот был найден нерабочим.
Cron на рабочем сервере каждый день сохраняет двоичные журналы и помещает их в ветку этой базы данных. Итак, в ветке есть файлы с ежедневными изменениями, и разработчик извлекает файлы, которых у него нет. Текущий дамп SQL будет отправлен разработчику другим способом. И когда размер репозитория становится слишком большим, мы отправим полный дамп разработчикам и сбросим все данные в репозитории и начнем с самого начала.
Вопросы
- Возможно ли решение?
- Если git загружает / извлекает данные из хранилища, загружает ли он / скачивает целые файлы или просто изменяет их (т.е. добавляет новые строки или редактирует текущие)?
-
Может ли Git управлять такими большими файлами?Нет. -
Как установить, сколько ревизий сохраняется в хранилище?Не имеет значения с новым решением. - Есть ли лучшее решение? Я не хочу заставлять разработчиков загружать такие большие файлы через FTP или что-то подобное.
7 ответов
rsync может быть хорошим вариантом для эффективного обновления копий баз данных разработчиками.
Он использует дельта-алгоритм для постепенного обновления файлов. Таким образом, он передает только блоки файла, которые были изменены или являются новыми. Конечно, сначала им все равно нужно будет загрузить полный файл, но последующие обновления будут выполняться быстрее.
По сути, вы получаете такое же инкрементное обновление, как git fetch, без постоянно расширяющейся начальной копии, которую выдает git clone. Потеря не имеет истории, но звучит так, будто вам это не нужно.
rsync является стандартной частью большинства дистрибутивов Linux, если вам это нужно, в Windows есть упакованный порт: http://itefix.no/cwrsync/
Чтобы передать базы данных разработчику, вы можете использовать команду, аналогичную следующей:
rsync -avz path/to/database(s) HOST:/folder
Или разработчики могут получить необходимую базу данных:
rsync -avz DATABASE_HOST:/path/to/database(s) path/where/developer/wants/it
Обновление 2017:
Microsoft вносит свой вклад в Microsoft / GVFS: виртуальную файловую систему Git, которая позволяет Git обрабатывать " самое большое репо на планете "
(то есть: кодовая база Windows, которая составляет приблизительно 3,5 МБ файлов и при регистрации в репозитории Git приводит к репо около 300 ГБ и производит 1760 ежедневных "лабораторных сборок" в 440 ветвях в дополнение к тысячам запросов на извлечение валидация сборки)
GVFS виртуализирует файловую систему под вашим репозиторием git, так что git и все инструменты видят то, что кажется нормальным репо, но GVFS загружает объекты только по мере необходимости.
Некоторые части GVFS могут быть добавлены в апстрим (для самого Git).
Но пока что все новые разработки для Windows сейчас (август 2017) на Git.
Обновление апрель 2015: GitHub предлагает: анонс Git Large File Storage (LFS)
Используя git-lfs (см. https://git-lfs.github.com/) и поддерживающий его сервер: lfs-test-server, вы можете хранить метаданные только в git-репо и в большом файле в другом месте.
Смотрите git-lfs / wiki / Tutorial:
git lfs track '*.bin'
git add .gitattributes "*.bin"
git commit -m "Track .bin files"
Оригинальный ответ:
Что касается ограничений git для больших файлов, вы можете рассмотреть bup (подробно представлен в GitMinutes # 24)
Дизайн bup выдвигает на первый план три проблемы, которые ограничивают git-репо:
- огромные файлы ( xdelta для packfile находится только в памяти, что плохо с большими файлами)
- огромное количество файлов, что означает, один файл на блоб, и медленный
git gc
генерировать один пакетный файл за раз. - огромные файлы пакета, с индексом файла пакета, неэффективным для извлечения данных из (огромного) файла пакета.
Обработка огромных файлов и xdelta
Основная причина, по которой git не может обрабатывать огромные файлы, заключается в том, что он запускает их
xdelta
, что обычно означает, что он пытается загрузить все содержимое файла в память одновременно.
Если он этого не сделает, ему придется хранить все содержимое каждой ревизии каждого отдельного файла, даже если вы изменили только несколько байтов этого файла.
Это было бы ужасно неэффективным использованием дискового пространства, и git хорошо известен своим удивительно эффективным форматом хранилища.К несчастью,
xdelta
отлично работает с небольшими файлами и работает очень медленно и требует много памяти для больших файлов.
Для основной цели Git, т.е. Управление вашим исходным кодом, это не проблема.То, что делает bup вместо xdelta, это то, что мы называем
hashsplitting
".
Нам нужен универсальный способ эффективного резервного копирования любого большого файла, который может незначительно измениться, без сохранения всего файла каждый раз. Мы читаем файл по одному байту за раз, вычисляя скользящую контрольную сумму последних 128 байтов.
rollsum
Кажется, хорошо справляется со своей работой. Вы можете найти это вbupsplit.c
,
По сути, он преобразует последние 128 байтов в 32-разрядное целое число. Затем мы берем младшие 13 бит роллсума, и если все они равны 1, мы считаем, что это конец фрагмента.
Это происходит в среднем один раз каждый2^13 = 8192 bytes
Таким образом, средний размер фрагмента составляет 8192 байта.
Мы делим эти файлы на куски на основе скользящей контрольной суммы.
Затем мы храним каждый кусок отдельно (индексируется по его sha1sum) как git blob.С хэш-разделением, независимо от того, сколько данных вы добавляете, изменяете или удаляете в середине файла, все фрагменты до и после затронутого фрагмента абсолютно одинаковы.
Все, что имеет значение для алгоритма разделения хеша, - это 32-байтовая последовательность "разделителей", и одно изменение может повлиять не более, чем на одну последовательность разделителей или байты между двумя последовательностями разделителей.
Как и в случае с магией, алгоритм разбиения на блоки hashsplit будет каждый раз блокировать файл одинаково, даже не зная, как он был ранее.Следующая проблема менее очевидна: как вы сохраняете их последовательность после того, как вы сохранили свои серии фрагментов в виде git-объектов? Каждый BLOB-объект имеет 20-байтовый идентификатор sha1, что означает, что простой список BLOB-объектов будет
20/8192 = 0.25%
длины файла.
Для файла 200 ГБ это 488 мегабайт только данных последовательности.Мы немного расширили алгоритм hashsplit, используя то, что мы называем "разветвлением". Вместо того, чтобы проверять только последние 13 бит контрольной суммы, мы используем дополнительные биты контрольной суммы для получения дополнительных разбиений.
То, что вы в итоге получите, - это настоящее дерево больших объектов, которые идеально подходят для представления объектов git 'tree'.
Обработка огромного количества файлов и git gc
git предназначен для работы с репозиториями разумного размера, которые меняются относительно редко. Вы можете подумать, что вы меняете свой исходный код "часто", и этот git обрабатывает гораздо более частые изменения, чем, скажем,
svn
может справиться.
Но это не тот тип "часто", о котором мы говорим.Убийца № 1 - это способ добавления новых объектов в хранилище: он создает один файл для каждого большого двоичного объекта. Затем вы запускаете git gc и объединяете эти файлы в один файл (используя высокоэффективное сжатие xdelta и игнорируете файлы, которые больше не нужны).
'
git gc
"работает медленно, но для репозиториев исходного кода получающееся в результате суперэффективное хранилище (и связанный с ним действительно быстрый доступ к хранимым файлам) того стоит.
bup
не делает этого Он просто пишет упаковочные файлы напрямую.
К счастью, эти упаковочные файлы все еще отформатированы в git, так что git может легко получить к ним доступ, как только они будут написаны.
Обработка огромного хранилища (имеется в виду огромное количество огромных упаковочных файлов)
Git не предназначен для работы с огромными хранилищами.
Большинство git-репозиториев настолько малы, что разумно объединить их в один упаковочный файл, которыйgit gc
обычно делает в конце концов.Проблематичной частью больших пакетных файлов являются не сами пакетные файлы - git предназначен для того, чтобы ожидать, что общий размер всех пакетов будет больше доступной памяти, и, как только он справится с этим, он может обрабатывать практически любой объем данных примерно одинаково эффективно.
Проблема в индексах packfile (.idx
) файлы.каждый пакетный файл (
*.pack
) в мерзавце есть связанныйidx
(*.idx
) это отсортированный список хэшей объектов git и смещений файлов.
Если вы ищете конкретный объект, основанный на его sha1, вы открываете idx, бинарный поиск, чтобы найти правильный хеш, затем берете соответствующее смещение файла, ищите это смещение в файле пакета и читаете содержимое объекта.Производительность бинарного поиска составляет около
O(log n)
с количеством хэшей в пакете, с оптимизированным первым шагом (вы можете прочитать об этом в другом месте), который несколько улучшает егоO(log(n)-7)
,
К сожалению, это немного ломается, когда у вас много пакетов.Чтобы повысить производительность такого рода операций, bup вводит
midx
(произносится как "midix" и сокращенно для "multi-idx") файлов.
Как видно из названия, они индексируют несколько пакетов одновременно.
Вы действительно действительно не хотите, чтобы большие двоичные файлы регистрировались в вашем Git-репозитории.
Каждое добавленное вами обновление будет кумулятивно увеличиваться в общем размере вашего репозитория, а это означает, что в будущем вашему репозиторию Git потребуется все больше и больше времени для клонирования и использования все большего и большего дискового пространства, потому что Git хранит всю историю ветки локально, это означает, что когда кто-то проверяет ветку, ему не нужно просто загружать последнюю версию базы данных; они также должны будут загрузить каждую предыдущую версию.
Если вам нужно предоставить большие двоичные файлы, загрузите их на некоторый сервер отдельно, а затем зарегистрируйте текстовый файл с URL-адресом, по которому разработчик может загрузить большой двоичный файл. FTP на самом деле является одним из лучших вариантов, поскольку он специально разработан для передачи двоичных файлов, хотя HTTP, вероятно, еще более прост.
Вы можете посмотреть на решение наподобие git-annex, которое касается управления (большими) файлами с помощью git, без проверки содержимого файла в git (!)
(Февраль 2015: сервис-хостинг, такой как GitLab, изначально интегрирован:
Смотрите " Поддерживает ли GitLab большие файлы через git-annex
или иным образом? ")
Git не управляет большими файлами, как объяснила Amber в своем ответе.
Это не значит, что мерзавец не сможет добиться большего успеха за один день.
Из эпизода 9 GitMinutes (май 2013 г., см. Также ниже), от Пеффа (Джефф Кинг), в 36'10 '':
(Стенограмма)
Существует множество других репозиториев, где люди заинтересованы в хранении, вы знаете, 20, 30 или 40 ГБ, иногда даже репозиториев размером с ТБ, и да, это происходит из-за большого количества файлов, но много из этого приходит от наличия действительно больших файлов и действительно больших двоичных файлов, которые не очень хорошо работают друг с другом.
Это своего рода открытая проблема. Есть пара решений: git-annex, вероятно, является наиболее зрелым из тех, где они в основном не помещают актив в git, они помещают большой актив на сервер ресурсов и помещают указатель в git.
Я хотел бы сделать что-то подобное, когда ресурс концептуально находится в git, то есть SHA1 этого объекта является частью SHA1, которая входит в дерево, которое входит в идентификатор фиксации и все эти вещи.
Таким образом, с точки зрения git, это часть репозитория, но на уровне ниже, на уровне хранения объектов, на уровне ниже концептуального графа истории, где у нас уже есть несколько способов хранения объекта: у нас есть свободные объекты, мы Если у меня есть упакованные объекты, я бы хотел иметь новый способ хранения объекта, который говорит: "У нас его нет, но он доступен сервером ресурсов", или что-то в этом роде.( Томас Феррис Николаизен) Ох, круто...
Проблема с такими вещами, как
git-annex
это: как только вы их используете, вы... заперты на решения, которые вы приняли в то время навсегда. Вы знаете, что если вы решите, что 200 МБ - это большой объем, и мы собираемся хранить его на сервере активов, а затем, позже, вы решите, что это должно было быть 300 МБ, не повезло: это закодировано в вашей истории навсегда.
Итак, говоря концептуально, на уровне git этот объект находится в репозитории git, а не какой-то указатель на него, не какой-то указатель на сервер ресурсов, фактический объект есть, а затем позаботится об этих деталях на низком уровне. на уровне хранилища, это освобождает вас от необходимости принимать множество разных решений и даже позже менять свое решение о том, как вы на самом деле хотите хранить данные на диске.
Пока не высокоприоритетный проект...
Спустя 3 года, в апреле 2016 года, Git Minutes 40 включает интервью Майкла Хаггерти из GitHub около 31 года (спасибо Кристиану Каудеру за интервью).
Он специализируется на референсном бэкэнде довольно давно.
Он цитирует работу Дэвида Тернера на back-end как наиболее интересную на данный момент. (Смотрите тока Дэвида " pluggable-backends
"ветка его мерзавца / мерзавца")
(Стенограмма)
Кристиан Каудер (CD): Цель состоит в том, чтобы, например, хранить git-ссылки в базе данных? Майкл Хаггерти (MH): Да, я рассматриваю это как два интересных аспекта: первый - просто возможность подключать разные ссылки на источники. Ссылки на записи хранятся в файловой системе как комбинация свободных ссылок и упакованных ссылок.
Свободная ссылка - это один файл на ссылку, а упакованная ссылка - это один большой файл, содержащий список из множества ссылок.Так что это хорошая система, особенно для локального использования; поскольку у обычных людей проблем с производительностью нет, но есть некоторые проблемы, например, вы не можете сохранять ссылки на журналы после удаления ссылок, потому что могут быть конфликты с новыми ссылками, которые были созданы с похожими имена. Существует также проблема, когда имена ссылок хранятся в файловой системе, поэтому у вас могут быть ссылки, которые называются одинаковыми, но с разной капитализацией.
Так что это вещи, которые могут быть исправлены с помощью другой системы ссылок в целом.
И еще один аспект серии исправлений Дэвида Тернера - это изменение хранения ссылок в базе данных с именем lmdb. Это действительно быстрая база данных на основе памяти, которая имеет некоторые преимущества в производительности по сравнению с файловой частью.
[следует другим соображениям, касающимся более быстрой упаковки, и ссылочной рекламы патчей]
Наличие вспомогательного хранилища файлов, на которые ссылается ваш git-stashed-код, - это то, куда большинство людей отправляется. git-annex
выглядит довольно всеобъемлющим, но многие магазины просто используют FTP или HTTP (или S3) хранилище для больших файлов, таких как дампы SQL. Мое предложение состояло бы в том, чтобы связать код в репозитории git с именами файлов во вспомогательном хранилище, вставив некоторые метаданные - в частности, контрольную сумму (вероятно, SHA) - в хэш, а также в дату.
- Таким образом, каждый вспомогательный файл получает базовое имя, дату и сумму SHA(для некоторой версии n).
- Если у вас дикий оборот файлов, использование только SHA представляет крошечную, но реальную угрозу коллизии хешей, следовательно, включение даты (время эпохи или дата ISO).
- Поместите получившееся имя файла в код, чтобы фрагмент aux был включен, в частности, по ссылке.
- Структурируйте имена таким образом, чтобы можно было легко написать небольшой скрипт для git grep всех имен файлов aux, чтобы получить список для любого коммита. Это также позволяет в какой-то момент удалить старые файлы и может быть интегрировано с системой развертывания, чтобы вывести новые файлы aux в производство, не забивая старые (пока), перед активацией кода из репозитория git.
Создание больших файлов в git (или в большинстве репозиториев) через некоторое время отрицательно сказывается на производительности git. git clone
на самом деле не должно занимать двадцать минут, например. Принимая во внимание, что использование файлов по ссылке означает, что некоторым разработчикам вообще не нужно загружать большие куски (резкий контраст с git clone
), поскольку вероятность того, что большинство из них имеют отношение только к развернутому коду в производстве. Конечно, ваш пробег может отличаться.
Загрузка больших файлов иногда создает проблемы и ошибки. Это обычно происходит. В основном git поддерживает загрузку менее 50 МБ. Для загрузки более 50 МБ файлов в репозиторий git пользователю необходимо установить еще один помощник для совместной загрузки большого файла (.mp4,.mp3,.psd) и т. Д.
Есть несколько основных команд git, которые вы знаете до загрузки большого файла в git. это конфигурация для загрузки на github. для этого нужно установить gitlfs.exe
установить его из lfsinstall.exe
тогда вы должны использовать основные команды git вместе с некоторыми другими
git lfs install
git init
git lfs track ".mp4"
git lfs track ".mp3"
git lfs track ".psd"
git add .
git add .gitattributes
git config lfs.https://github.com/something/repo.git/info/lfs.locksverify false
git commit -m "Add design file"
git push origin master` ones
Вы можете найти, что найдете это lfs.https://github.com/something/repo.git/info/lfs.locksverify false
как инструкции во время команды push, если push без использования
Как указано во многих других ответах, хранение больших файлов в git крайне не рекомендуется. Я больше не буду повторять это.
Ваши вопросы больше похожи на вопрос о постоянстве базы данных, а не на git. Если информации о базе не так много, то
- Для Java вы можете использовать flywaydb(java) для хранения различий в базе данных между каждым выпуском.
- Для Django он может хранить информацию о БД в json dump (
python manage.py dumpdata your_app > datadump.json
) и перезагрузите его где-нибудь еще (python manage.py loaddata datadump.json
)
Однако, поскольку ваша БД велика, вам следует рассмотреть популярные двоичные хранилища, такие как nexus или artifactory, которые могут хранить двоичные файлы или использоваться в качестве хранилища для gitlfs. Затем, чтобы облегчить бремя разработчиков, потому что вы не хотите, чтобы они явно загружали файл, вам нужно создать свой собственный конвейер CI / CD, который позволит разработчикам публиковать его одним щелчком мыши.