Comprimere la cronologia di un repository git


85

Abbiamo un progetto git che ha una storia abbastanza grande.

In particolare, all'inizio del progetto c'erano molti file di risorse binari nel progetto, questi ora sono stati rimossi poiché sono effettivamente risorse esterne.

Tuttavia, la dimensione del nostro repository è> 200 MB (il checkout totale è attualmente di circa 20 MB) a causa del fatto che questi file sono stati precedentemente salvati.

Quello che vorremmo fare è "comprimere" la cronologia in modo che il repository sembri essere stato creato da una revisione successiva rispetto a prima. Per esempio

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Repository creato
  2. Ampio set di file binari aggiunto
  3. Grande set di file binari rimosso
  4. Nuovo "inizio" previsto del repository

Quindi effettivamente vogliamo perdere la storia del progetto prima di un certo punto. A questo punto c'è solo un ramo, quindi non ci sono complicazioni nel cercare di gestire più punti di inizio ecc. Tuttavia non vogliamo perdere tutta la cronologia e avviare un nuovo repository con la versione corrente.

È possibile o siamo condannati ad avere un deposito gonfio per sempre?

Risposte:


89

Puoi rimuovere il blocco binario e conservare il resto della tua cronologia. Git ti consente di riordinare e "eliminare" i commit precedenti, in modo da poter combinare solo i commit che aggiungono e rimuovono i tuoi file binari di grandi dimensioni. Se le aggiunte sono state eseguite tutte in un commit e le rimozioni in un altro, questo sarà molto più semplice che gestire ogni file.

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

Cerca qui i commit che aggiungono ed eliminano i tuoi file binari e annota i loro SHA1, ad esempio 2bcdefe 3cdef3.

Quindi, per modificare la cronologia del repository, usa il rebase -icomando con la sua opzione interattiva, iniziando dal genitore del commit in cui hai aggiunto i tuoi binari. Avvierà il tuo $ EDITOR e vedrai un elenco di commit che iniziano con 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
  .
  .

Inserire squash 3cdef3 come seconda riga e rimuovi la riga pick 3cdef3dall'elenco. Ora hai un elenco di azioni per l'interattivo rebaseche combinerà i commit che aggiungono ed eliminano i tuoi binari in un commit la cui differenza è solo qualsiasi altra modifica in quei commit. Quindi riapplicherà tutti i commit successivi in ​​ordine, quando gli dici di completare:

$ git rebase --continue

Ci vorranno uno o due minuti.
Ora hai un repo che non ha più i binari in entrata o in uscita. Ma occuperanno ancora spazio perché, per impostazione predefinita, Git mantiene le modifiche per 30 giorni prima che possano essere raccolte in modo spazzatura, in modo che tu possa cambiare idea. Se vuoi rimuoverli ora:

$ 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                      

Ora hai rimosso il gonfio ma hai mantenuto il resto della tua storia.


7
Devi solo ricordare se altri hanno già estratto da quel repository, la riscrittura della cronologia confonderà la loro attrazione. Il manuale di git-rebase spiega come recuperare quegli altri repository. kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto

questa è un'ottima risposta per il problema specifico dell'utente, ma non per la domanda vera e propria! La risposta di davitenio è un'ottima risposta alla domanda vera e propria.
Sam Watkins

27

Puoi usare git filter-branchcon gli innesti per rendere il commit numero 4 il nuovo commit di root del tuo ramo. Basta creare il file.git/info/grafts con una sola riga contenente lo SHA1 del numero di commit 4.

Se ora esegui un git logo gitkvedrai che quei comandi mostreranno il commit numero 4 come radice del tuo ramo. Ma nulla sarà effettivamente cambiato nel tuo repository. È possibile eliminare .git/info/graftse l'output di git logo gitksarà come prima. Per rendere effettivamente il commit numero 4 la nuova root dovrai eseguire git filter-branch, senza argomenti.


Questo è molto meglio di un rebase poiché non ha problemi a preservare i commit di unione e non causa la modifica dei timestamp. Più facile e veloce anche di tutti i metodi di rebase.
mmrobins

In realtà, c'è un modo per eliminare fisicamente tutti i commit che non fanno più parte di quel ramo? git gc --prune=0non sembra pulirli.
Verhogen

1
@verhogen git gc --prune=nowpulisce fisicamente tutti i commit a cui non si fa più riferimento. Se questo non funziona per te, potresti avere qualche ramo di monitoraggio remoto che fa ancora riferimento alla vecchia radice. Elenca con git branch -r, quindi rimuovi il ramo remoto ad esempio con git branch -rd origin/mastere quindi esegui di git gc --prune=nownuovo.
kayahr

20

Grazie al post di JesperE che ho esaminato git-filter-branch, potrebbe effettivamente essere quello che vuoi. Sembra che tu possa conservare anche i tuoi commit precedenti tranne che verrebbero modificati dopo la rimozione dei tuoi Big Files. Dalla pagina man di git-filter-branch :

Supponi di voler rimuovere un file (contenente informazioni riservate o violazione del copyright) da tutti i commit:

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

Assicurati di leggere quella pagina man ... ovviamente vorresti farlo su un clone di riserva del tuo repository per assicurarti che funzioni come previsto.


2
Controlla il link di github ... ha alcune potenti opzioni con il comando git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm

5

È git-fast-exportquello che stai cercando?

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).
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.