Eliminar archivo grande .pack creado por git

112

Revisé una carga de archivos en una rama y los fusioné y luego tuve que eliminarlos y ahora me queda un archivo .pack grande del que no sé cómo deshacerme.

Eliminé todos los archivos usando git rm -rf xxxxxxy también ejecuté la --cachedopción.

¿Alguien puede decirme cómo puedo eliminar un archivo .pack grande que se encuentra actualmente en el siguiente directorio:

.git/objects/pack/pack-xxxxxxxxxxxxxxxxx.pack

¿Solo necesito eliminar la rama que todavía tengo pero que ya no uso? ¿O hay algo más que deba ejecutar?

No estoy seguro de la diferencia, pero muestra un candado contra el archivo.

Gracias


EDITAR

Aquí hay algunos extractos de mi bash_history que deberían dar una idea de cómo logré llegar a este estado (supongamos que en este punto estoy trabajando en una rama de git llamada 'my-branch' y tengo una carpeta que contiene más carpetas / archivos):

git add .
git commit -m "Adding my branch changes to master"
git checkout master
git merge my-branch
git rm -rf unwanted_folder/
rm -rf unwanted_folder/     (not sure why I ran this as well but I did)

Pensé que también ejecuté lo siguiente, pero no aparece en bash_history con los demás:

git rm -rf --cached unwanted_folder/

También pensé que había ejecutado algunos comandos git (como git gc) para intentar ordenar el archivo del paquete, pero tampoco aparecen en el archivo .bash_history.

usuario1116573
fuente
¿Puede aclarar cómo los eliminó? Si todavía están en el historial de confirmaciones, todavía estarán en sus archivos de paquete.
loganfsmyth
Hola @loganfsmyth, agregué los scripts del historial de bash que, con suerte, ayudarán.
user1116573

Respuestas:

201

El problema es que, aunque eliminó los archivos, todavía están presentes en revisiones anteriores. Ese es el objetivo de git, es que incluso si eliminas algo, aún puedes recuperarlo accediendo al historial.

Lo que está buscando hacer se llama reescritura del historial e involucró el git filter-branchcomando.

GitHub tiene una buena explicación del problema en su sitio. https://help.github.com/articles/remove-sensitive-data

Para responder a su pregunta de manera más directa, lo que básicamente necesita ejecutar es este comando unwanted_filename_or_folderreemplazado en consecuencia:

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch unwanted_filename_or_folder' --prune-empty

Esto eliminará todas las referencias a los archivos del historial activo del repositorio.

Siguiente paso, realizar un ciclo de GC para forzar que todas las referencias al archivo caduquen y purguen del archivo de paquete. No es necesario reemplazar nada en estos comandos.

git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
# or, for older git versions (e.g. 1.8.3.1) which don't support --stdin
# git update-ref $(git for-each-ref --format='delete %(refname)' refs/original)
git reflog expire --expire=now --all
git gc --aggressive --prune=now
loganfsmyth
fuente
3
Lo he marcado como aceptado si eso lo hace más fácil para cualquiera que venga a esta pregunta en el futuro, aunque en realidad resolví mi problema en ese momento creando un nuevo repositorio de git
user1116573
3
No sé cómo se te ocurrió esto, pero ... Tú eres el hombre. Gracias.
Ezekiel Victor
5
Esta respuesta me indicó la dirección correcta. Pero para eliminar realmente los archivos se necesitan 3 comandos más 1) git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin2) git reflog expire --expire=now --all3)git gc --prune=now
arod
3
Encuentro el uso bfgmucho más fácil. También se recomienda en los documentos oficiales de github: help.github.com/articles/…
Timo
2
@Timo Es bueno agregar una nueva respuesta, si las cosas han cambiado con el tiempo. ¡Ve a por ello!
loganfsmyth
12

Escenario A : si sus archivos grandes solo se agregaron a una rama, no necesita ejecutar git filter-branch. Solo necesita eliminar la rama y ejecutar la recolección de basura:

git branch -D mybranch
git reflog expire --expire-unreachable=all --all
git gc --prune=all

Escenario B : Sin embargo, según su historial de bash, parece que fusionó los cambios en master. Si no ha compartido los cambios con nadie ( git pushtodavía no ). Lo más fácil sería restablecer el maestro antes de la fusión con la rama que tenía los archivos grandes. Esto eliminará todas las confirmaciones de su rama y todas las confirmaciones realizadas en master después de la fusión. Por lo tanto, es posible que pierda los cambios, además de los archivos grandes, que realmente deseaba:

git checkout master
git log # Find the commit hash just before the merge
git reset --hard <commit hash>

Luego, ejecute los pasos del escenario A.

Escenario C : si hubo otros cambios de la rama o cambios en el maestro después de la fusión que desea mantener, sería mejor volver a basar el maestro e incluir selectivamente las confirmaciones que desee:

git checkout master
git log # Find the commit hash just before the merge
git rebase -i <commit hash>

En su editor, elimine las líneas que correspondan a las confirmaciones que agregaron los archivos grandes, pero deje todo lo demás como está. Guardar y Salir. Su rama maestra solo debe contener lo que desee y no archivos grandes. Tenga en cuenta que git rebasesin -peliminará las confirmaciones de fusión, por lo que se quedará con un historial lineal para el maestro después <commit hash>. Esto probablemente esté bien para usted, pero si no, podría intentarlo -p, pero git help rebasedice combining -p with the -i option explicitly is generally not a good idea unless you know what you are doing.

Luego, ejecute los comandos del escenario A.

