Pregunta

Accidentalmente, he cometido un archivo no deseado (filename.orig mientras que la resolución de una mezcla) a mi repositorio comete varios años, sin que me dé cuenta hasta ahora.Quiero eliminar completamente el archivo desde el repositorio de la historia.

Es posible reescribir el cambio de la historia que filename.orig nunca fue añadido al repositorio en el primer lugar?

¿Fue útil?

Solución

No utilice esta receta si su situación no es la que se describe en la pregunta. Esta receta es para arreglar una fusión incorrecta y reproducir tus confirmaciones válidas en una fusión fija.

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

Pruebe la siguiente receta:

(Tenga en cuenta que en realidad no necesita una rama temporal, puede hacerlo con un 'HEAD separado', pero debe tomar nota de la identificación de confirmación generada por el paso <=> para suministrar a < => comando en lugar de usar el nombre de rama temporal).

Otros consejos

Introducción: tiene 5 soluciones disponibles

El póster original dice:

  

Accidentalmente cometí un archivo no deseado ... en mi repositorio varias confirmaciones    Hace ... Quiero eliminar completamente el archivo del historial del repositorio.

     

¿Es    posible reescribir el historial de cambios de modo que filename.orig nunca    agregado al repositorio en primer lugar?

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

  1. Modificación de confirmaciones.
  2. Restablecimientos duros (posiblemente más un rebase).
  3. rebase no interactivo.
  4. rebases interactivos.
  5. Filtrando ramas.

En el caso del póster original, modificar la confirmación no es realmente una opción por sí mismo, ya que realizó varios compromisos adicionales después, pero por el bien de manera completa, también explicaré cómo hacerlo, para cualquier otra persona que justifique quiere modificar su compromiso anterior.

Tenga en cuenta que todas estas soluciones implican alterar / reescribir history / commits de una forma a otra, por lo que cualquier persona con copias antiguas de los commits tendrá que hacer trabajo adicional para volver a sincronizar su historial con el nuevo historial.


Solución 1: Enmiendas confirmadas

Si accidentalmente realizó un cambio (como agregar un archivo) en su anterior comprometerse, y ya no desea que exista la historia de ese cambio, 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 una rebase)

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

git reset --hard HEAD^

Ese comando restablecerá su rama al anterior 1 st padre cometer.

Sin embargo , si, como el póster original, ha realizado varias confirmaciones después 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. Aquí están los pasos que puede usar para modificar una confirmación más atrás en el historial:


Solución 3: Rebase no interactiva

Esto funcionará si solo desea eliminar una confirmación del historial por completo:


Solución 4: Bases interactivas

Esta solución le permitirá lograr lo mismo que las soluciones # 2 y # 3, es decir, modificar o eliminar confirmaciones más atrás en el historial que su inmediato compromiso anterior, por lo que la solución que elija utilizar depende de usted. Los rebases interactivos no son adecuados para rebases de cientos de commits, para razones de rendimiento, por lo que usaría rebases no interactivos o la rama de filtro solución (ver más abajo) en ese tipo de situaciones.

Para comenzar el rebase interactivo, use lo siguiente:

Esto hará que git rebobine el historial de confirmación al padre del confirma que quieres 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 & Quot; seleccione & Quot; con " editar " en la línea 1 st , así:

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

A continuación, ingrese git rebase --continue. Si elige eliminar la confirmación 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, entonces git volverá a aplicar la confirmación y luego pausará la nueva versión.

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 rebase:

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

Eso es todo. Como paso final, si modificó el commit o lo eliminó completamente, siempre es una buena idea verificar que no haya otros cambios inesperados fueron hechos a su rama por diffing con su estado antes del rebase:

git diff master@{1}

Solución 5: Filtrar ramas

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

Eso eliminará <file> de todas las confirmaciones, comenzando desde la confirmación raíz. Si en su lugar solo desea reescribir el rango de confirmación HEAD~5..HEAD, entonces puede pasar eso como un argumento adicional a filter-branch, como se señala en esta respuesta :

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

<*>

Alternativa de filtro-rama: BFG Repo Cleaner

