Git Push neue Zweigstelle mit gleichen Dateien, lädt alle Dateien erneut

8

Gegebenes folgendes Szenario.

  1. Neuen Zweig erstellen
  2. Übergeben Sie eine 10-MB-Datei
  3. Git push (lädt die 10MB-Datei hoch)
  4. Erstellen Sie einen neuen Zweig (Waise)
  5. Commit die gleiche 10mb-Datei (keine Änderungen gemacht, das gleiche Objekt sha Hash)
  6. Git push lädt die 10MB-Datei AGAIN
  7. hoch

Meine Erwartungen sind, dass die bereits hochgeladenen Dateien nicht erneut mit git push hochgeladen werden. Aber was wirklich passiert, ist, dass wenn eine neue Verzweigung gemacht wird, alle Dateien (auch wenn Tausende kleinerer Quelldateien statt einer 10MB Datei) immer wieder hochgeladen werden.

Meine Frage: Wie kann ich erreichen, dass Git erkennt, dass die 10-MB-Datei bereits hochgeladen wurde? Kennen Sie eine Problemumgehung, damit Git bereits vorhandene Objekte auf dem Server erkennt, wenn Sie Commits durchführen? Git erkennt Dateien anhand seines sha. Es sollte also erkennen können, dass einige Dateien in der Baumstruktur des Commits bereits auf dem Server vorhanden sind.

Möglicher Anwendungsfall: Ich habe zwei völlig verschiedene Zweige, aber einige gemeinsame Dateien werden innerhalb dieser beiden geteilt. Wenn ich einen Zweig drücke, möchte ich die gemeinsamen Dateien nicht erneut hochladen, wenn ich den zweiten Zweig drücke.

Tatsächlicher Anwendungsfall: Ich mache viele maschinelle Lernexperimente mit Python-Skripten und einigen kleineren Datensätzen (1MB - 10MB). Jedes Mal, wenn ich ein Experiment starte, füge ich alle notwendigen Experimentdateien zu einem neuen Git-Baum hinzu und verwende diesen Baum in einem neuen Commit ohne Verzweigung. Dieses Commit hängt völlig frei in der Luft und wird dann mit einer neuen Git-Referenz referenziert (z.B. refs / jobs / my-experiment-name). Wenn ich jetzt zwei Experimente mit fast den gleichen Dateien (und somit zwei Referenzen) habe, schiebt Git alle Objekte erneut, wenn ich diese Referenzen verschiebe. Ich habe eine geringe Bandbreite und das verlangsamt meine Arbeit wirklich.

%Vor%

Auf der technischen Seite haben wir:

  1. Zwei Commits, die nicht die gleichen Eltern teilen (haben eine komplett andere Geschichte)
  2. Diese beiden Commits haben genau die gleiche Tree-Sha-ID (und verweisen somit auf die gleichen Objektdateien)
  3. Wenn beide Commits gedrückt werden, werden alle Objekte in derselben Baumstruktur zweimal übertragen. Obwohl ich entweder erwarte, dass Git erkennt, dass der Baum im zweiten Commit bereits vorhanden ist oder dass Dateiobjekte in diesem Baum bereits auf dem Server sind.

Antwort (Ich kann nicht mehr antworten, da jemand dies als Duplikat markiert hat).

Die Lösung ist leider nicht so einfach.

Jedes Mal, wenn Git zwei Repositories synchronisieren möchte, erstellt es eine Paketdatei, die alle notwendigen Objekte (wie Dateien, Commits, Bäume) enthält. Wenn Sie git push ausführen, sendet die Gegenstelle alle vorhandenen Referenzen (Zweige), und ihr Kopf sendet SHA an den Client. Dies ist das Problem: Das Paketprotokoll ist nicht zur Verwendung gedacht per-Objekt, aber per-commit. Entsprechend dem Protokoll selbst ist das oben erläuterte Verhalten korrekt. Um das zu umgehen, habe ich ein einfaches Skript erstellt, mit dem jeder git push basierend auf Objekten anstelle von Commits erstellen kann.

Sie finden es hier: Ссылка

Was es macht:

  1. Nimmt ein Commit (nur eins, Sie müssen es auch bei jedem nicht synchronisierten Eltern-Commit ausführen) und erstellt eine Liste von Objekt-SHAs (Dateien, Bäume, Commits), die zu diesem Commit gehören (außer Eltern-Commit).
  2. Sendet diese Liste an den Server, Server antwortet zurück SHAs von Objekten, die es noch nicht hat
  3. Der Client erstellt eine Paketdatei basierend auf den SHAs des fehlenden Objekts und sendet sie an den Server mit der Information, welche ref auf welches commit aktualisiert werden muss.
  4. Der Server empfängt die Pack-Datei, entpackt sie und aktualisiert die Ref mit dem übergebenen SHA.

