Git с большими файлами

Ситуация

У меня есть два сервера - "Производство и развитие". На сервере Production есть два приложения и несколько (6) баз данных (MySQL), которые мне нужно распространять для разработчиков для тестирования. Все исходные коды хранятся в GitLab на сервере разработки, и разработчики работают только с этим сервером и не имеют доступа к производственному серверу. Когда мы выпускаем приложение, мастер регистрируется в процессе производства и вытаскивает новую версию из Git. Базы данных большие (более 500 М каждая и подсчет), и мне нужно как можно проще распространять их для разработчиков для тестирования.

Возможные решения

  • После резервного копирования script, который удаляет базы данных, каждый в один файл, выполняет script, который подталкивает каждую базу данных к своей ветки. Разработчик вытаскивает одну из этих ветвей, если хочет обновить свою локальную копию.

    Этот файл был найден неработоспособным.

  • Cron на производственном сервере каждый день сохраняет двоичные журналы и помещает их в ветвь этой базы данных. Итак, в ветке есть файлы с ежедневными изменениями, а разработчик извлекает файлы, которых у него нет. Текущий SQL-дамп будет отправлен разработчику другим способом. И когда размер репозитория становится слишком большим, мы отправим полный дамп разработчикам и сфотографируем все данные в репозитории и начнем с самого начала.

Вопросы

  • Возможно ли решение?
  • Если git нажимает/вытягивает в/из репозитория, загружает/загружает целые файлы или просто изменения в них (т.е. добавляет новые строки или редактирует текущие)?
  • Может ли git управлять такими большими файлами? Нет.
  • Как установить, сколько ревизий сохраняется в репозитории? Не имеет значения с новым решением.
  • Есть ли лучшее решение? Я не хочу, чтобы разработчики загружали такие большие файлы по FTP или что-то подобное.

Ответ 1

rsync может быть хорошим вариантом для эффективного обновления созданных копий баз данных.

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

По существу, вы получаете такое же инкрементное обновление, как git fetch, без постоянно расширяющейся исходной копии, которую даст клон git. Потеря не имеет истории, но звучит так, как будто вам это не нужно.

rsync является стандартной частью большинства дистрибутивов Linux, если вам это нужно в окнах, имеется доступный порт: 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

Ответ 2

Обновление 2017:

Microsoft вносит вклад в Microsoft/GVFS: Git Виртуальная файловая система, которая позволяет Git дескриптор " самый большой репо на планете"
(т.е. база данных Windows, которая составляет приблизительно 3,5 Мбайта, и, когда она зарегистрирована в репозитории Git, приводит к репо около 300 ГБ и производит 1,760 ежедневных "лабораторных сборок" по 440 ветким в дополнение к тысячам вытащить запрос валидации запроса)

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

Некоторые части GVFS могут быть переданы вверх по течению (до самого Git).
Но в то же время все новые разработки Windows теперь (август 2017 года) на Git.


Обновление апреля 2015 года: GitHub предлагает: Объявление Git Большие файловые хранилища (LFS)

Использование git -lfs (см. git-lfs.github.com) и сервер, поддерживающий его: lfs-test-server, вы можете хранить метаданные только в репозитории Git и в большом файле в другом месте.

https://cloud.githubusercontent.com/assets/1319791/7051226/c4570828-ddf4-11e4-87eb-8fc165e5ece4.gif

См. git-lfs/wiki/Tutorial:

git lfs track '*.bin'
git add .gitattributes "*.bin"
git commit -m "Track .bin files"

Оригинальный ответ:

