tag remove rama como cambiar git git-filter-branch git-rewrite-history git-rm

rama - git remove tag



¿Cómo solucionas una fusión incorrecta y reproduces tus buenos compromisos en una combinación fija? (12)

Introducción: tienes 5 soluciones disponibles

El cartel original dice:

Accedí accidentalmente un archivo no deseado ... a mi repositorio hace varios intentos ... Quiero eliminar completamente el archivo del historial del repositorio.

¿Es posible volver a escribir el historial de cambios de forma tal que filename.orig nunca se haya agregado al repositorio en primer lugar?

Hay muchas formas diferentes de eliminar el historial de un archivo completamente de git:

  1. Enmendando los compromisos.
  2. Reajustes duros (posiblemente más un rebase).
  3. Rebase no interactivo.
  4. Rebases interactivos.
  5. Filtrado de ramas.

En el caso del póster original, enmendar el compromiso no es realmente una opción por sí mismo, ya que luego realizó varios compromisos adicionales, pero en aras de la integridad, también explicaré cómo hacerlo, para cualquier persona que solo quiera. para modificar su compromiso previo.

Tenga en cuenta que todas estas soluciones implican alterar / reescribir el historial / las confirmaciones de una manera a otra, por lo que cualquier persona con copias antiguas de las confirmaciones tendrá que hacer un trabajo adicional para volver a sincronizar su historia con la nueva historia.

Solución 1: enmiendas de compromisos

Si accidentalmente realizó un cambio (como agregar un archivo) en su confirmación anterior, y no desea que el historial de ese cambio siga existiendo, entonces simplemente puede modificar la confirmación anterior para eliminar el archivo:

git rm <file> git commit --amend --no-edit

Solución 2: restablecimiento completo (posiblemente más un rebase)

Al igual que la solución 1, si solo desea deshacerse de su confirmación anterior, también tiene la opción de simplemente hacer un restablecimiento completo a su padre:

git reset --hard HEAD^

Ese comando restablecerá por completo tu rama a la primera confirmación principal.

Sin embargo , si, al igual que el póster original, ha realizado varias confirmaciones después de la confirmación a la que desea deshacer el cambio, todavía puede usar restablecimientos para modificarlo, pero hacerlo también implica utilizar un rebase. Estos son los pasos que puede usar para enmendar una confirmación más atrás en la historia:

# Create a new branch at the commit you want to amend git checkout -b temp <commit> # Amend the commit git rm <file> git commit --amend --no-edit # Rebase your previous branch onto this new commit, starting from the old-commit git rebase --preserve-merges --onto temp <old-commit> master # Verify your changes git diff master@{1}

Solución 3: Rebase no interactivo

Esto funcionará si solo desea eliminar un compromiso del historial por completo:

# Create a new branch at the parent-commit of the commit that you want to remove git branch temp <parent-commit> # Rebase onto the parent-commit, starting from the commit-to-remove git rebase --preserve-merges --onto temp <commit-to-remove> master # Or use `-p` insteda of the longer `--preserve-merges` git rebase -p --onto temp <commit-to-remove> master # Verify your changes git diff master@{1}

Solución 4: Rebases interactivos

Esta solución le permitirá lograr las mismas cosas que las soluciones nº 2 y nº 3, es decir, modificar o eliminar confirmaciones más atrás en el historial que su confirmación previa anterior, por lo que la solución que elija utilizar depende de usted. Las rebases interactivas no están bien adaptadas para rebasar cientos de confirmaciones, por razones de rendimiento, por lo que usaría rebases no interactivas o la solución de rama de filtro (ver más abajo) en ese tipo de situaciones.

Para comenzar la rebase interactiva, use lo siguiente:

git rebase --interactive <commit-to-amend-or-remove>~ # Or `-i` instead of the longer `--interactive` git rebase -i <commit-to-amend-or-remove>~

Esto hará que git rebobine el historial de confirmación al padre de la confirmación que desea modificar o eliminar. Luego le presentará una lista de las confirmaciones de rebobinado en orden inverso en cualquier editor que git esté configurado para usar (esto es Vim por defecto):

