2008-10-30 18 views
79

Tenemos un proyecto git que tiene una historia bastante grande.Colapsar la historia de un repositorio git

Específicamente, al inicio del proyecto había bastantes archivos de recursos binarios en el proyecto, estos ya han sido eliminados ya que son efectivamente recursos externos.

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

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 de lo que era. Por ejemplo

1-----2-----3-----4-----+---+---+ 
        \  /
        +-----+---+---+ 
  1. repositorio creado
  2. grande conjunto de archivos binarios añadió
  3. grande conjunto de archivos binarios retira
  4. Nueva intención de 'inicio' del repositorio

Así que efectivamente nos quiere 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 tratar 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 depósito inflado para siempre?

Respuesta

88

Puede eliminar la inflamación binaria y conservar el resto de su historial. Git te permite reordenar y 'aplastar' las confirmaciones anteriores, de modo que puedes combinar solo las confirmaciones que agregan y eliminan tus grandes archivos binarios. Si las adiciones se hicieron todas 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 

Buscar esto para las confirmaciones que añadir y borrar los archivos binarios y fichas sus SHA1s, y dicen 2bcdef3cdef3.

Luego, para editar el historial del repositorio, use el comando rebase -i con su opción interactiva, comenzando con el padre del compromiso donde agregó los archivos binarios.Se pondrá en marcha $ 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 3cdef3 como la segunda línea y eliminar la línea que dice pick 3cdef3 de la lista. Ahora tiene una lista de acciones para el rebase interactivo que combinará las confirmaciones que agregan y eliminan sus binarios en una confirmación cuya diferencia es cualquier otro cambio en esas confirmaciones. A continuación, se volverá a aplicar todas las confirmaciones posteriores con el fin, cuando se dice que se complete:

$ git rebase --continue 

Esto tomará un minuto o dos.
Ahora tiene un repositorio que ya no tiene los binarios que van o vienen. Pero seguirán ocupando espacio porque, de forma predeterminada, Git mantiene los cambios durante alrededor de 30 días antes de que puedan ser recogidos basura, por lo que puede 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 hemos eliminado los excesos, pero mantuvo el resto de su historia.

+1

Bueno y la respuesta completa. – JesperE

+7

Solo tienes que recordar si otros ya han sacado de ese repositorio, reescribir el historial confundirá su extracción. El manual de git-rebase explica cómo recuperar esos otros repositorios. http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html – Otto

+0

¡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. –

6

es git-fast-export ¿Qué estás buscando?

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). 
21

Gracias al post de JesperE me miraron a los git-filter-branch - que en realidad puede ser lo que quieres. Parece que también podrías conservar tus confirmaciones anteriores, excepto que se modificarían desde que se eliminaron tus archivos grandes. Desde el git-filter-branch man page:

Supongamos 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-filtro 'rm nombre de archivo' CABEZA

Asegúrate de leer esa página de manual ... obviamente querrías hacer esto en un clon de repuesto de tu repositorio para asegurarte de que funciona como se esperaba.

+2

Echa un vistazo al enlace de github ... tiene algunas opciones potentes con el comando git-filter-branch: https://help.github.com/articles/remove-sensitive-data – ricosrealm

25

Puede usar git filter-branch con injertos para hacer que el compromiso número 4 sea la nueva confirmación raíz de su rama. Basta con crear el archivo .git/info/grafts con una sola línea en ella que contiene el SHA1 de cometer el número 4.

Si ahora hace un git log o gitk verá que esos comandos se mostrarán cometer número 4 como la raíz de su rama. Pero nada habrá cambiado realmente en su repositorio. Puede eliminar .git/info/grafts y la salida de git log o gitk será como antes. Para hacer realmente commit número 4 la nueva raíz tendrá que ejecutar git filter-branch, sin argumentos.

+0

Esto es mucho mejor que una rebase, ya que no tiene problemas para conservar las asignaciones de fusión, y no hace que las marcas de tiempo cambien. Más fácil y más rápido que todos los métodos de rebase también. – mmrobins

+0

Esto funciona genial, gracias! – Verhogen

+0

En realidad, ¿hay alguna manera de eliminar físicamente todas las confirmaciones que ya no forman parte de esa rama? 'git gc --prune = 0' no parece limpiarlos. – Verhogen

Cuestiones relacionadas