Что касается ограничений Git с большими файлами, вы можете рассмотреть bup (подробно представлен в GitMinutes # 24)

дизайн bup выделяет три проблемы, которые ограничивают Git repo:

  • огромные файлы (xdelta для packfile находится только в памяти, что не очень хорошо с большими файлами)
  • огромное количество файлов, что означает, что один файл на blob и медленный git gc для генерации одного пакетного файла за раз.
  • огромные packfiles, при этом индекс packfile неэффективен для извлечения данных из (огромного) пакета.

Обработка огромных файлов и 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.

С hashsplitting, независимо от того, сколько данных вы добавляете, изменяете или удаляете в середине файла, все куски до и после затронутого фрагмента абсолютно одинаковы.
Все, что имеет значение для алгоритма hashsplitting, - это 32-байтовая последовательность "разделитель", и одно изменение может влиять, по крайней мере, на одну разделительную последовательность или байты между двумя разделительными последовательностями.
Как и магия, алгоритм хэширования chhschit будет каждый раз вырезать ваш файл таким же образом, даже не зная, как он ранее его обменивал.

Следующая проблема менее очевидна: после того как вы сохранили свою серию кусков как Git blobs, как вы сохраняете их последовательность? Каждый blob имеет 20-байтовый идентификатор sha1, что означает, что простой список блоков будет 20/8192 = 0.25% длины файла.
Для 200-гигабайтного файла это 488 мегабайт только данных последовательности.

Мы расширяем алгоритм hashsplit немного дальше, используя то, что мы называем "разветвлением". Вместо того, чтобы проверять только последние 13 бит контрольной суммы, мы используем дополнительные бит контрольной суммы для создания дополнительных разделов.
То, что вы в конечном итоге, является фактическим деревом blobs - которые Git "tree" объекты идеально подходят для представления.

Обработка огромного количества файлов и git gc

git предназначен для обработки репозиториев разумного размера, которые относительно редко изменяются. Вы можете подумать, что вы часто меняете свой исходный код и что Git обрабатывает гораздо более частые изменения, чем, скажем, svn может обрабатывать.
Но это не то же самое "часто", о котором мы говорим.

Убийца # 1 - это то, как он добавляет новые объекты в репозиторий: он создает один файл на blob. Затем вы запустите "git gc" и объедините эти файлы в один файл (используя высокоэффективное сжатие xdelta и игнорируя любые файлы, которые больше не актуальны).

'git gc' медленный, но для репозиториев исходных кодов заслуживает внимания получившееся суперэффективное хранилище (и связанный с ним действительно быстрый доступ к сохраненным файлам).

bup не делает этого. Он просто пишет packfiles напрямую.
К счастью, эти packfiles все еще git -форматированы, поэтому Git может с радостью получить к ним доступ один раз они написаны.

Обработка огромного репозитория (что означает огромное количество огромных пакетов)

git на самом деле не предназначен для обработки супер-огромных репозиториев.
Большинство хранилищ Git достаточно малы, что разумно объединить их все в один пакетный файл, который обычно "git gc".

Проблемная часть больших packfiles не является самими packfiles - Git предназначен для ожидания того, что общий размер всех пакетов будет больше, чем доступная память, и как только он сможет справиться с этим, он может обрабатывать практически любое количество данные о такой же эффективности.
Проблема - это файлы индексов packfile (.idx).

каждый файл packfile (*.pack) в Git имеет связанный idx (*.idx), который сортирует список хешей объектов и wav файлов. Если вы ищете конкретный объект на основе его sha1, вы открываете idx, бинарный поиск, чтобы найти правильный хеш, затем берете связанное смещение файла, ищите это смещение в файле packfile и читаете содержимое объекта.

Производительность двоичного поиска составляет около O(log n) с количеством хэшей в пакете с оптимизированным первым шагом (вы можете прочитать об этом в другом месте), что несколько улучшает его до O(log(n)-7).
К сожалению, этот ломается немного, когда у вас много пакетов.

Чтобы повысить производительность такого рода операций, bup вводит midx (произносится как "midix" и short для "multi-idx" ) файлов.
Как следует из названия, они индексируют несколько пакетов за раз.

Ответ 3

Вы действительно, действительно, действительно не хотите, чтобы большие двоичные файлы были проверены в вашем репозитории Git.

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

Если вам нужно предоставить большие двоичные файлы, загрузите их на какой-либо сервер отдельно, а затем проверьте текстовый файл с URL-адресом, где разработчик может загрузить большой двоичный файл. FTP фактически является одним из лучших вариантов, поскольку он специально предназначен для передачи двоичных файлов, хотя HTTP, вероятно, еще более прост.

Ответ 4

Вы можете найти решение, например git-annex, что касается управления (большими) файлами с git, не проверяя содержимое файла на git (!)
(Февраль 2015: сервисный хостинг, такой как GitLab, интегрирует его изначально:
См. "Поддерживает ли GitLab большие файлы через git-annex или иначе?" )

git не управляет большими файлами, как объясняется Amber в ее ответе.

Это не значит, что git не сможет сделать лучше в один прекрасный день. Из эпизод GitMinutes 9 ( май 2013, см. Также ниже), Peff (Jeff King), на 36'10 '':

(стенограмма)

Есть все другие области больших хранилищ, где люди заинтересованы в хранении, вы знаете, 20 или 30 или 40 ГБ, иногда даже в хранилищах размером с ТБ, и да, это происходит из-за большого количества файлов, но много это происходит от наличия действительно больших файлов и действительно больших файлов двоичных файлов, которые не так хорошо работают друг с другом.

Такая открытая проблема. Есть несколько решений: git -annex, вероятно, самый зрелый из тех, где они в основном не кладут актив в git, они кладут большой актив на сервер активов и помещают указатель в git.

Я хотел бы сделать что-то подобное, когда объект концептуально находится в git, то есть SHA1 этого объекта является частью SHA1, который входит в дерево, который переходит в идентификатор фиксации и все эти вещи.
Таким образом, с точки зрения git она является частью репозитория, но на уровне ниже, на уровне хранения объектов, на уровне ниже графика концептуальной истории, где у нас уже есть несколько способов хранения объекта: мы имеем потерянные объекты, у нас упакованные объекты, я хотел бы иметь, возможно, новый способ хранения объекта, который сказать "у нас его нет, но он доступен на сервере активов" или что-то в этом роде.

(Томас Феррис Николаисен) О, круто...

Проблема с такими вещами, как git-annex: после того, как вы их используете, вы... заблокированы решениями, которые вы делали в то время навсегда. Вы знаете, что если вы решите, что 200 МБ - большой, и мы собираемся хранить на сервере активов, а потом, позже вы решите, аа, это должно было быть 300 МБ, и это было бы непросто: это закодировано в вашей истории навсегда. < ш > И поэтому, говоря концептуально, на уровне git этот объект находится в репозитории git, а не какой-то указатель на него, а не какой-то указатель на сервер активов, фактический объект есть и затем заботится о этих деталях на низком уровне, на уровне хранилища, то это освобождает вас, чтобы принимать множество различных решений, и даже позже измените свое решение о том, как вы действительно хотите хранить материал на диске.

Не высокоприоритетный проект на данный момент...


Через 3 года, в апреле 2016 года, git Минуты 40 включает интервью Майкл Хаггерти от GitHub около 31 '(Спасибо Christian Couder для интервью).

Он специализирующийся на справочном контенте довольно долгое время.
Он цитирует Дэвид Тернер работа в качестве самого интересного на данный момент. (См. ветвь Дэвида "pluggable-backends" ветки его git/git)

(стенограмма)

Christian Couder (CD): Цель состоит в том, чтобы иметь git refs, хранящийся в базе данных, например? Майкл Хаггерти (MH): Да, я рассматриваю это как два интересных аспекта: Первый - это просто возможность подключить разные ссылки на исходные записи. Ссылки на запись хранятся в файловой системе как комбинация свободных ссылок и упакованные ссылки.
Свободная ссылка - это один файл для каждой ссылки, а упакованная ссылка - это один большой файл, содержащий список многих ссылок.

Так что хорошая система, особенно для локального использования; поскольку у него нет реальной проблемы с производительностью для нормальных людей, но у него есть некоторые проблемы, например, вы не можете хранить ссылки reflogs после удаления ссылок, потому что могут быть конфликты с более новыми ссылками, которые были созданы с похожими имена. Также существует проблема, когда ссылочные имена хранятся в файловой системе, поэтому вы можете иметь ссылки, которые называются похожими, но с различной капитализацией.
Таким образом, это те вещи, которые могут быть исправлены с помощью другой базовой справочной системы в целом.
И другой аспект серии патчей Дэвида Тернера - это изменение для хранения ссылок в базе данных под названием lmdb, это действительно быстрая база данных на базе памяти, которая имеет некоторые преимущества в производительности над файловым контентом.

[следует другим соображениям, связанным с более быстрой упаковкой и ссылкой на рекламу патчей]

Ответ 5

Наличие дополнительного хранилища файлов, на которое ссылается ваш git -статический код, - это то, куда большинство людей идет. git-annex выглядит довольно всеобъемлющим, но многие магазины используют FTP или HTTP (или S3) репозиторий для больших файлов, таких как SQL-дампы. Мое предложение состояло в том, чтобы связать код в репозитории git с именами файлов во вспомогательном хранилище, набив некоторые из метаданных - в частности, контрольную сумму (возможно, SHA) - в хэш, а также дату.

  • Таким образом, каждый файл aux получает значение basename, date и SHA (для некоторой версии n).
  • Если у вас есть дикий файловый оборот, использование только SHA создает небольшую, но реальную угрозу столкновения хэшей, следовательно, включение даты (время эпохи или дата ISO).
  • Поместите полученное имя файла в код, так что дополнительный кусок включен, в частности, по ссылке.
  • Структурируйте имена таким образом, чтобы немного script можно было легко записать в git grep все имена файлов aux, чтобы список для любого фиксации был тривиальным для получения. Это также позволяет старым быть в отставке в какой-то момент и может быть интегрировано с системой развертывания, чтобы вытащить новые файлы aux для производства без скрещивания старых (пока) до активации кода из репозитория git.

Крамминг огромных файлов в git (или большинстве репозиториев) оказывает неприятное влияние на производительность git через некоторое время - a git clone действительно не должен занимать двадцать минут, например. Принимая во внимание, что использование файлов по ссылке означает, что некоторым разработчикам вообще не нужно будет загружать большие куски (резко контрастировать с git clone), так как вероятность того, что большинство из них имеет отношение только к развернутому коду в производстве. Конечно, ваш пробег может меняться.

Ответ 6

Я только что открыл новую причину, чтобы не проверять большие файлы на Git. Возможно, это не сработает.

Перемотайте назад в конструкцию ПК. У вас очень большая область хранения файлов и меньший объем оперативной памяти. ОС может обменивать физическое содержимое ОЗУ в хранилище и из него, но при этом может произойти сбой в распределении памяти с возможными последствиями для задействованной программы. Таким образом, программы, работающие с большими файлами, НЕ должны загружать весь файл в ОЗУ, потому что это было бы глупо.

Есть другие способы обойти это, особенно с базами данных. Например, вы можете физически хранить данные как ряд небольших файлов, каждый из которых является разумным размером для полной загрузки в ОЗУ.

К сожалению, сервер Git не построен с большими файлами. Stash представляет собой несжатый двоичный файл объемом 1,3 Гбайт, а сервер Git - malloc 1.3GB (плюс zip-заголовки) оперативной памяти для работы с этим файлом, когда ваш клиент клонирует этот репозиторий Git. Насколько счастлив ваш сервер с этим? Наверняка этого не было. Результат: malloc терпит неудачу. Я знаю это, не проверив источник Git, потому что сообщение об ошибке, о котором сообщается сервером Git, когда оно не удалось, и что именно размер одного из файлов в нашем репо.

Действительно забавная часть этого заключалась в том, что сервер Git был совершенно счастлив, что файлы были переброшены в его репо. Только когда мы вытащили репо у клиента, мы обнаружили, что это односторонняя операция!

Это не критиковать Git как инструмент - как разработчика, я люблю его. Но есть одна конкретная его часть, чья имплантация очень наивна и, следовательно, нарушена, и что управление большими файлами.