Git met grote bestanden

Ik heb twee servers, Productie en Ontwikkeling. Op de productieserver zijn er twee applicaties en meerdere (6) databases (MySQL) die ik naar ontwikkelaars moet distribueren om te testen. Alle broncodes worden opgeslagen in GitLabop de ontwikkelingsserver en ontwikkelaars werken alleen met deze server en hebben geen toegang tot de productieserver . Wanneer we een applicatie vrijgeven, logt de master in op productie en haalt de nieuwe versie uit Git. De databases zijn groot (meer dan 500 miljoen per stuk) en ik moet ze zo gemakkelijk mogelijk distribueren naar ontwikkelaars om ze te testen.

Mogelijke oplossingen

  • Na een back-upscript dat databases dumpt, elk naar een enkel bestand, voer je een script uit dat elke database naar zijn eigen branch pusht. Een ontwikkelaar haalt een van deze branches eruit als hij zijn lokale kopie wil updaten.

    Deze werkt niet.

  • Cron op de productieserver slaat elke dag binaire logboeken op en duwt ze naar de tak van die database. Dus in de branch zijn er bestanden met dagelijkse veranderingen en de ontwikkelaar haalt de bestanden op die hij niet heeft. De huidige SQL-dump wordt op een andere manier naar de ontwikkelaar verzonden. En wanneer de omvang van de repository te groot wordt, sturen we een volledige dump naar de ontwikkelaars en spoelen alle gegevens in de repository door en beginnen vanaf het begin.

Vragen

  • Is de oplossing mogelijk?
  • Als git naar/van de repository pusht/trekt, uploadt/downloadt het dan hele bestanden, of verandert het er alleen maar in (d.w.z. voegt nieuwe regels toe of bewerkt de huidige regels)?
  • Kan Git zulke grote bestanden beheren?Nee.
  • Hoe stel je in hoeveel revisies in een repository worden bewaard?Maakt niet uit met de nieuwe oplossing.
  • Is er een betere oplossing? Ik wil de ontwikkelaars niet dwingen zulke grote bestanden te downloaden via FTP of iets dergelijks.

Antwoord 1, autoriteit 100%

Update 2017:

Microsoft draagt ​​bij aan Microsoft/GVFS: een virtueel Git-bestand Systeem waarmee Git “de grootste repo ter wereld
(dwz: de Windows-codebasis, die uit ongeveer 3,5 miljoen bestanden bestaat en, wanneer ingecheckt in een Git-repo, resulteert in een repo van ongeveer 300 GB, en produceert 1760 dagelijkse “lab-builds” over 440 branches naast duizenden pull-verzoeken validatie builds)

GVFS virtualiseert het bestandssysteem onder je git repo zodat git en alle tools zien wat een normale repo lijkt te zijn, maar GVFS downloadt alleen objecten als ze nodig zijn.

Sommige delen van GVFS kunnen stroomopwaarts worden bijgedragen (naar Git zelf).
Maar in de tussentijd is alle nieuwe Windows-ontwikkeling nu (augustus 2017) op Git.


Update april 2015: GitHub stelt voor: Aankondiging van Git Large File Opslag (LFS)

git-lfsgebruiken (zie git-lfs.github.com) en een server die dit ondersteunt: lfs-test-server, je kunt metadata alleen opslaan in de git repo , en het grote bestand elders. Maximaal 2 Gb per vastlegging.

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

Zie git-lfs/wiki/Tutorial:

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

Oorspronkelijk antwoord:

Wat betreft wat de git-beperkingen zijn met grote bestanden, kun je overwegen bup(gepresenteerd in details in GitMinutes #24)

Het ontwerp van bupbenadrukt de drie problemen die een git repo beperken:

  • enorme bestanden(de xdelta voor packfilebevindt zich alleen in het geheugen, wat niet goed met grote bestanden)
  • groot aantal bestanden, wat betekent, één bestand per blob, en langzame git gcom één pakketbestand per keer te genereren.
  • enorme packfiles, met een packfile-index die inefficiënt is om gegevens uit de (enorme) packfile op te halen.

Omgaan met grote bestanden en xdelta

De belangrijkste reden waarom git grote bestanden niet aankan, is dat het ze door xdeltavoert , wat over het algemeen betekent het probeert de volledige inhoud van een bestand in één keer in het geheugen te laden.
Als het dit niet deed, zou het de volledige inhoud van elke afzonderlijke revisie van elk afzonderlijk bestand moeten opslaan, zelfs als je maar een paar bytes van dat bestand hebt gewijzigd.
Dat zou een vreselijk inefficiënt gebruik van schijfruimte zijn
, en git staat bekend om zijn verbazingwekkend efficiënte repository-indeling.

Helaas werkt xdeltageweldig voor kleine bestanden en wordt het verbazingwekkend traag en geheugenintensief voor grote bestanden.
Voor het hoofddoel van git, dwz. het beheren van uw broncode, is dit geen probleem.

Wat bup doet in plaats van xdelta is wat we “hashsplitting” noemen.
We wilden een algemene manier om efficiënt een back-up te maken van elkgroot bestand dat op kleine manieren zou kunnen veranderen, zonder het hele bestand telkens op te slaan.
We lezen het bestand één byte per keer door en berekenen een voortschrijdende controlesom van de laatste 128
bytes.

rollsumlijkt zijn werk redelijk goed te doen. Je kunt het vinden in bupsplit.c.
Kortom, het converteert de laatste 128 bytes die zijn gelezen naar een 32-bits geheel getal. Wat we dan doen, is de laagste 13 bits van de rollsum nemen, en als het allemaal enen zijn, beschouwen we dat als het einde van een stuk.
Dit gebeurt gemiddeld eens per 2^13 = 8192 bytes, dus de gemiddelde chunkgrootte is 8192 bytes.
We verdelen die bestanden in stukken op basis van de voortschrijdende controlesom.
Vervolgens slaan we elke chunk afzonderlijk op (geïndexeerd door zijn sha1sum) als een git-blob.

Met hashsplitting, ongeacht hoeveel gegevens u toevoegt, wijzigt of verwijdert in het midden van het bestand, zijn alle chunks vooren nade betreffende chunk absoluut hetzelfde.
Het enige dat van belang is voor het hashsplitting-algoritme is de 32-byte “separator”-reeks, en een enkele wijziging kan maximaal één separatorreeks of de bytes tussen twee separatorreeksen beïnvloeden.
Net als bij toverslag, zal het hashsplit chunking-algoritme je bestand elke keer op dezelfde manier in stukjes hakken, zelfs zonder te weten hoe het het eerder had gechunkt.

Het volgende probleem ligt minder voor de hand: nadat je je reeks chunks als git-blobs hebt opgeslagen, hoe sla je hun reeks op? Elke blob heeft een sha1-identificatiecode van 20 bytes, wat betekent dat de eenvoudige lijst met blobs 20/8192 = 0.25%van de bestandslengte zal zijn.
Voor een bestand van 200 GB is dat 488 meg aan alleen sequentiegegevens.

We breiden het hashsplit-algoritme iets verder uit met wat we ‘fanout’ noemen. In plaats van alleen de laatste 13 bits van de controlesom te controleren, gebruiken we extra controlesombits om extra splitsingen te produceren.
Wat je uiteindelijk krijgt, is een echte boom van blobs – welke git ‘tree’-objecten ideaal zijn om te vertegenwoordigen.

Omgaan met grote aantallen bestanden en git gc

git is ontworpen voor het verwerken van opslagplaatsen van redelijk formaat die relatief zelden veranderen. Je zou kunnen denken dat je je broncode “vaak” wijzigt en dat git veel frequentere wijzigingen afhandelt dan, laten we zeggen, svnaankan.
Maar dat is niet hetzelfde soort “vaak” waar we het over hebben.

De grootste moordenaar is de manier waarop het nieuwe objecten aan de repository toevoegt: het maakt één bestand per blob aan. Dan voer je later ‘git gc’ uit en combineer je die bestanden in een enkel bestand(met behulp van zeer efficiënte xdelta-compressie en negeer je alle bestanden die niet langer relevant zijn).

git gc‘ is traag, maar voor broncoderepositories is de resulterende superefficiënte opslag (en bijbehorende zeer snelle toegang tot de opgeslagen bestanden) de moeite waard .

bupdoet dat niet. Het schrijft packfiles gewoon rechtstreeks.
Gelukkig zijn deze packfiles nog steeds git-geformatteerd, dus git heeft er graag een keer toegang toe
ze zijn geschreven.

Omgaan met enorme repository (dat wil zeggen enorme aantallen enorme packfiles)

Git is eigenlijk niet ontworpen om supergrote opslagplaatsen aan te kunnen.
De meeste git-repository’s zijn klein genoeg om ze allemaal samen te voegen tot een enkel pakketbestand, wat ‘git gc‘ meestal uiteindelijk doet.

Het problematische deel van grote pakbestanden zijn niet de pakbestanden zelf – git is ontworpen om te verwachten dat de totale grootte van alle pakketten groter is dan het beschikbare geheugen, en als het dat eenmaal aankan, kan het vrijwel elke hoeveelheid gegevens over even efficiënt.
Het probleem zijn de packfile-indexen (.idx) bestanden.

elk pakketbestand (*.pack) in git heeft een bijbehorende idx(*.idx) die een gesorteerde lijst is van git-object-hashes en bestandsverschuivingen.
Als je op zoek bent naar een bepaald object op basis van zijn sha1, open je de idx, doorzoek je het binair om de juiste hash te vinden, neem dan de bijbehorende bestandsoffset, zoek naar die offset in het packbestand en lees de inhoud van het object.

De prestatie van de binaire zoekopdrachtgaat over O(log n)met het aantal hashes in het pakket, met een geoptimaliseerde eerste stap (u kunt erover lezen elders) die het enigszins verbetert tot O(log(n)-7).
Helaas gaat dit een beetje kapot als je veelpakkettenhebt.

Om de prestaties van dit soort bewerkingen te verbeteren, introduceert bup midx(uitgesproken als “midix” en een afkorting voor “multi-idx”) bestanden.
Zoals de naam al aangeeft, indexeren ze meerdere pakketten tegelijk.


Antwoord 2, autoriteit 48%

Je wilt echt, echt, echt niet dat grote binaire bestanden worden ingecheckt in je Git-repository.

Elke update die je toevoegt, zal cumulatief bijdragen aan de totale grootte van je repository, wat betekent dat je Git-repo later steeds langer nodig zal hebben om te klonen en steeds meer schijfruimte in beslag neemt, omdat Git de hele geschiedenis van de branch lokaal, wat betekent dat wanneer iemand de branch uitcheckt, ze niet alleen de laatste versie van de database hoeven te downloaden; ze zullen ook elke vorige versie moeten downloaden.

Als je grote binaire bestanden moet aanleveren, upload ze dan apart naar een server en check dan een tekstbestand in met een URL waar de ontwikkelaar het grote binaire bestand kan downloaden. FTP is eigenlijk een van de betereopties, omdat het specifiek is ontworpen voor het overbrengen van binaire bestanden, hoewel HTTP waarschijnlijk nog eenvoudiger is.


Antwoord 3, autoriteit 45%

rsynczou een goede optie kunnen zijn om de ontwikkelaarskopieën van de databases efficiënt bij te werken.

Het gebruikt een delta-algoritme om de bestanden stapsgewijs bij te werken. Op die manier worden alleen de blokken van het bestand overgedragen die zijn gewijzigd of die nieuw zijn. Ze zullen natuurlijk nog steeds eerst het volledige bestand moeten downloaden, maar latere updates zouden sneller zijn.

In wezen krijg je een vergelijkbare incrementele update als een git fetch zonder de steeds groter wordende initiële kopie die de git-kloon zou geven. Het verlies is niet dat je de geschiedenis hebt, maar het klinkt alsof je dat niet nodig hebt.

rsync is een standaardonderdeel van de meeste linux-distributies als je het nodig hebt op Windows, er is een verpakte poort beschikbaar: http:/ /itefix.no/cwrsync/

Om de databases naar een ontwikkelaar te pushen, kunt u een opdracht gebruiken die lijkt op:

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

Of de ontwikkelaars kunnen de database(s) die ze nodig hebben ophalen met:

rsync -avz DATABASE_HOST:/path/to/database(s) path/where/developer/wants/it

Antwoord 4, autoriteit 39%

Je kunt kijken naar een oplossing zoals git-annex, dat is over het beheren van (grote) bestanden met git, zonder de bestandsinhoud in git(!)
. te controleren
(Feb 2015: een service-hosting zoals GitLab integreert het native:
Zie “Ondersteunt GitLab grote bestanden via git-annexof anderszins?“)

git beheert geen grote bestanden, zoals uitgelegd door Amberin haar antwoord.

Dat betekent echter niet dat Git het op een dag niet beter zal kunnen doen.
Van GitMinutes aflevering 9( Mei 2013, zie ook hieronder), Van Peff (Jeff King), op 36 ’10”:

(transcriptie)

Er is een heel ander rijk van grote opslagplaatsen waar mensen geïnteresseerd zijn in het opslaan van, weet je, 20 of 30 of 40 GB, soms zelfs opslagplaatsen van TB-formaat, en ja, het komt door het hebben van veel bestanden, maar veel daarvan komt van het hebben van echt grote bestanden en echt grote binaire bestanden die niet zo goed met elkaar omgaan.

Dat is een soort open probleem. Er zijn een paar oplossingen: git-annex is waarschijnlijk de meest volwassen daarvan, waarbij ze de activa in principe niet in git stoppen, ze de grote activa op een activaserver plaatsen en een pointerplaatsen in git.

Ik zou zoiets willen doen, waarbij de asset conceptueelin git is, dat wil zeggen dat de SHA1 van dat object deel uitmaakt van de SHA1 die in de boom gaat, die in de commit ID en al die dingen.
Dus vanuit git-perspectief maakt het deel uit van de repository, maar op een niveau lager, op objectopslagniveau, op een niveau onder de conceptuelegeschiedenisgrafiek, waar we al meerdere manieren hebben om een ​​object op te slaan : we hebben losse voorwerpen, we hebben verpakte objecten, ik zou graag een nieuwe manier willen hebben om een ​​object op te slaan, dat wil zeggen “we hebben het hier niet, maar het is beschikbaar door een activaserver”, of iets dergelijks .

(Thomas Ferris Nicolaisen) Oh cool…

Het probleem met dingen als git-annexis: als je ze eenmaal gebruikt, zit je… voor altijd vast aan de beslissingen die je toen nam. Weet je, als je besluit oh 200 MB is groot, en we gaan het opslaan op een activaserver, en dan, later besluit je, aah, het had 300 MBmoeten zijn, nou, pech: dat is voor altijd gecodeerd in je geschiedenis.
En dus door conceptueelte zeggen, op het git-niveau, bevindt dit object zich inde git-repository, niet een of andere verwijzing ernaar, niet een of andere verwijzing naar een activaserver, de feitelijkobject is er, en dan zorg je voor die details op een laag niveau, op het opslagniveau, dan heb je de tijd om veel verschillende beslissingen te nemen, en zelfs veranderinglater uw beslissing over hoe u de spullen op schijf wilt opslaan.

Voorlopig geen project met hoge prioriteit…


3 jaar later, in april 2016, Git Minuten 40bevat een interview van Michael Haggertyvan GitHubrond 31′ (bedankt jij Christian Coudervoor het interview).

Hij is gespecialiseerd in referentie-backend al een tijdje.
Hij citeert David Turner‘s werkop back-end als het meest interessant op dit moment. (Zie David’s huidige “pluggable-backends” branch van zijn git/git fork)

(transcriptie)

Christian Couder (CD): Het doel is om bijvoorbeeld git-refs in een database op te slaan?
Michael Haggerty (MH): Ja, ik zie het als twee interessante aspecten: de eerste is simpelweg de mogelijkheid om verschillende bronvermeldingen in te pluggen. Invoerreferenties worden opgeslagen in het bestandssysteem, als een combinatie van losse referenties en verpakte referenties.
Losse referentie is één bestand per referentie, en ingepakte referentie is één groot bestand met een lijst van vele vele referenties.

Dus dat is een goed systeem, vooral voor lokaal gebruik; omdat het geen echt prestatieprobleem heeft voor normale mensen, maar het heeft wel een probleem, zoals dat je referenties reflogs niet kunt opslaan nadat de referenties zijn verwijderd, omdat er conflicten kunnen zijn met nieuwere referenties die zijn gemaakt met soortgelijke namen. Er is ook een probleem waarbij referentienamen worden opgeslagen op het bestandssysteem, zodat u referenties kunt hebben met dezelfde naam, maar met een ander hoofdlettergebruik.
Dat zijn dus dingen die kunnen worden opgelost door in het algemeen een ander referentie-back-endsysteem te hebben.
En het andere aspect van de patchreeks van David Turner is een wijziging om referenties op te slaan in een database genaamd lmdb, dit is een echt snelle, op geheugen gebaseerde database die enkele prestatievoordelen heeft ten opzichte van de bestandsback-end.

[volgt andere overwegingen met betrekking tot sneller inpakken en referentie-patchadvertentie]


Antwoord 5, autoriteit 3%

Het hebben van een hulpopslag van bestanden waarnaar wordt verwezen vanuit je git-stashed code is waar de meeste mensen naartoe gaan. git-annexziet er behoorlijk uitgebreid uit, maar veel winkels gebruiken gewoon een FTP- of HTTP (of S3)-repository voor de grote bestanden, zoals SQL-dumps. Mijn suggestie zou zijn om de code in de git repo te koppelen aan de namen van de bestanden in de hulpopslag door een deel van de metadata – met name een checksum (waarschijnlijk SHA) – in de hash te proppen, evenals een datum.

  • Dus elk aux-bestand krijgt een basisnaam, datum en SHA (voor sommige versie n) som.
  • Als je een wild bestand hebt, vormt het gebruik van alleen een SHA een kleine maar reële dreiging van een hash-botsing, vandaar de toevoeging van een datum (epoch-tijd of ISO-datum).
  • Plaats de resulterende bestandsnaam in de code, zodat de aux chunk wordt opgenomen, heel specifiek, als referentie.
  • Structureer de namen op zo’n manier dat een klein script gemakkelijk kan worden geschreven om alle aux-bestandsnamen te git grep, zodat de lijst voor elke commit triviaal is om te verkrijgen. Dit maakt het ook mogelijk de oude op een bepaald moment buiten gebruik te stellen, en kan worden geïntegreerd met het implementatiesysteem om de nieuwe aux-bestanden naar productie te trekken zonder de oude (nog) af te kraken, voordat de code uit de git-repo wordt geactiveerd.

Het proppen van enorme bestanden in git (of de meeste repo’s) heeft na een tijdje een vervelende invloed op de prestaties van git – een git clonezou bijvoorbeeld echt geen twintig minuten moeten duren. Terwijl het gebruik van de bestanden door middel van referentie betekent dat sommige ontwikkelaars de grote stukken nooit hoeven te downloaden (een scherp contrast met de git clone), aangezien de kans groot is dat de meeste alleen relevant zijn voor de geïmplementeerde code in de maak. Uw kilometerstand kan natuurlijk variëren.


Antwoord 6

Het uploaden van grote bestanden leidt soms tot problemen en fouten. Dit gebeurt meestal. Git ondersteunt voornamelijk een bestand van minder dan 50 MB om te uploaden. Voor het uploaden van meer dan 50 MB bestanden in de git-repository moet de gebruiker een andere assistent installeren die samenwerkt om grote bestanden (.mp4,.mp3,.psd) enz. te uploaden.

er zijn enkele basis git-commando’s die je kent voordat je een groot bestand uploadt in git. dit is de configuratie voor uploaden op github. het moet gitlfs.exe

. installeren
installeer het van lfsinstall.exe

dan zou je de basiscommando’s van git moeten gebruiken samen met een aantal andere

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

misschien vind je het lfs.https://github.com/something/repo.git/info/lfs.locksverify false-achtige instructies tijdens het push-commando als push zonder het te gebruiken


Antwoord 7

Zoals vermeld in veel andere antwoorden, wordt het ten zeerste afgeraden om grote bestanden in git op te slaan. Ik zal hier niet meer op terugkomen.

Uw vragen lijken meer op een vraag over databasepersistentie dan op git. Als de database-info niet zo veel is, dan

  1. Voor Java kun je flywaydb(java) gebruiken om de diff van de database tussen elke release op te slaan.
  2. Voor Django kan het db-info opslaan in json dump (python manage.py dumpdata your_app > datadump.json) en ergens anders opnieuw laden (python manage.py loaddata datadump.json)

Aangezien uw DB echter groot is, moet u rekening houden met populaire binaire winkels zoals nexusof artifactorydie binaire bestanden kunnen opslaanof wordt gebruikt als winkel voor gitlfs. Om vervolgens de last van ontwikkelaars te verlichten, omdat u niet wilt dat ze het bestand expliciet downloaden, moet u uw eigen CI/CD-pijplijn bouwen waarmee ontwikkelaars het met één klik kunnen publiceren.

Other episodes