De vez en cuando soltaba un DVD-rip en un proyecto de sitio web, luego descuidadamente git commit -a -m ...
, y, zap, el repositorio estaba lleno de 2,2 conciertos. La próxima vez que hice algunas ediciones, eliminé el archivo de video y lo comprometí todo, pero el archivo comprimido todavía está allí en el repositorio, en el historial.
Sé que puedo iniciar ramas de esos commits y volver a crear una rama en otra. Pero, ¿qué debo hacer para fusionar los 2 commits para que el archivo grande no se muestre en el historial y se limpie en el procedimiento de recolección de basura?
git filter-branch
, pero encontré que lo contrario es cierto.Respuestas:
Use el BFG Repo-Cleaner , una alternativa más simple y más rápida a la
git-filter-branch
diseñada específicamente para eliminar archivos no deseados del historial de Git.Siga cuidadosamente las instrucciones de uso , la parte central es solo esto:
Cualquier archivo de más de 100 MB de tamaño (que no esté en su última confirmación) se eliminará del historial de su repositorio de Git. Luego puede usar
git gc
para limpiar los datos muertos:El BFG suele ser al menos 10-50 veces más rápido que correr
git-filter-branch
, y generalmente es más fácil de usar.Divulgación completa: soy el autor del BFG Repo-Cleaner.
fuente
git push --force
después de sus pasos, de lo contrario, el repositorio remoto aún no ha cambiado.git push --force
. También vale la pena señalar: es posible que el control remoto no permita los empujes forzados (gitlab.com no lo hace, por defecto. Tuvo que "desproteger" la rama).Lo que desea hacer es altamente perjudicial si ha publicado el historial a otros desarrolladores. Consulte "Recuperación de la base de datos ascendente" en la
git rebase
documentación para conocer los pasos necesarios después de reparar su historial.Tiene al menos dos opciones:
git filter-branch
y un rebase interactivo, ambos explicados a continuación.Utilizando
git filter-branch
Tuve un problema similar con los datos de prueba binarios voluminosos de una importación de Subversion y escribí sobre la eliminación de datos de un repositorio git .
Digamos que tu historial de git es:
Tenga en cuenta que
git lola
es un alias no estándar pero muy útil. Con el--name-status
interruptor, podemos ver las modificaciones del árbol asociadas con cada confirmación.En el commit "Descuidado" (cuyo nombre de objeto SHA1 es ce36c98) el archivo
oops.iso
es el DVD-rip agregado por accidente y eliminado en el siguiente commit, cb14efd. Usando la técnica descrita en la publicación de blog mencionada anteriormente, el comando para ejecutar es:Opciones:
--prune-empty
elimina las confirmaciones que se vuelven vacías ( es decir , no cambian el árbol) como resultado de la operación de filtro. En el caso típico, esta opción produce un historial más limpio.-d
nombra un directorio temporal que aún no existe para usar para construir el historial filtrado. Si está ejecutando en una distribución moderna de Linux, la especificación de un árbol/dev/shm
dará como resultado una ejecución más rápida .--index-filter
es el evento principal y se ejecuta contra el índice en cada paso de la historia. Desea eliminaroops.iso
donde se encuentre, pero no está presente en todas las confirmaciones. El comandogit rm --cached -f --ignore-unmatch oops.iso
elimina el DVD-rip cuando está presente y no falla de lo contrario.--tag-name-filter
describe cómo reescribir nombres de etiquetas. Un filtro decat
es la operación de identidad. Es posible que su repositorio, como el ejemplo anterior, no tenga etiquetas, pero incluí esta opción para una generalidad completa.--
especifica el final de las opciones paragit filter-branch
--all
Lo siguiente--
es una abreviatura para todas las referencias. Su repositorio, como el ejemplo anterior, puede tener solo una referencia (maestra), pero incluí esta opción para una generalidad completa.Después de algunos cambios, la historia es ahora:
Tenga en cuenta que la nueva confirmación "Descuidado" solo agrega
other.html
y que la confirmación "Eliminar DVD-rip" ya no está en la rama maestra. La rama etiquetadarefs/original/refs/heads/master
contiene sus confirmaciones originales en caso de que haya cometido un error. Para eliminarlo, siga los pasos en "Lista de verificación para reducir un repositorio".Para una alternativa más simple, clone el repositorio para descartar los bits no deseados.
El uso de una
file:///...
URL de clonación copia objetos en lugar de crear solo enlaces duros.Ahora tu historia es:
Los nombres de objeto SHA1 para las dos primeras confirmaciones ("Índice" y "Página de administración") permanecieron iguales porque la operación de filtro no modificó esas confirmaciones. “Descuidado” perdido
oops.iso
y “página de inicio de sesión” consiguieron un nuevo padre, por lo que sus SHA1s hicieron el cambio.Rebase interactivo
Con una historia de:
desea eliminar
oops.iso
de "Descuidado" como si nunca lo hubiera agregado, y luego "Eliminar DVD-rip" es inútil para usted. Por lo tanto, nuestro plan para crear una nueva versión interactiva es mantener la "Página de administración", editar "Descuidado" y descartar "Eliminar DVD-rip".La ejecución
$ git rebase -i 5af4522
inicia un editor con los siguientes contenidos.Al ejecutar nuestro plan, lo modificamos a
Es decir, eliminamos la línea con "Eliminar DVD-rip" y cambiamos la operación en "Descuidado" para que sea en
edit
lugar depick
.Guardar-salir del editor nos deja en el símbolo del sistema con el siguiente mensaje.
Como nos dice el mensaje, estamos en el compromiso "Descuidado" que queremos editar, por lo que ejecutamos dos comandos.
El primero elimina el archivo ofensivo del índice. El segundo modifica o modifica "Descuidado" para que sea el índice actualizado e
-C HEAD
indica a git que reutilice el antiguo mensaje de confirmación. Finalmente,git rebase --continue
continúa con el resto de la operación de rebase.Esto da una historia de:
que es lo que quieres
fuente
-f
(o--force
) a sugit push
comando: “Por lo general, el comando se niega a actualizar una referencia remota que no sea un antecesor de la referencia local utilizada para sobrescribirla. Esta bandera deshabilita el cheque. Esto puede hacer que el repositorio remoto pierda confirmaciones; úsalo con cuidado ".... "git rm --cached -rf --ignore-unmatch path/to/dir"...
¿Por qué no usar este comando simple pero poderoso?
La
--tree-filter
opción ejecuta el comando especificado después de cada pago del proyecto y luego vuelve a comprometer los resultados. En este caso, elimina un archivo llamado DVD-rip de cada instantánea, ya sea que exista o no.Si sabe qué commit introdujo el archivo enorme (digamos 35dsa2), puede reemplazar HEAD con 35dsa2..HEAD para evitar reescribir demasiado historial, evitando así las confirmaciones divergentes si aún no ha presionado. Este comentario cortesía de @ alpha_989 parece demasiado importante para dejarlo aquí.
Ver este enlace .
fuente
fatal: bad revision 'rm'
, lo que solucioné usando en"
lugar de'
. Comando general:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
commit
dónde coloca el archivo (digamos35dsa2
), puede reemplazarloHEAD
por35dsa2..HEAD
.tree-filter
es mucho más lento que deindex-filter
esa manera, no intentará verificar todas las confirmaciones y reescribirlas. si usa HEAD, intentará hacerlo.(La mejor respuesta que he visto a este problema es: https://stackoverflow.com/a/42544963/714112 , copiado aquí ya que este hilo aparece alto en las clasificaciones de búsqueda de Google pero ese otro no)
🚀 Una carcasa increíblemente rápida de una sola línea 🚀
Este script de shell muestra todos los objetos de blob en el repositorio, ordenados de menor a mayor.
Para mi repositorio de muestra, se ejecutó aproximadamente 100 veces más rápido que los otros que se encuentran aquí.
En mi confiable sistema Athlon II X4, maneja el repositorio Kernel de Linux con sus 5.622.155 objetos en poco más de un minuto .
El guión base
Cuando ejecute el código anterior, obtendrá una buena salida legible para humanos como esta:
🚀 Eliminación rápida de archivos 🚀
Supongamos que desea eliminar los archivos
a
yb
de cada confirmación accesibleHEAD
, puede usar este comando:fuente
--tag-name-filter cat
de re-etiqueta de la nueva confirmaciones correspondientes a medida que se vuelven a escribir, es decir,git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD
(ver esta respuesta relacionada )git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD
orden de trabajo a la derecha del bategit rev-list --objects --all \ | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \ | awk '/^blob/ {print substr($0,6)}' \ | sort --numeric-sort --key=2 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Después de probar prácticamente todas las respuestas en SO, finalmente encontré esta gema que rápidamente eliminó y eliminó los archivos grandes en mi repositorio y me permitió sincronizar nuevamente: http://www.zyxware.com/articles/4027/how-to-delete -archivos-permanentemente-de-sus-repositorios-locales-y-remotos-git
CD a su carpeta de trabajo local y ejecute el siguiente comando:
reemplace FOLDERNAME con el archivo o carpeta que desea eliminar del repositorio git dado.
Una vez hecho esto, ejecute los siguientes comandos para limpiar el repositorio local:
Ahora envíe todos los cambios al repositorio remoto:
Esto limpiará el repositorio remoto.
fuente
Estos comandos funcionaron en mi caso:
Es un poco diferente de las versiones anteriores.
Para aquellos que necesitan llevar esto a github / bitbucket (solo probé esto con bitbucket):
fuente
git rm --cached files
. La propuesta de Greg Bacon es más completa, y bastante similar a esta mina, pero se perdió el índice de fuerza para los casos en que está utilizando filter-branch varias veces, y escribió tanta información que mi versión es como un currículum de eso.-f
opción no solo-rf
aquí engit rm --cached -rf --ignore-unmatch oops.iso
lugar degit rm --cached -r --ignore-unmatch oops.iso
según @ lfender6445 a continuaciónSolo tenga en cuenta que estos comandos pueden ser muy destructivos. Si hay más personas trabajando en el repositorio, todos tendrán que sacar el nuevo árbol. Los tres comandos intermedios no son necesarios si su objetivo NO es reducir el tamaño. Debido a que la rama del filtro crea una copia de seguridad del archivo eliminado y puede permanecer allí durante mucho tiempo.
fuente
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
git filter-branch --tree-filter 'rm -f path/to/file' HEAD
funcionó bastante bien para mí, aunque me encontré con el mismo problema descrito aquí , que resolví siguiendo esta sugerencia .El libro pro-git tiene un capítulo completo sobre la reescritura del historial : eche un vistazo a la sección
filter-branch
/ Eliminar un archivo de cada confirmación .fuente
Si sabe que su confirmación fue reciente en lugar de recorrer todo el árbol, haga lo siguiente:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
fuente
Me encontré con esto con una cuenta de bitbucket, donde accidentalmente había almacenado copias de seguridad ginormous * .jpa de mi sitio.
git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all
Relpace
MY-BIG-DIRECTORY
con la carpeta en cuestión para reescribir completamente su historial ( incluidas las etiquetas ).fuente: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/
fuente
Esto lo eliminará de tu historial
fuente
Básicamente hice lo que estaba en esta respuesta: https://stackoverflow.com/a/11032521/1286423
(para la historia, lo copiaré y pegaré aquí)
No funcionó, porque me gusta cambiar el nombre y mover muchas cosas. Por lo tanto, algunos archivos grandes estaban en carpetas que han sido renombradas, y creo que el gc no pudo eliminar la referencia a esos archivos debido a la referencia en los
tree
objetos que apuntan a ese archivo. Mi solución definitiva para matarlo realmente fue:Mi repositorio (the
.git
) cambió de 32 MB a 388 KB, que incluso la rama de filtro no podía limpiar.fuente
git filter-branch
es un poderoso comando que puede usar para eliminar un archivo enorme del historial de confirmaciones. El archivo permanecerá por un tiempo y Git lo eliminará en la próxima recolección de basura. A continuación se muestra el proceso completo para eliminar archivos del historial de confirmación . Por seguridad, el siguiente proceso ejecuta los comandos en una nueva rama primero. Si el resultado es lo que necesitaba, vuelva a restablecerlo en la rama que realmente desea cambiar.fuente
Use Git Extensions , es una herramienta de interfaz de usuario. Tiene un complemento llamado "Buscar archivos grandes" que encuentra archivos de lage en repositorios y permite eliminarlos permanentemente.
No use 'git filter-branch' antes de usar esta herramienta, ya que no podrá encontrar archivos eliminados por 'filter-branch' (Altough 'filter-branch' no elimina completamente los archivos de los archivos del paquete de repositorio) .
fuente
Puedes hacer esto usando el
branch filter
comando:git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
fuente
Hay muy buenas respuestas en este hilo, pero mientras tanto, muchas de ellas están desactualizadas. El uso
git-filter-branch
ya no se recomienda, porque es difícil de usar y muy lento en grandes repositorios.git-filter-repo
Es mucho más rápido y sencillo de usar.git-filter-repo
es un script de Python, disponible en github: https://github.com/newren/git-filter-repo .Solo necesita un archivo: el script Python3 git-filter-repo. Cópielo en una ruta incluida en la variable PATH. En Windows puede que tenga que cambiar la primera línea de la secuencia de comandos (consulte INSTALL.md). Necesita instalar Python3 instalado en su sistema, pero esto no es gran cosa.
Primero puedes correr
Esto le ayuda a determinar qué hacer a continuación.
Puede eliminar su archivo de extracción de DVD en todas partes:
Filter-repo es realmente rápido. Una tarea que tomó alrededor de 9 horas en mi computadora por filter-branch, se completó en 4 minutos por filter-repo. Puedes hacer muchas más cosas buenas con filter-repo. Consulte la documentación para eso.
Advertencia: haga esto en una copia de su repositorio. Muchas acciones de filter-repo no se pueden deshacer. ¡filter-repo cambiará los hashes de confirmación de todas las confirmaciones modificadas (por supuesto) y todos sus descendientes hasta las últimas confirmaciones!
fuente
Cuando se encuentre con este problema,
git rm
no será suficiente, ya que git recuerda que el archivo existió una vez en nuestro historial y, por lo tanto, mantendrá una referencia a él.Para empeorar las cosas, el rebase tampoco es fácil, porque cualquier referencia al blob evitará que el recolector de basura git limpie el espacio. Esto incluye referencias remotas y referencias de registro.
Creé
git forget-blob
un pequeño script que intenta eliminar todas estas referencias, y luego usa git filter-branch para reescribir cada commit en la rama.Una vez que su blob esté completamente desreferenciado,
git gc
lo eliminaráEl uso es bastante simple
git forget-blob file-to-forget
. Puedes obtener más información aquíhttps://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/
Lo armé gracias a las respuestas de Stack Overflow y algunas entradas de blog. Créditos para ellos!
fuente
Además de
git filter-branch
(solución git lenta pero pura) y BFG (más fácil y muy eficaz), también hay otra herramienta para filtrar con buen rendimiento:https://github.com/xoofx/git-rocket-filter
De su descripción:
El propósito de git-rocket-filter es similar al comando
git-filter-branch
mientras proporciona las siguientes características únicas:fuente