pendientes - ¿Cómo eliminar/eliminar un archivo grande del historial de confirmaciones en el repositorio Git?
git ver archivos modificados (14)
¿Por qué no usar este comando simple pero poderoso?
git filter-branch --tree-filter ''rm -f DVD-rip'' HEAD
La opción --tree-filter
ejecuta el comando especificado después de cada verificación del proyecto y luego vuelve a enviar los resultados. En este caso, elimina un archivo llamado DVD-rip de cada instantánea, ya sea que exista o no.
Vea este enlace .
Ocasionalmente, coloqué un DVD-rip en un proyecto de sitio web, y luego, descuidadamente, git commit -a -m ...
, y, zap, el repo fue hinchado por 2,2 conciertos. La próxima vez hice algunas ediciones, eliminé el archivo de video y lo confirmé todo, pero el archivo comprimido todavía está allí en el repositorio, en la historia.
Sé que puedo comenzar ramas desde esos compromisos y cambiar de base una rama a otra. Pero, ¿qué debo hacer para unir los 2 confirmaciones para que el archivo grande no se muestre en el historial y se limpie en el procedimiento de recolección de basura?
(La mejor respuesta que he visto a este problema es: https://.com/a/42544963/714112 , copiado aquí ya que este hilo aparece alto en los rankings de búsqueda de Google pero ese otro no)
🚀 Una cáscara 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 muestra de recompra, corrió unas 100 veces más rápido que los otros que se encuentran aquí.
En mi confiable sistema Athlon II X4, maneja el repositorio de Linux Kernel con sus 5,622,155 objetos en poco más de un minuto .
El guión base
git rev-list --objects --all /
| git cat-file --batch-check=''%(objecttype) %(objectname) %(objectsize) %(rest)'' /
| awk ''/^blob/ {print substr($0,6)}'' /
| sort --numeric-sort --key=2 /
| cut --complement --characters=13-40 /
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Cuando ejecute el código anterior, obtendrá una salida agradable y legible para el ser humano como esta:
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
🚀 Eliminación rápida de archivos 🚀
Supongamos que desea eliminar los archivos a
y b
de cada confirmación accesible desde HEAD
, puede usar este comando:
git filter-branch --index-filter ''git rm --cached --ignore-unmatch a b'' HEAD
Básicamente hice lo que estaba en esta respuesta: https://.com/a/11032521/1286423
(Para la historia, lo copiaré y pegaré aquí)
$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/
$ git reflog expire --all
$ git gc --aggressive --prune
$ git push origin master --force
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 tree
objetos del tree
que apuntan a esos archivos. Mi solución definitiva para matarlo realmente fue:
# First, apply what''s in the answer linked in the front
# and before doing the gc --prune --aggressive, do:
# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit
# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit,
# so we remove all the references.
# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/
# Then you can do a garbage collection,
# and the hidden files really will get gc''ed
git gc --prune --aggressive
Mi repositorio (el .git
) cambió de 32MB a 388KB, que incluso la rama de filtro no pudo limpiar.
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, la reorganización tampoco es fácil, ya que cualquier referencia a la mancha evitará que el recolector de basura Git limpie el espacio. Esto incluye referencias remotas y referencias de reflog.
Puse git forget-blob
, un pequeño script que intenta eliminar todas estas referencias, y luego usa git filter-branch para reescribir cada confirmación en la rama.
Una vez que tu blob no esté referenciado completamente, git gc
se deshará de él.
El uso es bastante simple git forget-blob file-to-forget
. Puedes obtener más información aquí
Puse esto junto gracias a las respuestas de y algunas entradas de blog. Créditos a ellos!
Después de probar virtualmente cada respuesta en SO, finalmente encontré esta gema que eliminó y eliminó rápidamente los archivos grandes en mi repositorio y me permitió sincronizar nuevamente: http://www.zyxware.com/articles/4027/how-to-delete-files-permanently-from-your-local-and-remote-git-repositories
CD a su carpeta de trabajo local y ejecute el siguiente comando:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
reemplace FOLDERNAME con el archivo o carpeta que desea eliminar del repositorio de git dado.
Una vez hecho esto, ejecute los siguientes comandos para limpiar el repositorio local:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
Ahora empuje todos los cambios al repositorio remoto:
git push --all --force
Esto limpiará el repositorio remoto.
Estos comandos funcionaron en mi caso:
git filter-branch --force --index-filter ''git rm --cached -r --ignore-unmatch oops.iso'' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
Es un poco diferente de las versiones anteriores.
Para aquellos que necesitan enviar esto a github / bitbucket (solo probé esto con bitbucket):
# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn''t have in your local
git push --all --prune --force
# Once you pushed, all your teammates need to clone repository again
# git pull will not work
Lo que quieres hacer es altamente perjudicial si has publicado el historial para otros desarrolladores. Consulte “Recuperación de Rebase de Upstream” en la documentación de git rebase
para conocer los pasos necesarios después de reparar su historial.
Tiene al menos dos opciones: git filter-branch
y una rebase interactiva, ambas explicadas a continuación.
Usando 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:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Tenga en cuenta que git lola
es un alias no estándar pero muy útil. Con el interruptor --name-status
, podemos ver modificaciones de árbol asociadas con cada confirmación.
En la confirmación "Sin cuidado" (cuyo nombre de objeto SHA1 es ce36c98) el archivo oops.iso
es el DVD-rip agregado por accidente y eliminado en la próxima confirmación, cb14efd. Usando la técnica descrita en la publicación de blog mencionada anteriormente, el comando a ejecutar es:
git filter-branch --prune-empty -d /dev/shm/scratch /
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" /
--tag-name-filter cat -- --all
Opciones:
-
--prune-empty
elimina las confirmaciones que se vuelven vacías ( es decir , no cambian el árbol) como resultado de la operación de filtrado. 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, especificar un árbol en/dev/shm
resultará en una ejecución más rápida . -
--index-filter
es el evento principal y se ejecuta contra el índice en cada paso del historial. Desea eliminaroops.iso
donde se encuentre, pero no está presente en todos los confirmaciones. El comandogit rm --cached -f --ignore-unmatch oops.iso
elimina el DVD-rip cuando está presente y, de lo contrario, no falla. -
--tag-name-filter
describe cómo reescribir los nombres de etiquetas. Un filtro decat
es la operación de identidad. Es posible que su repositorio, como el ejemplo anterior, no tenga ninguna etiqueta, pero incluí esta opción para una generalidad completa. -
--
Especifica el final de las opciones paragit filter-branch
-
--all
siguientes--
es una abreviatura de todas las referencias. Su repositorio, como el ejemplo anterior, puede tener solo un ref (maestro), pero incluí esta opción para generalidad completa.
Después de algunos batidos, la historia es ahora:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Tenga en cuenta que la nueva confirmación "Sin other.html
" agrega solo other.html
y que la other.html
"Eliminar DVD-rip" ya no está en la rama principal. La rama etiquetada refs/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".
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
Para una alternativa más simple, clone el repositorio para descartar los bits no deseados.
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
Al usar un file:///...
URL clonada copia los objetos en lugar de crear solo enlaces físicos.
Ahora tu historia es:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
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. oops.iso
"descuidado" y "página de inicio de sesión" consiguieron un nuevo padre, por lo que sus SHA1 cambiaron.
Rebase interactiva
Con una historia de:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
desea eliminar oops.iso
de "No tener oops.iso
" como si nunca lo hubiera agregado, y luego "Eliminar DVD-rip" es inútil para usted. Por lo tanto, nuestro plan de entrar en una reorganización interactiva es mantener la "Página de administración", editar "Sin cuidado" y descartar "Eliminar DVD-rip".
Al ejecutar $ git rebase -i 5af4522
inicia un editor con los siguientes contenidos.
pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit''s log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
Ejecutando nuestro plan, lo modificamos para
edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
Es decir, eliminamos la línea con "Eliminar DVD-rip" y cambiamos la operación en "Sin cuidado" para edit
lugar de pick
.
Guardar-salir del editor nos deja en un símbolo del sistema con el siguiente mensaje.
Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Como el mensaje nos dice, estamos en la confirmación "Sin cuidado" que queremos editar, por lo que ejecutamos dos comandos.
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
El primero elimina el archivo ofensivo del índice. El segundo modifica o modifica "Descuido" para que sea el índice actualizado y -C HEAD
indica a git que reutilice el mensaje de confirmación anterior. Finalmente, git rebase --continue
continúa con el resto de la operación de rebase.
Esto da una historia de:
$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
que es lo que quieres
Me encontré con esto con una cuenta de bitbucket, donde accidentalmente había almacenado grandes copias de seguridad * .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 volver a escribir completamente su historial ( incluidas las etiquetas ).
fuente: http://naleid.com/blog/2012/01/17/finding-and-purging-big-files-from-git-history
Puedes hacer esto usando el comando de branch filter
:
git filter-branch --tree-filter ''rm -rf path/to/your/file'' HEAD
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
Solo tenga en cuenta que estos comandos pueden ser muy destructivos. Si hay más personas trabajando en el repositorio, todos tendrán que tirar del 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.
$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/
$ git reflog expire --all
$ git gc --aggressive --prune
$ git push origin master --force
Usa Git Extensions , es una herramienta de interfaz de usuario. Tiene un complemento llamado "Buscar archivos grandes" que encuentra los archivos lage en los repositorios y permite eliminarlos de forma permanente.
No use ''git filter-branch'' antes de usar esta herramienta, ya que no podrá encontrar los archivos eliminados por ''filter-branch'' (A pesar de que ''high-filter-branch'' no elimina los archivos completamente de los archivos del paquete de repositorio) .
Use BFG Repo-Cleaner , una alternativa más simple y rápida a 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:
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
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 Git. Luego puedes usar git gc
para limpiar los datos muertos:
$ git gc --prune=now --aggressive
El BFG suele ser al menos 10-50x más rápido que ejecutar git-filter-branch
y, en general, es más fácil de usar.
Revelación completa: soy el autor de BFG Repo-Cleaner.
git filter-branch --tree-filter ''rm -f path/to/file'' HEAD
funcionó bastante bien para mí, aunque me encontré con el mismo problema que se describe here , que resolví siguiendo esta sugerencia .
El libro pro-git tiene un capítulo completo sobre la reescritura del historial : eche un vistazo a la filter-branch
/ eliminación de un archivo de cada confirmación .