Natürlich hat das einige Nachteile, aber ich habe sie im verlinkten Github-Repository beschrieben.

Mit meinem obigen Skript bekommen Sie jetzt folgendes:

%Vor%

Wie Sie sehen, wird nur ein Objekt (das Commit-Objekt) und nicht die dummy -Datei und ihr Baumobjekt erneut synchronisiert.

    
Marc J. Schmidt 12.01.2018, 14:37
quelle

5 Antworten

1

Git entwickelt seine Manifeste und verpackt Objekte mit nur einem Austausch von Referenzen. Wenn der Zweig, den du schiebst, keine gemeinsamen Vorfahren mit der Fernbedienung hat, wird er alle Objekte, die von diesem Zweig erreichbar sind, packen und erneut hochladen.

Die Sequenz läuft ungefähr so ​​ab

  • Sie: senden Sie eine Liste Ihrer aktuellen Remote-Refreshs an Remote
  • Remote: sendet alle nicht erreichbaren Objekte aus dieser Liste an den Client und eine Liste neuer Referenzen
  • Sie: Fügen Sie Objekte zu Ihrer lokalen Datenbank hinzu und aktualisieren Sie Remote-Referenzen
  • Sie: Finden Sie alle Commits in Ihrem Zweig, die nicht von einem Remote-Verweis aus erreichbar sind (Kein gemeinsamer Vorgänger, alle Commits auf dem Zweig werden gesendet)
  • Sie: Erstellen Sie ein Manifest aus den Diffs für diese Commits (findet 10 MB-Datei)
  • Sie: packen und senden an Remote

Die Fernbedienung erkennt bereits in ihrer Datenbank vorhandene Objekte und verwendet die vorhandenen Objekte.

Die Logik besteht darin, dass das Senden von ein paar unnötigen Dateien von Zeit zu Zeit weitaus effizienter ist als das Durchlaufen des gesamten Verlaufs (möglicherweise Zehntausende oder Hunderttausende von Objekten, auf die zugegriffen und verglichen wird) bei jedem Push.

    
LightBender 12.01.2018 15:22
quelle
1

Ich denke, Sie müssen nicht mehr --orphan verwenden, um neue Experiment-Zweige zu erstellen.

Workflow

  1. Erstellen Sie Ihr erstes Projekt.
  2. Fügen Sie Ihre zentralen Dateien dem Hauptzweig
  3. hinzu
  4. Erstellen Sie alle nicht-verwaisten Zweige, die Sie für jedes Experiment benötigen. Erstellen Sie sie basierend auf dem Master-Zweig.

Das ist es.

Was ist los?

Sie haben darauf bestanden, dass Sie keine Zweige verwenden und dass Sie nur Referenzen verwenden. Branchen sind jedoch eine Art Referenz . Darüber hinaus erstellt git checkout --orphan <newthing> tatsächlich einen Zweig . Das Problem ist, dass es ein Zweig ist, der nichts weiß, was zuvor dem Repository hinzugefügt wurde, weil es keine Eltern hat. Es ist im Wesentlichen das Gleiche, als hätte man ein ganz neues Repository erstellt.

Wenn Sie neue Zweige mit git checkout -b <newthing> master erstellen, stört git nicht die hochgeladenen Dateien, die bereits in master waren.

Wie verwalten Sie jetzt neue gemeinsame Dateien?

Nehmen wir an, Sie haben eines Tages eine neue Datei, die alle zukünftigen Experimente nutzen sollen - eine neue gemeinsame / gemeinsame Datei. Alles, was Sie tun müssen, ist, diese Datei zu master hinzuzufügen und Ihren nächsten Experimentzweig basierend auf dem aktualisierten Masterzweig zu erstellen. Wenn diese Datei für Ihre vorhandenen / zuvor erstellten Tests verfügbar sein soll, müssen Sie nur diese Zweige auschecken und git pull --rebase origin master ausführen. Dies würde die Commits, die Sie dem Master hinzugefügt haben, einziehen, die die neu hinzugefügten Dateien enthalten würden.

Montagekomplexität