pick 00ddaac Add symlinks for executables pick 03fa071 Set `push.default` to `simple` pick 7668f34 Modify Bash config to use Homebrew recommended PATH pick 475593a Add global .gitignore file for OS X pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

La confirmación que desea modificar o eliminar estará en la parte superior de esta lista. Para eliminarlo, simplemente elimine su línea en la lista. De lo contrario, reemplace "pick" con "edit" en la línea, así:

edit 00ddaac Add symlinks for executables pick 03fa071 Set `push.default` to `simple`

A continuación, ingrese git rebase --continue . Si eligió eliminar el compromiso por completo, entonces todo lo que necesita hacer (aparte de la verificación, vea el paso final para esta solución). Si, por otro lado, desea modificar la confirmación, git volverá a aplicar la confirmación y luego pausará la rebase.

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue

En este punto, puede eliminar el archivo y enmendar el compromiso, luego continuar con el rebase:

git rm <file> git commit --amend --no-edit git rebase --continue

Eso es. Como paso final, ya sea que hayas modificado o eliminado por completo la confirmación, siempre es una buena idea verificar que no se hayan realizado otros cambios inesperados en tu sucursal al diferenciarlo con su estado antes de la rebase:

git diff master@{1}

Solución 5: Filtrado de Ramas

Finalmente, esta solución es mejor si desea borrar por completo todos los rastros de la existencia de un archivo del historial, y ninguna de las otras soluciones está a la altura de la tarea.

git filter-branch --index-filter / ''git rm --cached --ignore-unmatch <file>''

Eso eliminará <file> de todas las confirmaciones, comenzando desde la confirmación de la raíz. Si, en cambio, solo desea volver a escribir el rango de confirmación HEAD~5..HEAD , entonces puede pasarlo como un argumento adicional a filter-branch , como se indica en esta respuesta :

git filter-branch --index-filter / ''git rm --cached --ignore-unmatch <file>'' HEAD~5..HEAD

Nuevamente, una vez que se completa la filter-branch del filter-branch , generalmente es una buena idea verificar que no haya otros cambios inesperados al diferenciar su rama con su estado anterior antes de la operación de filtrado:

git diff master@{1}

Alternativa filtro-rama: BFG Repo Cleaner

He escuchado que la herramienta BFG Repo Cleaner se ejecuta más rápido que git filter-branch , por lo que es posible que también desees verlo como una opción. Incluso se menciona oficialmente en la documentación de la rama de filtro como una alternativa viable:

git-filter-branch le permite realizar reescrituras complejas de shell de su historial de Git, pero probablemente no necesite esta flexibilidad si simplemente está eliminando datos no deseados como archivos grandes o contraseñas. Para esas operaciones, es posible que desee considerar BFG Repo-Cleaner , una alternativa basada en JVM para git-filter-branch, generalmente al menos 10-50 veces más rápido para esos casos de uso, y con características bastante diferentes:

  • Cualquier versión particular de un archivo se limpia exactamente una vez . El BFG, a diferencia de git-filter-branch, no le da la oportunidad de manejar un archivo de manera diferente según dónde o cuándo se confirmó en su historial. Esta restricción proporciona el beneficio de rendimiento principal de The BFG, y se adapta bien a la tarea de limpiar datos incorrectos: no importa dónde se encuentran los datos incorrectos, solo quiere que desaparezcan .

  • Por defecto, BFG aprovecha al máximo las máquinas de múltiples núcleos, limpiando los archivos de confirmación en paralelo. git-filter-branch limpia las confirmaciones de forma secuencial (es decir, de una sola manera), aunque es posible escribir filtros que incluyan su propio paralelismo, en los scripts ejecutados contra cada confirmación.

  • Las opciones de comando son mucho más restrictivas que la rama de git-filter, y están dedicadas solo a las tareas de eliminar datos no deseados, por ejemplo: - --strip-blobs-bigger-than 1M .

Recursos adicionales

  1. Pro Git § 6.4 Git Tools - Reescribiendo la historia .
  2. git-filter-branch (1) Manual .
  3. Manual de git-commit (1) .
  4. git-reset (1) Manual .
  5. git-rebase (1) Manual .
  6. The BFG Repo Cleaner (ver también esta respuesta del propio creador ).