solo ninguno
fuente
Hay una variante del Escenario A aquí , sin embargo, con un problema adicional inesperado.
Escenario Un problema mío resuelto, para eliminar una gran cantidad de archivo de paquete temporal. El repositorio fue administrado por un servidor de compilación y provoca la creación de archivos no deseados dentro de la carpeta .git / objects / pack. Podría liberar valiosos GB de mi disco.
xrissz
7

Como loganfsmyth ya indicó en su respuesta , debe purgar el historial de git porque los archivos continúan existiendo allí incluso después de eliminarlos del repositorio. Los documentos oficiales de GitHub recomiendan BFG, que encuentro más fácil de usar que filter-branch:

Eliminar archivos del historial

Descarga BFG de su sitio web. Asegúrese de tener Java instalado, luego cree un duplicado y purgue el historial. Asegúrese de reemplazarlo YOUR_FILE_NAMEcon el nombre del archivo que desea eliminar:

git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --delete-files YOUR_FILE_NAME some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push

Eliminar una carpeta

Igual que el anterior pero use --delete-folders

java -jar bfg.jar --delete-folders YOUR_FOLDER_NAME some-big-repo.git

Otras opciones

BFG también permite opciones aún más sofisticadas (ver documentos ) como estas:

Elimine todos los archivos de más de 100 M del historial:

java -jar bfg.jar --strip-blobs-bigger-than 100M some-big-repo.git

¡Importante!

Cuando ejecute BFG, tenga cuidado de que ambos YOUR_FILE_NAMEy YOUR_FOLDER_NAMEsean solo nombres de archivos / carpetas. No son caminos , ¡así que algo como foo/bar.jpgno funcionará! En su lugar, todos los archivos / carpetas con el nombre especificado se eliminarán del historial del repositorio, sin importar en qué ruta o rama existieran.

Timo
fuente
Me pregunto si quiero aplicar esta bfgherramienta a un repositorio de git local, ¿cómo debería verse el comando?
Angel Todorov
5

Una opción:

ejecutar git gcmanualmente para condensar una cantidad de archivos de paquete en uno o varios archivos de paquete. Esta operación es persistente (es decir, el archivo de paquete grande conservará su comportamiento de compresión) por lo que puede ser beneficioso comprimir un repositorio periódicamente congit gc --aggressive

Otra opción es guardar el código y .git en algún lugar y luego eliminar el .git y comenzar de nuevo a usar este código existente, creando un nuevo repositorio de git ( git init).

Michael Durrant
fuente
Hola, Michael, intenté ejecutar git gcy bajé a solo un par de archivos de paquete, pero el grande sigue siendo uno de ellos y me gustaría deshacerme de él para poder hacer una copia de seguridad de la carpeta externamente más fácilmente (zip antes era 1 -2Mb, ahora 55Mb). A menos que alguien pueda sugerir algo más, creo que tendré que crear un git nuevo. Supongo que esto significa que perderé el acceso a las ramas que tengo actualmente, etc.
user1116573
2
Dejé de intentarlo y eliminé la carpeta .git y creé un nuevo repositorio de git como dijiste. Lo consideraré una lección aprendida. Gracias Michael.
user1116573
4
Esto no tiene mucho sentido. ¿Por qué no puedes simplemente decirle a git que consolide el repositorio actual y elimine los archivos del paquete en el proceso?
jml
4

Ejecute el siguiente comando, reemplazándolo PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATApor la ruta del archivo que desea eliminar, no solo por su nombre de archivo. Estos argumentos:

  1. Obligar a Git a procesar, pero no verificar, el historial completo de cada rama y etiqueta
  2. Elimina el archivo especificado, así como cualquier confirmación vacía generada como resultado
  3. Sobrescriba sus etiquetas existentes
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" --prune-empty --tag-name-filter cat -- --all

Esto eliminará a la fuerza todas las referencias a los archivos del historial activo del repositorio.

Siguiente paso, realizar un ciclo de GC para forzar que todas las referencias al archivo caduquen y purguen del archivo del paquete. No es necesario reemplazar nada en estos comandos.

git update-ref -d refs/original/refs/remotes/origin/master
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --aggressive --prune=now
Benjamín Wasula
fuente
Finalmente, de la segunda parte, obtuve un repositorio de 28G a 158M. Casi nada más en Google funcionó. Gracias.
Sridhar Sarnobat
Seguí los pasos anteriores y presioné como "git push origin --force --all" y aún así mis ramas remotas (maestra, desarrollo y característica / ASD-1010) no se limpiaron. Cuando lo cloné de nuevo desde un repositorio remoto, los archivos .pack todavía estaban presentes. ¿Cómo puedo reflejar esta limpieza en todas las ramas de git remotas?
Sambit Swain hace
1

Llego un poco tarde al programa, pero en caso de que la respuesta anterior no resolviera la consulta, encontré otra manera. Simplemente elimine el archivo grande específico de .pack. Tuve este problema en el que verifiqué accidentalmente un archivo grande de 2 GB. Seguí los pasos explicados en este enlace: http://www.ducea.com/2012/02/07/howto-completely-remove-a-file-from-git-history/

Rishabh Kumar
fuente
Después de hacer este método, eliminará por completo todo el historial del proyecto o simplemente eliminará el archivo especificado.
Samim Aftab Ahmed
-3

esta es una solución más práctica que de codificación. zip el archivo. Abra el zip en formato de vista de archivo (diferente de descomprimir). Elimina el archivo .pack. Descomprima y reemplace la carpeta. ¡Funciona de maravilla!

shreya10
fuente