Reducir el tamaño del repository de git en Bitbucket

¡Después de algunos meses de (commit & push) para mi proyecto, el tamaño del repository aumenta gradualmente en Bitbucket! es de aproximadamente 1 GB, traté de eliminar algunas carpetas de bases de datos que no son importantes para ser agregadas. Después de search encontré que la mayoría de las sugerencias proponen:

git filter-branch -f --tree-filter 'rm -rf folder/subfolder' HEAD 

Después de eliminar algunas carpetas, presiono el cambio al repository por – force, como

 git push origin master --force 

¡Finalmente encontré que el repository aumenta cada vez que uso esos commands! ¡Visiblemente, el repository aumenta 2.5 GB!

Cualquier sugerencia, por favor?

EDITAR Dependiendo de la sugerencia a continuación, probé los siguientes commands
(para todos los files grandes)

git filter-branch –index-filter "git rm -rf –cached –ignore-unmatch $ files" –tag-name-filter cat – –todo

(eliminar el historial temporal de git-filter-branch o deja de lado por un time prolongado)

rm -rf .git / refs / original /

 git reflog expire --all git gc --aggressive --prune 

¡Pero la carpeta .git / objects todavía tiene un gran tamaño!

De acuerdo, dada su respuesta a su comentario, ahora podemos decir lo que sucedió.

Lo que hace git filter-branch es copyr (algunas o todas) sus confirmaciones a las nuevas, y luego actualizar las references. Esto significa que su repository se hace más grande (no más pequeño), al less inicialmente.

Los commits que se copyn son accesibles a través de las references dadas. En este caso, la reference que proporcionó es HEAD (que git se convierte en "su twig actual", probablemente master , pero cualquiera que sea su twig actual en el momento del command filter-branch ). Si (y solo si) la nueva copy es, precisamente, bit por bit idéntica a la original, entonces en realidad es el original y no se ha hecho una copy real (el original se reutiliza en su lugar). Sin embargo, tan pronto como realice algún cambio, como eliminar folder/subfolder , a partir de ese momento, realmente se trata de copys.

El material copydo es, en este caso, más pequeño, porque ha eliminado algunos elementos. (En general, no es mucho más pequeño porque git comprime los elementos bastante bien). Pero todavía está agregando más cosas al repository: nuevas confirmaciones, que se refieren a treees nuevos, que, afortunadamente, se refieren a las mismas manchas antiguas (objects de file) como antes, solo un poco less esta vez (los objects para los files de la folder/subfolder todavía están en el repository, pero los commits y los tree-objects copydos ya no se refieren a ellos).

Pictóricamente, en este punto del process de filter-branch , ahora tenemos los dos commits anteriores:

 R--o--o---o--o <-- master \ / o--o <-- feature 