Accidentalmente cometí un archivo no deseado ( filename.orig al resolver una fusión) en mi repositorio hace varios intentos, sin que me diera cuenta hasta ahora. Quiero eliminar completamente el archivo del historial del repositorio.

¿Es posible volver a escribir el historial de cambios de forma tal que filename.orig nunca se haya agregado al repositorio en primer lugar?


Definitivamente, git filter-branch es el camino a seguir.

Lamentablemente, esto no será suficiente para eliminar el filename.orig de filename.orig de su repositorio, ya que puede ser referenciado por etiquetas, entradas de búsqueda, controles remotos, etc.

Recomiendo eliminar todas estas referencias también, y luego llamar al recolector de basura. Puede usar el script git forget-blob de this sitio web para hacer todo esto en un solo paso.

git forget-blob filename.orig


Esta es la mejor manera:
github.com/guides/completely-remove-a-file-from-all-revisions

Solo asegúrese de hacer una copia de seguridad de las copias de los archivos primero.

EDITAR

La edición de Neon fue desafortunadamente rechazada durante la revisión.
Vea la publicación de Neones a continuación, ¡podría contener información útil!

Por ejemplo, para eliminar todos los archivos *.gz comprometidos accidentalmente en el repositorio git:

$ du -sh .git ==> e.g. 100M $ git filter-branch --index-filter ''git rm --cached --ignore-unmatch *.gz'' HEAD $ git push origin master --force $ rm -rf .git/refs/original/ $ git reflog expire --expire=now --all $ git gc --prune=now $ git gc --aggressive --prune=now

¿Eso todavía no funcionó para mí? (Actualmente estoy en la versión 1.7.6.1 de git)

$ du -sh .git ==> e.g. 100M

No estoy seguro de por qué, ya que solo tenía UNA rama principal. De todos modos, finalmente conseguí que mi repositorio de git realmente se limpiara empujando hacia un nuevo repositorio de git vacío y vacío, por ejemplo

$ git init --bare /path/to/newcleanrepo.git $ git push /path/to/newcleanrepo.git master $ du -sh /path/to/newcleanrepo.git ==> e.g. 5M

(¡sí!)

Luego cloné eso en un nuevo directorio y lo moví sobre su carpeta .git a este. p.ej

$ mv .git ../large_dot_git $ git clone /path/to/newcleanrepo.git ../tmpdir $ mv ../tmpdir/.git . $ du -sh .git ==> e.g. 5M

(si! finalmente limpiado!)

Después de verificar que todo está bien, puede eliminar los directorios ../large_dot_git y ../tmpdir (tal vez dentro de un par de semanas o meses, por si acaso ...)



La forma más sencilla que encontré fue sugerida por leontalbot (como comentario), que es una zyxware.com/articles/4027/… . Creo que vale su propio espacio como respuesta:

(Lo convertí a un script de bash)

#!/bin/bash if [[ $1 == "" ]]; then echo "Usage: $0 FILE_OR_DIR [remote]"; echo "FILE_OR_DIR: the file or directory you want to remove from history" echo "if ''remote'' argument is set, it will also push to remote repository." exit; fi FOLDERNAME_OR_FILENAME=$1; #The important part starts here: ------------------------ git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all rm -rf .git/refs/original/ git reflog expire --expire=now --all git gc --prune=now git gc --aggressive --prune=now if [[ $2 == "remote" ]]; then git push --all --force fi echo "Done."

Todos los créditos van a Annopjohn y a leontalbot por señalarlo.

NOTA

Tenga en cuenta que el script no incluye validaciones, así que asegúrese de no cometer errores y de que tiene una copia de seguridad en caso de que algo salga mal. Funcionó para mí, pero puede que no funcione en tu situación. Úselo con precaución (siga el enlace si desea saber qué está pasando).


La reescritura del historial de Git exige cambiar todos los ID de confirmación afectados, por lo que todos los que estén trabajando en el proyecto deberán eliminar sus copias antiguas del repositorio y hacer un nuevo clon después de que haya limpiado el historial. Mientras más inconvenientes tenga para la gente, más necesitará una buena razón para hacerlo: su archivo superfluo realmente no está causando un problema, pero si solo está trabajando en el proyecto, también puede limpiar el historial de Git si lo desea ¡a!