Wenn Sie mit dem Ziehen beginnen, werden die Dinge vielleicht kompliziert. Es gibt ein paar verschiedene Strategien zum Aktualisieren von Zweigen, und die Verwendung von --rebase ist eine dieser Strategien. Es ist nicht erforderlich, aber es ist wahrscheinlich der bessere Weg. Es gibt noch weitere Dinge, die zu berücksichtigen sind, z. B. die Behandlung von widersprüchlichen Änderungen, die jedoch außerhalb des Bereichs dieser Frage liegen. Es gibt genügend Ressourcen, um das Rebasing / Merging etc. zu erklären.

TR; DR

Versuchen Sie nicht, Commit-Bäume und Eltern / Kind-Beziehungen manuell zu verwalten. Lass einfach git sein Ding machen.

    
eddiemoya 17.01.2018 17:27
quelle
0
  

Tatsächlicher Anwendungsfall: Ich mache viele maschinelle Lernexperimente mit Python-Skripten und einigen kleineren Datensätzen (1MB - 10MB). Jedes Mal, wenn ich ein Experiment starte, füge ich alle notwendigen Experimentdateien zu einer neuen Git-Struktur hinzu

Das scheint weniger ein git-bezogenes Problem zu sein und mehr ein Content-Management-Problem: Wenn Sie einen gemeinsamen Satz statischer Dateien haben, die Sie von einem Git-Repo-Zweig in einen anderen Zweig wiederverwenden wollen, der Satz von Dateien (der sich nie ändert) muss nicht unbedingt im Git Repo sein.

Sie könnten in Erwägung ziehen, es in einem statischen Pfad auf einem Server hochzuladen und sicherzustellen, dass Ihr ML-Skript (versioniert in Ihrem Git-Repository) seinen Lernprozess startet, indem es diesen anfänglichen Datensatz aus dem statischen Pfad holt und kopiert Oder sie könnten in ihrem eigenen Git Repo sein, und Ihr Skript würde damit beginnen, den Unterordner, der diesen Daten gewidmet ist, durch Klonen / Ziehen von diesem externen Repo zu klonen / aktualisieren.

Auf diese Weise umgehen Sie das Versionskontrollproblem und nur die Version (in Ihrem Multi-Branch Repo, die Ihren Experimenten gewidmet ist), was tatsächlich von Commit zu Commit wechseln kann: Ihre ML-Skripte und möglicherweise einige spezifische (und kleinere) ) Datensätze, zugeschnitten auf neue Experimente.

    
VonC 14.01.2018 21:17
quelle
0

Es könnte besser sein, Git LFS in Ihrem Szenario zu verwenden. Git LFS wurde für die Verwaltung großer Dateien in Git-Repositories entwickelt.

Gemäß diesem GitHub-Problem (mit wurde aufgelöst ~ vor 2 Jahren, doppelte Dateien werden als eine einzelne Entität mit derselben OID verwaltet. So werden sie nur einmal gedrückt und nur einmal abgerufen.

Mindestens Github und Gitlab enthalten bereits Unterstützung für git-lfs in allen Repositories.

    
Gonzalo Matheu 14.01.2018 21:53
quelle
0

Wie andere bereits erwähnt haben; Git sucht nur nach Blobs in dem Zweig, den du schiebst, aber du kannst Git dazu bringen, nach Blobs im Master-Zweig zu suchen, indem du es in deine Vorfahren einfügst.

Es scheint, dass Sie wirklich an einem verwaisten Zweig arbeiten möchten, so dass Sie den Master-Zweig nur zusammenführen können, wenn Sie pushen möchten. Sie können den gesamten Inhalt des Master-Zweiges ignorieren, indem Sie die ours -Strategie verwenden.

%Vor%

Der Inhalt ist genau der gleiche:

%Vor%

Wenn Sie sich nicht mit Ihrem ursprünglichen Zweig anlegen möchten, können Sie einen neuen Zweig nur für das Drücken erstellen.

Außerdem kannst du einfach mit dem Master anfangen und einfach alle Dateien wegwerfen:

%Vor%

Wenn Sie wirklich möchten, dass dieser recht spezifische Anwendungsfall in Git richtig gehandhabt wird, können Sie die Entwickler auf der Mailingliste kontaktieren . Sie könnten Ihnen andere Alternativen anbieten, aber es ist möglich, dass sie nicht zustimmen, dass es etwas gibt, das im Code verbessert werden kann, ohne dass es in anderen Fällen zu erheblichen Kompromissen kommt.

Hinweis : Ich weiß nicht, warum Sie die Dummy-Datei in Ihren Schritten hinzufügen mussten, auf meiner Seite wird der gesamte Inhalt des Master-Zweigs inszeniert, wenn ich git checkout --orphan mache.

    
FelipeC 18.01.2018 02:04
quelle

Tags und Links