He oído que la herramienta BFG Repo Cleaner funciona más rápido que --strip-blobs-bigger-than 1M, por lo que es posible que desee comprobar eso como una opción tambié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 con script de shell   de su historial de Git, pero probablemente no & # 8217; no necesita esta flexibilidad si   usted & # 8217; simplemente está eliminando datos no deseados como archivos grandes o contraseñas.   Para esas operaciones, puede considerar El BFG   Repo-Cleaner , un JVM basado   alternativa a git-filter-branch, típicamente 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 diferente según dónde o cuándo se confirmó dentro de su   historia. Esta restricción proporciona el beneficio de rendimiento central de The   BFG, y se adapta bien a la tarea de limpiar datos incorrectos: usted no & # 8217; t   importa dónde están los datos incorrectos, solo lo quieres gone.

  •   
  • 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   se compromete secuencialmente (es decir, de una sola hebra), aunque es   posible escribir filtros que incluyen su propio paralelismo, en el   scripts ejecutados contra cada confirmación.

  •   
  • Las opciones de comando son muchas   más restrictivo que la rama git-filter, y dedicado solo a   tareas de eliminar datos no deseados, por ejemplo: <=>.

  •   

Recursos adicionales

  1. Pro Git & # 167; 6.4 Herramientas Git - Reescribiendo el historial .
  2. git-filter-branch (1) Página del manual .
  3. git-commit (1) Página del manual .
  4. git-reset (1) Página del manual .
  5. git-rebase (1) Página del manual .
  6. El BFG Repo Cleaner (ver también esta respuesta del creador mismo ).

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

Si tienes

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 a HEAD, eliminará filename.orig y reescribirá el cambio. El uso de --ignore-unmatch significa que el comando no fallará si, por alguna razón, filename.orig no se encuentra 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 usuarios de Windows: la ruta del archivo debe usar barras diagonales

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

Sólo asegúrese de copia de seguridad las copias de los archivos primero.

EDITAR

La edición por parte de Neon tengo por desgracia rechazada durante el proceso de revisión.
Ver Neons post de abajo, puede contener información útil!


E. g.para eliminar todos los *.gz archivos accidentalmente cometidos en el repositorio de 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

Que aún no funciona para mí?(Actualmente estoy en la versión git 1.7.6.1)

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

No sé por qué, ya que yo sólo tenía UNA rama master.De todos modos, por fin tengo mi repositorio git verdaderamente limpiado por empujar a un nuevo vacío y desnudo repositorio de git, 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í!)

Entonces me clon que a un nuevo directorio y se mueven encima es .git carpeta en ésta.por ejemplo,

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

(¡sí!finalmente limpiar!)

Después de verificar que todo está bien, entonces usted puede eliminar la ../large_dot_git y ../tmpdir directorios (tal vez en un par de semanas o un mes a partir de ahora, sólo en caso de...)

Reescribir el 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 haber limpiado el historial. Cuantas más personas molesten, más necesita una buena razón para hacerlo: su archivo superfluo no está causando realmente un problema, pero si solo usted está trabajando en el proyecto, también podría limpiar ¡sube el historial de Git si quieres!

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

Debe seguir cuidadosamente los pasos aquí: http://rtyley.github.com / bfg-repo-cleaner / # use - pero el bit central es solo esto: descargue el BFG jar (requiere Java 6 o superior) y ejecuta este comando:

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

Se analizará todo el historial del repositorio y cualquier archivo llamado filename.orig (que no esté en su última confirmación ) se eliminará. ¡Esto es considerablemente más fácil que usar <=> para hacer lo mismo!

Divulgación completa: soy el autor del BFG Repo-Cleaner.

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

Solo para agregar eso a la solución de Charles Bailey, simplemente utilicé un git rebase -i para eliminar archivos no deseados de un commit anterior y funcionó de maravilla. 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

La forma más sencilla que he encontrado fue sugerido por leontalbot (como comentario), que es un post publicado por Anoopjohn.Creo que vale la pena su propio espacio como una respuesta:

(Me he convertido a un script en 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 va a Annopjohn, y a leontalbot para señalarla.

NOTA

Ser conscientes de que el guión no se incluyen validaciones, así que asegúrate de no cometer errores y que tiene una copia de seguridad en caso de que algo va mal.A mí me funcionó, pero no puede funcionar en su situación.USAR CON PRECAUCIÓN (sigue el enlace si quieres saber lo que está pasando).

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

Lamentablemente, esto no será suficiente para eliminar completamente filename.orig de su repositorio, ya que todavía se puede hacer referencia a él mediante etiquetas, entradas de registro, 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 este sitio web para hacer todo esto en un solo paso.

git forget-blob filename.orig

Si es la última confirmación que desea limpiar, probé con git versión 2.14.3 (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

Esto es para lo que git filter-branch fue diseñado.

También puedes usar:

git reset HEAD file/path

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top