Para hacerlo lo más fácil posible, recomiendo usar BFG Repo-Cleaner , una alternativa más simple y rápida a git-filter-branch específicamente diseñada para eliminar archivos del historial de Git. Una forma en la que hace su vida más fácil aquí es que en realidad maneja todas las referencias de forma predeterminada (todas las etiquetas, sucursales, etc.) pero también es 10 - 50 veces más rápido.

Debe seguir cuidadosamente los pasos aquí: http://rtyley.github.com/bfg-repo-cleaner/#usage - pero el bit del núcleo es simplemente esto: descargue el archivo BFG jar (requiere Java 6 o superior) y ejecute este comando :

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

Se escaneará todo el historial del repositorio y se eliminará cualquier archivo llamado filename.orig (que no se encuentra en su última confirmación ). ¡Esto es considerablemente más fácil que usar git-filter-branch para hacer lo mismo!

Revelación completa: soy el autor de BFG Repo-Cleaner.


Si es la última confirmación que desea limpiar, probé con la versión 2.14.3 de git (Apple Git-98):

touch empty git init git add empty git commit -m init # 92K .git du -hs .git dd if=/dev/random of=./random bs=1m count=5 git add random git commit -m mistake # 5.1M .git du -hs .git git reset --hard HEAD^ git reflog expire --expire=now --all git gc --prune=now # 92K .git du -hs .git


Si no ha cometido nada desde entonces, simplemente git rm el archivo y git commit --amend .

Si usted tiene

git filter-branch / --index-filter ''git rm --cached --ignore-unmatch path/to/file/filename.orig'' merge-point..HEAD

Pasará por cada cambio de merge-point de merge-point a HEAD , eliminará el nombre de archivo.orig y reescribirá el cambio. El uso de --ignore-unmatch significa que el comando no fallará si, por alguna razón, filename.orig falta en un cambio. Esa es la forma recomendada de la sección de ejemplos en la página de manual de git-filter-branch .

Nota para los usuarios de Windows: la ruta del archivo debe usar barras diagonales


Solo para agregar eso a la solución de Charles Bailey, acabo de usar git rebase -i para eliminar archivos no deseados de una confirmación anterior y funcionó a la perfección. Los pasos:

# Pick your commit with ''e'' $ git rebase -i # Perform as many removes as necessary $ git rm project/code/file.txt # amend the commit $ git commit --amend # continue with rebase $ git rebase --continue


También puedes usar:

git reset HEAD file/path


Por favor, no use esta receta si su situación no es la descrita en la pregunta. Esta receta es para corregir una fusión incorrecta y para reproducir sus buenos compromisos en una combinación fija.

Aunque filter-branch hará lo que quieras, es un comando bastante complejo y probablemente elegiría hacer esto con git rebase . Probablemente sea una preferencia personal. filter-branch puede hacerlo en un solo comando un poco más complejo, mientras que la solución de rebase está realizando las operaciones lógicas equivalentes un paso a la vez.

Prueba la siguiente receta:

# create and check out a temporary branch at the location of the bad merge git checkout -b tmpfix <sha1-of-merge> # remove the incorrectly added file git rm somefile.orig # commit the amended merge git commit --amend # go back to the master branch git checkout master # replant the master branch onto the corrected merge git rebase tmpfix # delete the temporary branch git branch -d tmpfix

(Tenga en cuenta que en realidad no necesita una sucursal temporal, puede hacer esto con un ''HEAD extraído'', pero debe tomar nota del ID de confirmación generado por el paso git commit --amend para suministrar al git rebase comando en lugar de usar el nombre de rama temporal.)


You should probably clone your repository first. Remove your file from all branches history: git filter-branch --tree-filter ''rm -f filename.orig'' -- --all Remove your file just from the current branch: git filter-branch --tree-filter ''rm -f filename.orig'' -- --HEAD Lastly you should run to remove empty commits: git filter-branch -f --prune-empty -- --all