web-dev-qa-db-fra.com

Git avec de gros fichiers

Situation

J'ai deux serveurs, Production et Développement. Sur le serveur de production, il y a deux applications et plusieurs (6) bases de données (MySQL) que je dois distribuer aux développeurs pour les tests. Tous les codes source sont stockés dans GitLab sur le serveur de développement et les développeurs travaillent uniquement avec ce serveur et n'ont pas accès au serveur de production. Lorsque nous publions une application, master se connecte en production et extrait la nouvelle version de Git. Les bases de données sont volumineuses (plus de 500 millions chacune et plus) et je dois les distribuer aussi facilement que possible aux développeurs pour les tests.

Solutions possibles

  • Après un script de sauvegarde qui sauvegarde les bases de données, chacune dans un seul fichier, exécutez un script qui pousse chaque base de données dans sa propre branche. Un développeur tire l'une de ces branches s'il souhaite mettre à jour sa copie locale.

    Celui-ci a été trouvé non fonctionnel.

  • Cron sur le serveur de production enregistre chaque jour les journaux binaires et les pousse dans la branche de cette base de données. Donc, dans la branche, il y a des fichiers avec des changements quotidiens et le développeur extrait les fichiers qu'il n'a pas. Le vidage SQL actuel sera envoyé au développeur d'une autre manière. Et lorsque la taille du référentiel devient trop grande, nous enverrons un vidage complet aux développeurs et vidons toutes les données du référentiel et recommençons depuis le début.

Des questions

  • La solution est-elle possible?
  • Si git pousse/tire vers/depuis le référentiel, télécharge-t-il/télécharge-t-il des fichiers entiers, ou simplement des changements dans ceux-ci (c'est-à-dire ajoute de nouvelles lignes ou modifie les actuelles)?
  • Git peut-il gérer des fichiers aussi volumineux? Non.
  • Comment définir combien de révisions sont conservées dans un référentiel? Peu importe avec la nouvelle solution.
  • Y a-t-il une meilleure solution? Je ne veux pas forcer les développeurs à télécharger des fichiers aussi volumineux via FTP ou quelque chose de similaire.
39
Jakub Riedl

rsync pourrait être une bonne option pour mettre à jour efficacement les copies des développeurs des bases de données.

Il utilise un algorithme delta pour mettre à jour progressivement les fichiers. De cette façon, il transfère uniquement les blocs du fichier qui ont changé ou qui sont nouveaux. Bien sûr, ils devront d'abord télécharger le fichier complet, mais les mises à jour ultérieures seront plus rapides.

Essentiellement, vous obtenez une mise à jour incrémentielle similaire à une extraction git sans la copie initiale jamais étendue que le clone git donnerait. La perte n'est pas d'avoir l'historique, mais on dirait que vous n'en avez pas besoin.

rsync est une partie standard de la plupart des distributions Linux si vous en avez besoin sur Windows, un port packagé est disponible: http://itefix.no/cwrsync/

Pour envoyer les bases de données à un développeur, vous pouvez utiliser une commande similaire à:

rsync -avz path/to/database(s) Host:/folder

Ou les développeurs peuvent extraire la ou les bases de données dont ils ont besoin avec:

rsync -avz DATABASE_Host:/path/to/database(s) path/where/developer/wants/it
25
PeterSW

Mise à jour 2017:

Microsoft contribue à Microsoft/GVFS : un système de fichiers virtuel Git qui permet à Git de gérer " le plus grand repo sur la planète "
(...) à des milliers de versions de validation de demande de tirage)

GVFS virtualise le système de fichiers sous votre référentiel git afin que git et tous les outils voient ce qui semble être un référentiel normal, mais GVFS télécharge uniquement les objets selon leurs besoins.

Certaines parties de GVFS pourraient être apportées en amont (à Git lui-même).
Mais en attendant, tout nouveau développement Windows est maintenant (août 2017) sur Git .


Mise à jour d'avril 2015: GitHub propose: Annonce de Git Large File Storage (LFS)

Utilisation de git-lfs (voir git-lfs.github. com) et un serveur le supportant: lfs-test-server , vous ne pouvez stocker des métadonnées que dans le dépôt git et le gros fichier ailleurs.

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

Voir git-lfs/wiki/Tutorial :

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

Réponse originale:

Concernant quelles sont les limitations git avec les gros fichiers , vous pouvez considérer bup (présenté en détail dans GitMinutes # 24 )

Le conception de bup met en évidence les trois problèmes qui limitent un dépôt git:

  • fichiers énormes (le xdelta pour packfile est en mémoire uniquement, ce qui n'est pas bon avec des fichiers volumineux)
  • grand nombre de fichiers , ce qui signifie, un fichier par blob et lent git gc pour générer un packfile à la fois.
  • énormes fichiers de fichiers , avec un index de fichier de fichiers inefficace pour récupérer les données du fichier de fichiers (énorme).

Gestion des fichiers volumineux et xdelta

La principale raison pour laquelle git ne peut pas gérer de gros fichiers est qu'il les exécute xdelta, qui généralement signifie qu'il essaie de charger tout le contenu d'un fichier en mémoire à la fois .
S'il ne le faisait pas, il devrait stocker tout le contenu de chaque révision de chaque fichier, même si vous n'avez modifié que quelques octets de ce fichier .
Ce serait une utilisation terriblement inefficace de l'espace disque
, et git est bien connu pour son format de référentiel incroyablement efficace.

Malheureusement, xdelta fonctionne très bien pour les petits fichiers et devient incroyablement lent et gourmand en mémoire pour les gros fichiers .
Pour le but principal de git, ie. gérer votre code source, ce n'est pas un problème.

Ce que bup fait au lieu de xdelta est ce que nous appelons "hashsplitting".
Nous voulions un moyen universel de sauvegarder efficacement n'importe quel gros fichier qui pourrait changer de petites manières, sans stocker le fichier entier à chaque fois. Nous lisons le fichier un octet à la fois, calculant une somme de contrôle glissante des 128 derniers octets.

rollsum semble faire assez bien son travail. Vous pouvez le trouver dans bupsplit.c .
Fondamentalement, il convertit les 128 derniers octets lus en un entier 32 bits. Ce que nous faisons ensuite, c'est prendre les 13 bits les plus bas de la somme, et si ce sont tous des 1, nous considérons que c'est la fin d'un morceau.
Cela se produit en moyenne une fois tous les 2^13 = 8192 bytes, La taille moyenne des blocs est donc de 8192 octets.
Nous divisons ces fichiers en morceaux en fonction de la somme de contrôle glissante.
Ensuite, nous stockons chaque morceau séparément (indexé par son sha1sum) comme un blob git.

Avec le hachage, quelle que soit la quantité de données que vous ajoutez, modifiez ou supprimez au milieu du fichier, tous les morceaux avant et après le morceau affecté sont absolument pareil.
.
Comme par magie, l'algorithme de segmentation en hachage divisera votre fichier de la même manière à chaque fois, même sans savoir comment il l'avait précédemment découpé.

Le problème suivant est moins évident: après avoir stocké votre série de morceaux en tant que blobs git, comment stockez-vous leur séquence? Chaque blob a un identifiant sha1 de 20 octets, ce qui signifie que la simple liste de blobs sera 20/8192 = 0.25% De la longueur du fichier.
Pour un fichier de 200 Go, cela représente 488 Mo de données de séquence.

Nous étendons un peu plus l'algorithme de hachage en utilisant ce que nous appelons le "fanout". Au lieu de vérifier uniquement les 13 derniers bits de la somme de contrôle, nous utilisons des bits de somme de contrôle supplémentaires pour produire des divisions supplémentaires.
Ce que vous vous retrouvez avec est un véritable arbre de taches - que les objets "arbre" ​​git sont idéaux pour représenter.

Gestion d'un grand nombre de fichiers et git gc

git est conçu pour gérer des référentiels de taille raisonnable qui changent relativement rarement . Vous pourriez penser que vous changez votre code source "fréquemment" et que git gère des changements beaucoup plus fréquents que, par exemple, svn peut gérer.
Mais ce n'est pas le même genre de "fréquemment" dont nous parlons.

Le tueur n ° 1 est la façon dont il ajoute de nouveaux objets au référentiel: il crée un fichier par blob. Ensuite, vous exécutez plus tard 'git gc' et combinez ces fichiers en un seul fichier (en utilisant une compression xdelta très efficace et en ignorant tous les fichiers qui ne sont plus pertinents).

'git gc' Est lent , mais pour les référentiels de code source, le stockage super-efficace qui en résulte (et l'accès très rapide associé au stockage stocké fichiers) en vaut la peine.

bup ne fait pas ça. Il écrit juste directement les fichiers pack.
Heureusement, ces fichiers sont toujours au format git, donc git peut y accéder avec plaisir une fois qu'ils sont écrits.

Manipulation d'un énorme référentiel (ce qui signifie un grand nombre de fichiers packf énormes)

Git n'est pas réellement conçu pour gérer des référentiels super-énormes .
La plupart des référentiels git sont suffisamment petits pour qu'il soit raisonnable de les fusionner tous dans un seul packfile, ce que "git gc" Finit généralement par faire.

La partie problématique des grands fichiers packf n'est pas les fichiers packf eux-mêmes - git est conçu pour s'attendre à ce que la taille totale de tous les packs soit supérieure à la mémoire disponible, et une fois qu'il peut gérer cela, il peut gérer pratiquement n'importe quelle quantité de données de manière aussi efficace.
Le problème vient des fichiers d'index de packfile (.idx) .

chaque packfile (*.pack) dans git a un idx (*.idx) associé qui est une liste triée de hachages d'objets git et de décalages de fichiers.
Si vous recherchez un objet particulier en fonction de son sha1, vous ouvrez l'idx, recherchez-le binaire pour trouver le bon hachage, puis prenez l'offset de fichier associé, recherchez cet offset dans le packfile et lisez le contenu de l'objet.

Les performances de la recherche binaire sont de l'ordre de O(log n) avec le nombre de hachages dans le pack, avec une première étape optimisée (vous pouvez lisez-le ailleurs) qui l'améliore quelque peu en O(log(n)-7).
Malheureusement, cela se décompose un peu lorsque vous avez lots de packs .

Pour améliorer les performances de ce type d'opération, bup introduit des fichiers midx (prononcé "midix" et abréviation de "multi-idx").
Comme son nom l'indique, ils indexent plusieurs packs à la fois.

59
VonC

Vous ne voulez vraiment, vraiment, vraiment pas de gros fichiers binaires archivés dans votre référentiel Git.

Chaque mise à jour que vous ajoutez ajoutera cumulativement à la taille globale de votre référentiel, ce qui signifie que votre dépôt Git prendra de plus en plus de temps à cloner et à utiliser de plus en plus d'espace disque, car Git stocke localement l'historique complet de la branche, ce qui signifie que lorsque quelqu'un vérifie la succursale, il n'a pas seulement à télécharger la dernière version de la base de données; ils devront également télécharger toutes les versions précédentes.

Si vous devez fournir des fichiers binaires volumineux, téléchargez-les séparément sur un serveur, puis archivez un fichier texte avec une URL où le développeur peut télécharger le fichier binaire volumineux. FTP est en fait l'une des options meilleures, car il est spécifiquement conçu pour transférer des fichiers binaires, bien que HTTP soit probablement encore plus simple.

29
Amber

Vous pouvez regarder une solution comme git-annex, qui concerne la gestion de (gros) fichiers avec git, sans vérifier le fichier contenu dans git (!)
(Février 2015: n service d'hébergement comme GitLab l'intègre nativement :
Voir " GitLab prend-il en charge les fichiers volumineux via git-annex ou autrement? ")

git ne gère pas les gros fichiers, comme expliqué par Ambre dans sa réponse .

Cela ne signifie pas pour autant que git ne pourra pas faire mieux un jour.
De GitMinutes épisode 9 ( Mai 2013, voir aussi ci-dessous) , De Peff ( Jeff King) , à 36'10 '':

(transcription)

Il existe un tout autre domaine de grands référentiels où les gens sont intéressés à stocker, vous savez, 20 ou 30 ou 40 Go, parfois même des référentiels de taille TB, et oui, cela vient d'avoir beaucoup de fichiers, mais une grande partie vient d'avoir de très gros fichiers et de très gros fichiers binaires qui ne se traitent pas si bien les uns avec les autres.

C'est en quelque sorte un problème ouvert. Il y a quelques solutions: git-annex est probablement la plus mature de celles-ci, où elles ne placent essentiellement pas l'actif dans git, elles placent la grande ressource sur un serveur d'actifs et mettent un pointeur dans git.

J'aimerais faire quelque chose comme ça, où l'actif est conceptuellement en git, c'est-à-dire que le SHA1 de cet objet fait partie du SHA1 qui va dans l'arbre, qui va dans la validation ID et toutes ces choses.
Donc, du point de vue git, il fait partie du référentiel, mais à un niveau inférieur, au niveau de stockage d'objets, à un niveau inférieur au graphique historique conceptuel, où nous avons déjà avoir plusieurs façons de stocker un objet: nous avons objets lâches , nous avons objets emballés , j'aimerais peut-être avoir une nouvelle façon de stocker un objet, c'est-à-dire "nous ne l'avons pas ici, mais il est disponible par un serveur de ressources", ou quelque chose comme ça.

( Thomas Ferris Nicolaisen ) Oh cool ...

Le problème avec des choses comme git-annex c'est: une fois que vous les utilisez, vous êtes ... enfermé pour toujours dans les décisions que vous avez prises à ce moment-là. Vous savez, que si vous décidez que oh 200 Mo est gros, et que nous allons stocker sur un serveur de ressources, et ensuite, vous déciderez, aah, cela aurait dû être 300 Mo, bonne chance : c'est encodé dans votre histoire pour toujours.
Et donc en disant conceptuellement, au niveau git, cet objet est in le dépôt git, pas un pointeur vers lui, pas un peu pointeur vers un serveur de ressources, l'objet réel est là, puis s'occupe de ces détails à un bas niveau, au niveau du stockage, ce qui vous libère pour faire beaucoup de différents décisions, et même changer votre décision plus tard sur la façon dont vous voulez réellement stocker les choses sur le disque.

Pas un projet prioritaire pour l'instant ...


3 ans plus tard, en avril 2016, Git Minutes 4 comprend une interview de Michael Haggerty de GitHub = environ 31 '(Merci Christian Couder pour l'interview ).

Il est spécialisé dans le back-end de référence pendant un bon moment .
Il cite le travail de David Turner en back-end comme le plus intéressant en ce moment. (Voir Le courant de David "pluggable-backends "branche de son git/git fork )

(transcription)

Christian Couder (CD): Le but est d'avoir des git refs stockés dans une base de données, par exemple? Michael Haggerty (MH): Oui, je le vois comme deux aspects intéressants: Le premier est simplement d'avoir la possibilité de brancher différentes références d'entrée de source. Les références d'entrée sont stockées dans le système de fichiers, sous la forme d'une combinaison de références lâches et références condensées .
La référence lâche est un fichier par référence, et la référence compressée est un gros fichier contenant une liste de nombreuses références.

C'est donc un bon système, surtout pour un usage local; car il n'a pas de réel problème de performances pour les gens normaux, mais il a un problème, comme vous ne pouvez pas stocker les reflogs de références après la suppression des références, car il peut y avoir des conflits avec des références plus récentes qui ont été créées avec des similaires noms. Il y a aussi un problème où les noms de référence sont stockés sur le système de fichiers afin que vous puissiez avoir des références qui sont nommées similaires mais avec une capitalisation différente.
Ce sont donc des choses qui pourraient être corrigées en ayant différents systèmes back-end de référence en général.
Et l'autre aspect de la série de correctifs de David Turner est un changement pour stocker les références dans une base de données appelée lmdb , il s'agit d'une base de données basée sur la mémoire très rapide qui présente certains avantages en termes de performances par rapport à l'arrière-plan du fichier.

[suit d'autres considérations concernant un emballage plus rapide et une annonce de patch de référence]

25
VonC

Avoir un stockage auxiliaire de fichiers référencés à partir de votre code caché git est l'endroit où la plupart des gens vont. git-annex semble assez complet, mais de nombreux magasins utilisent simplement un référentiel FTP ou HTTP (ou S3) pour les gros fichiers, comme les vidages SQL. Ma suggestion serait de lier le code du dépôt git aux noms des fichiers dans le stockage auxiliaire en remplissant certaines des métadonnées - en particulier une somme de contrôle (probablement SHA) - dans le hachage, ainsi qu'une date.

  • Ainsi, chaque fichier aux obtient un nom de base, une date et une somme SHA (pour certaines versions n).
  • Si vous avez un roulement de fichier sauvage, utiliser uniquement un SHA pose une menace minuscule mais réelle de collision de hachage, d'où l'inclusion d'une date (heure Epoch ou date ISO).
  • Mettez le nom de fichier résultant dans le code, de sorte que le bloc aux soit inclus, très spécifiquement, par référence.
  • Structurez les noms de manière à ce qu'un petit script puisse être écrit facilement pour git grep tous les noms de fichiers auxiliaires, de sorte que la liste de tout commit soit triviale à obtenir. Cela permet également de retirer les anciens à un moment donné et peut être intégré au système de déploiement pour extraire les nouveaux fichiers aux en production sans encombrer les anciens (encore), avant d'activer le code du git repo.

Crambler d'énormes fichiers dans git (ou la plupart des repos) a un impact désagréable sur les performances de git après un certain temps - un git clone ne devrait vraiment pas prendre vingt minutes, par exemple. Alors que l'utilisation des fichiers par référence signifie que certains développeurs n'auront jamais besoin de télécharger les gros morceaux (un contraste frappant avec le git clone), car il est probable que la plupart ne concernent que le code déployé en production. Votre kilométrage peut varier, bien sûr.

1
Alex North-Keys

Les fichiers volumineux téléchargés créent parfois des problèmes et des erreurs. Cela se produit généralement. Git prend principalement en charge moins de 50 Mo de fichiers à télécharger. Pour télécharger plus de 50 Mo de fichiers dans le référentiel git, l'utilisateur doit avoir besoin d'installer un autre assistant qui coopère pour télécharger un gros fichier (.mp4, .mp3, .psd), etc.

vous connaissez certaines commandes de base de git avant de télécharger un gros fichier dans git. c'est la configuration pour le téléchargement sur github. il doit installer gitlfs.exe

l'intaller depuis lfsinstall.exe



alors vous devez utiliser les commandes de base de git avec différents

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

vous pouvez trouver que vous le trouvez lfs.https://github.com/something/repo.git/info/lfs.locksverify false comme des instructions lors de la commande Push si Push sans l'utiliser

0
Ariful Islam