Introducción: tienes 5 soluciones disponibles
El cartel original dice:
Accidentalmente cometí un archivo no deseado ... en mi repositorio hace varias confirmaciones ... Quiero eliminar completamente el archivo del historial del repositorio.
¿Es posible reescribir el historial de cambios de manera que filename.orig
nunca se haya agregado al repositorio en primer lugar?
Hay muchas formas diferentes de eliminar completamente el historial de un archivo de git:
- La modificación se compromete.
- Restablecimientos duros (posiblemente más un rebase).
- Rebase no interactivo.
- Rebases interactivos.
- Filtrando ramas.
En el caso del póster original, enmendar el commit no es realmente una opción en sí misma, ya que luego realizó varios commits adicionales, pero en aras de la integridad, también explicaré cómo hacerlo, para cualquier otra persona que solo quiera para modificar su compromiso anterior.
Tenga en cuenta que todas estas soluciones implican alterar / reescribir el historial / confirmaciones de una manera u otra, por lo que cualquier persona con copias antiguas de las confirmaciones tendrá que hacer un trabajo adicional para volver a sincronizar su historial con el nuevo historial.
Solución 1: compromisos de modificación
Si accidentalmente realizó un cambio (como agregar un archivo) en su confirmación anterior, y no desea que el historial de ese cambio ya exista, 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 una rebase)
Al igual que la solución 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á su rama a la primera confirmación principal anterior .
Sin embargo , si, como el póster original, ha realizado varias confirmaciones después de la confirmación a la que desea deshacer el cambio, aún puede usar restablecimientos duros para modificarlo, pero hacerlo también implica el uso de un rebase. Estos son los pasos que puede seguir para modificar una confirmación más atrás en el historial:
# 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 interactiva
Esto funcionará si solo desea eliminar una confirmación 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: bases interactivas
Esta solución le permitirá lograr lo mismo 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 anterior inmediata, por lo que la solución que elija utilizar dependerá de usted. Los rebases interactivos no son adecuados para rebases de cientos de commits, por razones de rendimiento, por lo que usaría rebases no interactivos o la solución de ramificación de filtro (ver más abajo) en ese tipo de situaciones.
Para comenzar el rebase interactivo, 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 rebobinadas 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 1ª línea, así:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
A continuación, ingrese git rebase --continue
. Si elige eliminar el compromiso por completo, entonces todo lo que necesita hacer (aparte de la verificación, consulte el paso final para esta solución). Si, por otro lado, desea modificar el compromiso, entonces git volverá a aplicar el compromiso y luego pausará el 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 modificar la confirmación, luego continuar con el rebase:
git rm <file>
git commit --amend --no-edit
git rebase --continue
Eso es. Como paso final, tanto si modificó el commit como si lo eliminó por completo, siempre es una buena idea verificar que no se realizaron otros cambios inesperados en su rama al diferirlo con su estado antes del rebase:
git diff master@{1}
Solución 5: Filtrar ramas
Finalmente, esta solución es mejor si desea eliminar 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 raíz. Si, en cambio, solo desea reescribir el rango de confirmación HEAD~5..HEAD
, puede pasarlo como un argumento adicional filter-branch
, como se señala en
esta respuesta :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
Nuevamente, una vez que filter-branch
se completa, 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 de filtro-rama: BFG Repo Cleaner
He oído que la herramienta BFG Repo Cleaner se ejecuta más rápido que git filter-branch
, por lo que es posible que también desee comprobar eso como una opción. Incluso se menciona oficialmente en la documentación de rama de filtro como una alternativa viable:
git-filter-branch le permite realizar reescrituras complejas con scripts 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 The BFG Repo-Cleaner , una alternativa basada en JVM a git-filter-branch, generalmente al menos 10-50x 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 en función de dónde o cuándo se confirmó en su historial. Esta restricción brinda el beneficio de rendimiento central de The BFG, y se adapta bien a la tarea de limpiar los datos incorrectos: no le importa dónde están los datos incorrectos, solo quiere que se vayan .
Por defecto, el BFG aprovecha al máximo las máquinas multinúcleo, limpiando los árboles de archivos de confirmación en paralelo. git-filter-branch limpia los commits secuencialmente (es decir, de una sola hebra), aunque es
posible escribir filtros que incluyan su propio paralelismo en los scripts ejecutados contra cada commit.
Las opciones de comando son mucho más restrictivas que la rama git-filter, y están dedicadas solo a las tareas de eliminar datos no deseados, por ejemplo:--strip-blobs-bigger-than 1M
.
Recursos adicionales
- Pro Git § 6.4 Herramientas Git - Reescribiendo el historial .
- git-filter-branch (1) Página del manual .
- Página del manual de git-commit (1) .
- Página de manual de git-reset (1) .
- Página del manual de git-rebase (1) .
- El BFG Repo Cleaner (ver también esta respuesta del propio creador ).