Colapsar el historial de un repositorio de git

85

Tenemos un proyecto de git que tiene una gran historia.

Específicamente, al principio del proyecto había bastantes archivos de recursos binarios en el proyecto, estos ahora se han eliminado ya que son recursos efectivamente externos.

Sin embargo, el tamaño de nuestro repositorio es> 200 MB (el pago total actualmente es de ~ 20 MB) debido a que estos archivos se han confirmado previamente.

Lo que nos gustaría hacer es "colapsar" el historial para que el repositorio parezca haber sido creado a partir de una revisión posterior a la anterior. Por ejemplo

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Repositorio creado
  2. Se agregó un gran conjunto de archivos binarios
  3. Gran conjunto de archivos binarios eliminados
  4. Nuevo 'inicio' previsto del repositorio

Así que efectivamente queremos perder el historial del proyecto antes de cierto punto. En este punto solo hay una rama, por lo que no hay ninguna complicación al tratar de lidiar con múltiples puntos de inicio, etc. Sin embargo, no queremos perder todo el historial y comenzar un nuevo repositorio con la versión actual.

¿Es esto posible o estamos condenados a tener un repositorio inflado para siempre?

Gareth
fuente

Respuestas:

89

Puede eliminar la hinchazón binaria y conservar el resto de su historial. Git te permite reordenar y 'aplastar' las confirmaciones anteriores, por lo que puedes combinar solo las confirmaciones que agregan y eliminan tus archivos binarios grandes. Si todas las adiciones se realizaron en una confirmación y las eliminaciones en otra, esto será mucho más fácil que tratar con cada archivo.

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

Busque esto para las confirmaciones que agregan y eliminan sus archivos binarios y anote sus SHA1, digamos 2bcdefy 3cdef3.

Luego, para editar el historial del repositorio, use el rebase -icomando con su opción interactiva, comenzando con el padre de la confirmación donde agregó sus binarios. Lanzará su $ EDITOR y verá una lista de confirmaciones que comienzan 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
  .
  .

Insertar squash 3cdef3como segunda línea y eliminar la línea que dice pick 3cdef3de la lista. Ahora tiene una lista de acciones para el interactivo rebaseque combinará las confirmaciones que agregan y eliminan sus binarios en una confirmación cuya diferencia es cualquier otro cambio en esas confirmaciones. Luego, volverá a aplicar todas las confirmaciones posteriores en orden, cuando le indique que se complete:

$ git rebase --continue

Esto llevará uno o dos minutos.
Ahora tiene un repositorio que ya no tiene archivos binarios que van o vienen. Pero seguirán ocupando espacio porque, de forma predeterminada, Git mantiene los cambios durante 30 días antes de que se puedan recolectar la basura, para que puedas cambiar de opinión. Si desea eliminarlos ahora:

$ 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                      

Ahora eliminó la hinchazón pero conservó el resto de su historial.

Paul
fuente
7
Solo tiene que recordar si otros ya han extraído de ese repositorio, reescribir el historial confundirá su atracción. El manual de git-rebase explica cómo recuperar esos otros repositorios. kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto
esta es una gran respuesta para el problema específico del usuario, ¡pero no para la pregunta real! La respuesta de davitenio es una gran respuesta para la pregunta real.
Sam Watkins
27

Puede usar git filter-branchcon injertos para hacer que la confirmación número 4 sea la nueva confirmación raíz de su rama. Solo crea el archivo.git/info/grafts con solo una línea que contiene el SHA1 de la confirmación número 4.

Si ahora hace un git logo gitkverá que esos comandos mostrarán el compromiso número 4 como la raíz de su rama. Pero nada habrá cambiado realmente en su repositorio. Puede eliminar .git/info/graftsy la salida de git logo gitkserá como antes. Para que la confirmación número 4 sea la nueva raíz, tendrá que ejecutarla git filter-branch, sin argumentos.

davitenio
fuente
Esto es mucho mejor que una rebase, ya que no tiene problemas para preservar las confirmaciones de fusión y no hace que cambien las marcas de tiempo. También es más fácil y rápido que todos los métodos de rebase.
mmrobins
En realidad, ¿hay alguna forma de eliminar físicamente todas las confirmaciones que ya no forman parte de esa rama? git gc --prune=0no parece limpiarlos.
Verhogen
1
@verhogen git gc --prune=nowlimpia físicamente todas las confirmaciones a las que ya no se hace referencia. Si esto no funciona para usted, es posible que tenga alguna rama de seguimiento remoto que aún haga referencia a la raíz anterior. Enumere con git branch -r, luego elimine la rama remota, por ejemplo, con git branch -rd origin/mastery luego git gc --prune=nowvuelva a ejecutar .
kayahr
20

Gracias a la publicación de JesperE que miré git-filter-branch, puede que eso sea lo que quieres. Parece que también podría conservar sus confirmaciones anteriores, excepto que se modificarían ya que se eliminaron sus archivos grandes. Desde la página de manual de git-filter-branch :

Suponga que desea eliminar un archivo (que contiene información confidencial o violación de derechos de autor) de todas las confirmaciones:

git filter-branch --tree-filter 'nombre de archivo rm' HEAD

Asegúrese de leer esa página de manual ... obviamente, querrá hacer esto en un clon de repuesto de su repositorio para asegurarse de que funcione como se espera.

Pat Notz
fuente
2
Consulte el enlace de github ... tiene algunas opciones poderosas con el comando git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm
5

¿Es git-fast-exportlo que buscas?

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
fuente