y los nuevos (supondré que la folder/subfolder apareció en la confirmación raíz original R para que tengamos una copy R' aquí):

 R'-o'-o'--o'-o' \ / o'-o' 

Lo que filter-branch hace ahora, al final del process de copy, es volver a señalar algunas references (nombres de twigs y tags, principalmente). Los que vuelve a señalar son los que usted le dice al mencionarlos como lo que la documentation llama "references positivas". En este caso, si estaba en master (es decir, HEAD era otro nombre para master ), la única reference positiva que dio es master … así que eso es todos los puntos de reference filter-branch . También hace references de copy de security cuyo nombre comienza con refs/original/ . Esto significa que ahora tienes los siguientes commits:

 R--o--o---o--o <-- refs/original/refs/heads/master \ / o--o <-- feature R'-o'-o'--o'-o' <-- master \ / o'-o' 

Tenga en count que la feature aún apunta a todas las confirmaciones antiguas (no copydas), de modo que incluso si / después de deshacerse de cualquier refs/original/ references, git retendrá todas las confirmaciones aún referencedas en cualquier actividad de recolección de basura, dando :

 R--o \ o--o <-- feature R'-o'-o'--o'-o' <-- master \ / o'-o' 

Para que filter-branch actualice todas las references, debe nombrarlas todas. Una manera fácil de hacerlo es usar --all , que literalmente nombra todas las references. En este caso, la image inicial de "después" se ve así:

 R--o--o---o--o <-- refs/original/refs/heads/master \ / o--o <-- refs/original/refs/heads/feature R'-o'-o'--o'-o' <-- master \ / o'-o' <-- feature 

Ahora bien, si borra todos los refs/original/ references, todos los commits anteriores pasan a ser sin references y pueden ser recogidos. Bueno, es decir, lo hacen a less que haya tags que los señalen.

Para las references de label, filter-branch solo las actualiza de cualquier forma si proporciona un --tag-name-filter . Por lo general, usted quiere --tag-name-filter cat , que no modifica los nombres de las tags, pero hace que filter-branch dirija a las confirmaciones recién copydas. De esta forma no se aferra a los compromisos anteriores: el objective del ejercicio es hacer que todo use las copys nuevas, y tirar las copys antiguas, para que los objects del file grande puedan ser recogidos.


Poniendo todo esto junto, en lugar de:

 git filter-branch -f --tree-filter 'rm -rf folder/subfolder' 

puedes usar:

 git filter-branch -f --tree-filter 'rm -rf folder/subfolder' \ --tag-name-filter cat -- --all 

(No necesita la secuencia de barra invertida-línea nueva; la puse para hacer que la línea se ajuste mejor en stackoverflow. Tenga en count que --tree-filter es muy lento: para este caso en particular es mucho más rápido de usar --index-filter . El command de filter de índice aquí sería git rm --cached --ignore-unmatch -r folder/subfolder .)

Tenga en count también que debe hacer todo esto en (una copy de) el repository original (usted mantuvo una copy de security, ¿no?). (Si no guardó una copy de security, los refs/originals/ pueden ser su salvación).


Editar: OK, entonces hiciste un filter-branch -ing, e hiciste algo que eliminó cualquier refs/originals/ . (En mi experimento en un repository temporal, al ejecutar git filter-branch en HEAD utilicé cualquier twig en la que estuve como la twig que fue networkingirigida, y realicé una copy de "originales" del valor anterior). No hay copys de security del repository. ¿Ahora que?

Bueno, como primer paso, haz una copy de security ahora . De esta forma, si las cosas empeoran, al less puedes volver "solo ligeramente mal". Para hacer una copy de security del repository, simplemente puede clonarlo (o: clonarlo, luego llamar al original "copy de security" y luego comenzar a trabajar en el clon). Para reference futura, dado que git filter-branch puede ser bastante destructivo, por lo general es aconsejable comenzar haciendo este process de copy de security. (Además, notaré que un clon en bitbucket, cuando aún no está editado, serviría. Desafortunadamente usted hizo un push . Quizás bitbucket puede recuperar una versión anterior del repository a partir de algunas copys de security o instantáneas propias).

A continuación, observemos una peculiaridad de los commits y sus SHA-1 "nombres verdaderos", que mencioné anteriormente. El nombre SHA-1 de una confirmación es una sum de comprobación criptográfica de su contenido. Echemos un vistazo a una confirmación de muestra en el propio tree fuente de git (recortado un poco por longitud, y direcciones de correo electrónico pegadas a cosechadores de papel de aluminio):

 $ git cat-file -p 5de7f500c13c8158696a68d86da1030313ddaf69 tree 73eee5d136d2b00c623c3fceceffab85c9e9b47e parent c4ad00f8ccb59a0ae0735e8e32b203d4bd835616 author Jeff King <peff peff.net> 1405233728 -0400 committer Junio C Hamano <gitster pobox.com> 1406567673 -0700 alloc: factor out commit index We keep a static counter to set the commit index on newly allocated objects. However, since we also need to set the [snip] 

Aquí, podemos ver que el contenido de este commit (cuyo "nombre verdadero" es 5de7f50... ) comienza con un tree y otro SHA-1, un parent y otro SHA-1, un author y un committer , luego una línea en blanco seguido por el text del post de confirmación.

Si observa un tree , verá que contiene los "nombres verdaderos" (valores SHA-1) de los subtreees (subdirectorys) y los objects de file ("blobs", en la terminología git) junto con sus modos- realmente, solo si el blob debería tener permiso de ejecución establecido, o no, y sus nombres dentro del directory. Por ejemplo, la primera línea del tree arriba es:

 100644 blob 5e98806c6cc246acef5f539ae191710a0c06ad3f .gitattributes 

lo que significa que el object de repository 5e98806... debe extraerse, colocarse en un file llamado .gitattributes y establecer no ejecutable.

Si pido a git que realice un nuevo compromiso, y lo configuro, como su contenido:

  • el mismo tree ( 73eee5d... )
  • el mismo padre ( c4ad00f... )
  • el mismo autor y autor
  • y la misma línea en blanco y el post

luego, cuando obtenga Git para escribir ese compromiso en el repository, generará el mismo "verdadero nombre" 5de7f50... En otras palabras, literalmente es la misma confirmación: ya está en el repository y git commit-tree me devolverá la ID existente. Si bien es un poco complicado configurar todo esto, eso es exactamente lo que termina git filter-branch : extrae la confirmación original, aplica los filters, configura todo y luego hace un git commit-tree .

Lo que esto significa para ti

En su repository original, ejecutó un command git filter-branch que copió commits a commits nuevos y modificados (con diferentes tree s y por lo tanto, en algún momento, diferentes nombres verdaderos que condujeron a diferentes parent ID en commit posteriores, y así sucesivamente) . Sin embargo, si copy esas confirmaciones copydas aplicando un filter que esta vez no hace nada , los nuevos objects de tree serán los mismos que los anteriores. Si el nuevo padre es el mismo y el autor, el confirmador y el post también permanecen iguales, el nuevo ID de confirmación para la copy será el mismo que el anterior.

Es decir, estas nuevas copys no son copys después de todo, ¡son solo los originales otra vez!

Cualquier otra confirmación, aquellas que no se copyron en el primer pase, se copyn y, por lo tanto, tienen diferentes ID.

Aquí es donde las cosas se ponen difíciles.

Si su repository actual se ve así (gráficamente hablando):

 R--o--o---o--o <-- xxx [needs a name so that filter-branch will process it] \ / o--o <-- feature R'-o'-o'--o'-o' <-- master \ / o'-o' 

y aplicamos una nueva filter-branch a todas las references (o incluso "todas less la master ") de tal manera que genera los mismos treees esta vez, copyrá R nuevamente y el nuevo tree coincidirá con R' , por lo que la copy será en realidad R' . Luego copyrá el primer nodo post- R , realizará los mismos cambios, y la copy será en realidad el primer nodo post- R' , o' . Esto se repetirá para todos los nodos, posiblemente incluso incluyendo R' y todos los o' s. Si filter-branch copy R' , la copy resultante será simplemente R' otra vez, porque "eliminar directory inexistente" no cambia: nuestro filter no hace nada con estas confirmaciones particulares.

Finalmente, filter-branch moverá las tags, dejando los refs/originals/ versiones detrás:

 R--o--o---o--o <-- refs/originals/refs/xxx \ / o--o <-- refs/originals/refs/feature R'-o'-o'--o'-o' <-- master, xxx \ / o'-o' <-- feature 

Este es, de hecho, el resultado deseado.

¿Qué pasa si el repository se parece más a esto? Es decir, qué pasa si no hay una label xxx o similar que apunte al original (prefiltrado) master , para que tenga esto:

 R--o \ o--o <-- feature R'-o'-o'--o'-o' <-- master \ / o'-o' 

La secuencia de commands de filter-branch aún copyrá R y el resultado seguirá siendo R' . Luego copyrá el primer o nodo y el resultado seguirá siendo el primer o' nodo, y así sucesivamente. No copyrá los nodos ahora eliminados, pero no tendrá que: ya los tenemos, accesibles a través del master nombre de twig. Como antes, filter-branch puede copyr R' y los diversos o' nodos, pero esto está bien, ya que el filter no hará nada para que las copys sean realmente solo los originales después de todo.

Por último, filter-branch , como de costumbre, actualizará las references:

 R--o \ o--o <-- refs/originals/refs/feature R'-o'-o'--o'-o' <-- master \ / o'-o' <-- feature 

La key que hace que todo esto funcione es que el filter deja intactos los commits ya modificados, de modo que sus segundas "copys" son solo las primeras copys de nuevo. 1

Una vez que todo está hecho, puede hacer la misma contracción descrita en la documentation de git filter-branch para deshacerse de los refs/originals/ names y garbage-collect de los objects ahora sin reference.


1 Si ha estado utilizando un filter que no se repite tan fácilmente (por ejemplo, uno que realiza nuevos commits con "la hora actual" como sus sellos de time), realmente necesitaría un repository original intacto, o aquellos refs/originals/ references (cualquiera sería suficiente para mantener una "copy original").