Reduzieren des Verlaufs eines Git-Repositorys

83

Wir haben ein Git-Projekt, das eine ziemlich große Geschichte hat.

Insbesondere zu Beginn des Projekts befanden sich viele binäre Ressourcendateien im Projekt. Diese wurden jetzt entfernt, da es sich tatsächlich um externe Ressourcen handelt.

Die Größe unseres Repositorys beträgt jedoch> 200 MB (der gesamte Checkout beträgt derzeit ~ 20 MB), da diese Dateien zuvor festgeschrieben wurden.

Wir möchten den Verlauf "reduzieren", sodass das Repository anscheinend aus einer späteren Revision erstellt wurde als es war. Beispielsweise

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Repository erstellt
  2. Große Menge von Binärdateien hinzugefügt
  3. Große Menge von Binärdateien entfernt
  4. Neuer vorgesehener 'Start' des Repositorys

Wir möchten also effektiv die Projekthistorie vor einem bestimmten Punkt verlieren. Zu diesem Zeitpunkt gibt es nur einen Zweig, sodass es nicht kompliziert ist, mit mehreren Startpunkten usw. umzugehen. Wir möchten jedoch nicht den gesamten Verlauf verlieren und ein neues Repository mit der aktuellen Version starten.

Ist das möglich oder sind wir dazu verdammt, für immer ein aufgeblähtes Repository zu haben?

Gareth
quelle

Antworten:

88

Sie können das binäre Aufblähen entfernen und den Rest Ihres Verlaufs behalten. Mit Git können Sie frühere Commits neu anordnen und "quetschen", sodass Sie nur die Commits kombinieren können, die Ihre großen Binärdateien hinzufügen und entfernen. Wenn die Hinzufügungen alle in einem Commit und die Entfernungen in einem anderen durchgeführt wurden, ist dies viel einfacher als der Umgang mit jeder Datei.

$ git log --stat       # list all commits and commit messages 

Suchen Sie hier nach den Commits, die Ihre Binärdateien hinzufügen und löschen, und notieren Sie sich deren SHA1s, z . B. 2bcdefund 3cdef3.

Verwenden Sie dann zum Bearbeiten des Repo-Verlaufs den rebase -iBefehl mit seiner interaktiven Option, beginnend mit dem übergeordneten Element des Commits, in dem Sie Ihre Binärdateien hinzugefügt haben. Es wird Ihr $ EDITOR gestartet und Sie sehen eine Liste der Commits, beginnend mit 2bcdef:

$ git rebase -i 2bcdef^    # generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
#
pick 2bcdef   Add binary files and other edits
pick xxxxxx   Another change
  .
  .
pick 3cdef3   Remove binary files; link to them as external resources
  .
  .

squash 3cdef3Als zweite Zeile einfügen und die Zeile mit der Aufschrift pick 3cdef3aus der Liste entfernen . Sie haben jetzt eine Liste von Aktionen für das Interaktive rebase, die die Commits, die Ihre Binärdateien hinzufügen und löschen, zu einem Commit zusammenfassen, dessen Unterschied nur alle anderen Änderungen in diesen Commits sind. Dann werden alle nachfolgenden Commits der Reihe nach erneut angewendet, wenn Sie anweisen, dass sie abgeschlossen werden sollen:

$ git rebase --continue

Dies dauert ein oder zwei Minuten.
Sie haben jetzt ein Repo, in dem die Binärdateien nicht mehr kommen oder gehen. Sie beanspruchen jedoch weiterhin Speicherplatz, da Git Änderungen standardmäßig 30 Tage lang aufbewahrt, bevor sie mit Müll gesammelt werden können, sodass Sie Ihre Meinung ändern können. Wenn Sie sie jetzt entfernen möchten:

$ git reflog expire --expire=1.minute refs/heads/master
      #all deletions up to 1 minute  ago available to be garbage-collected
$ git fsck --unreachable      # lists all the blobs(files) that will be garbage-collected
$ git prune
$ git gc                      

Jetzt haben Sie das Aufblähen entfernt, aber den Rest Ihrer Geschichte beibehalten.

Paul
quelle
7
Sie müssen sich nur daran erinnern, dass das Umschreiben des Verlaufs den Zugriff verwirrt, wenn andere bereits aus diesem Repository gezogen haben. Das Handbuch zu git-rebase erklärt, wie diese anderen Repos wiederhergestellt werden. kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto
Dies ist eine großartige Antwort für das spezifische Problem des Benutzers, aber nicht für die eigentliche Frage! Davitenios Antwort ist eine großartige Antwort auf die eigentliche Frage.
Sam Watkins
26

Sie können git filter-branchmit Transplantaten das Commit Nummer 4 zum neuen Root-Commit Ihres Zweigs machen. Erstellen Sie einfach die Datei.git/info/grafts mit nur einer Zeile, die den SHA1 von Commit Nummer 4 enthält.

Wenn Sie jetzt ein git logoder tun, werden gitkSie sehen, dass diese Befehle die Commit-Nummer 4 als Stamm Ihres Zweigs anzeigen. In Ihrem Repository hat sich jedoch nichts geändert. Sie können löschen .git/info/graftsund die Ausgabe von git logoder gitkwird wie zuvor sein. Um Commit Nummer 4 tatsächlich zum neuen Root zu machen, müssen Sie git filter-branchohne Argumente ausführen .

Davitenio
quelle
Dies ist viel besser als eine Rebase, da es keine Probleme beim Beibehalten von Merge-Commits gibt und keine Änderung der Zeitstempel verursacht. Einfacher und schneller als alle Rebase-Methoden.
mmrobins
Gibt es tatsächlich eine Möglichkeit, alle Commits, die nicht mehr Teil dieses Zweigs sind, physisch zu löschen? git gc --prune=0scheint sie nicht aufzuräumen.
Verhogen
1
@verhogen git gc --prune=nowbereinigt physisch alle Commits, auf die nicht mehr verwiesen wird. Wenn dies bei Ihnen nicht funktioniert, haben Sie möglicherweise einen Remote-Tracking-Zweig, der immer noch auf den alten Stamm verweist. Liste mit git branch -r, entferne dann den Remote-Zweig zum Beispiel mit git branch -rd origin/masterund führe ihn dann git gc --prune=nowerneut aus.
Kayahr
20

Dank des Beitrags von JesperE, den ich mir angesehen habe git-filter-branch, ist das vielleicht genau das, was Sie wollen. Es sieht so aus, als könnten Sie auch Ihre früheren Commits beibehalten, außer sie würden geändert, da Ihre Big Files entfernt wurden. Aus der Manpage zu git-filter-branch :

Angenommen, Sie möchten eine Datei (die vertrauliche Informationen oder Urheberrechtsverletzungen enthält) aus allen Commits entfernen:

git filter-branch --tree-filter 'rm filename' HEAD

Lesen Sie unbedingt diese Manpage ... natürlich möchten Sie dies auf einem Ersatzklon Ihres Repositorys tun, um sicherzustellen, dass es wie erwartet funktioniert.

Pat Notz
quelle
2
Schauen Sie sich den Link von Github an ... hat einige leistungsstarke Optionen mit dem Befehl git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm
5

Ist git-fast-exportwas du suchst?

NAME
   git-fast-export - Git data exporter

SYNOPSIS
   git-fast-export [options] | git-fast-import

DESCRIPTION
   This program dumps the given revisions in a form suitable to be piped into git-fast-
   import(1).

   You can use it as a human readable bundle replacement (see git-bundle(1)), or as a kind
   of an interactive git-filter-branch(1).
JesperE
quelle