2011-09-07 9 views
11

Estoy en el proceso de dividir un antiguo conjunto de aplicaciones que originalmente residía en un único repositorio de Subversion.¿Cómo eliminar todos los archivos en un repositorio de Git que no están en el directorio de trabajo?

Lo he convertido a un repositorio de Git y eliminé lo que no quiero, pero me gustaría reducir el tamaño del repositorio eliminando los datos históricos asociados con los archivos eliminados (el repositorio original mantenerse como referencia, por lo que no es necesario en el nuevo).

Idealmente, lo que me gustaría hacer es ir a través de todo el repositorio y eliminar cualquier archivo o carpeta que no esté presente en el directorio de trabajo, junto con cualquier historial asociado a ellos. Esto me dejaría con los contenidos de HEAD y un historial de commits que afectan esos archivos. Sin embargo, no he encontrado una forma de hacerlo (huérfano HEAD no ayuda, ya que no conserva el historial).

¿Esto es posible? Sé cómo eliminar un solo archivo o carpeta de todo el historial a través de git-filter-branch, pero hay demasiados archivos y carpetas para que esto sea un enfoque práctico ... a menos que haya una forma de filtrar en todos los archivos que no estén en HEAD ?

+0

¿Qué pasa con los archivos que quedó rebautizado en el pasado? Eliminar el historial cuando se produjo el cambio de nombre o mantener el cambio de nombre (y rastrear un nombre de archivo diferente anterior al cambio de nombre) – knittl

+0

Buen punto. Preferiría mantener el historial antes del cambio de nombre, por lo que habría que tener unos pocos archivos adicionales, lo cual está bien. –

Respuesta

3

Lo hice un par de veces: extrae los commits de un solo archivo y crea un nuevo repositorio a partir de ellos. No hace falta algo como esto:

$ c=10; for commit in $(git log --format=%h -- path/to/file|tac); do 
     c=$((c+1)) 
     git format-patch -1 --stdout $commit > $c.patch 
    done 

Esto crea el 11.patch archivos de revisión, 12.patch y así sucesivamente. Luego edito estos parches (usando vim o perl, lo que parezca mejor para el trabajo), eliminando trozos enteros de archivos que no me interesan, y tal vez corrija los nombres también en caso de cambiar el nombre en el encabezado diff hunk.

Yo usaría git am en los parches de un nuevo repositorio de git. Si algo no sale bien, transfiero el nuevo repositorio de git y edito los parches nuevamente y repito el git am.

La razón por la que empiezan a contar desde el 10 es porque soy perezoso para anteponer un 0 a la secuencia de parche y para las confirmaciones más de 99 acabo de empezar a los 99

+0

puede usar '$ (printf"% 02d "$ c) .patch' para anteponer un cero inicial. – jfs

+0

Gracias por mencionar eso. Debo comenzar a usar printf más a menudo a partir de ahora. – holygeek

+0

Gracias ... ¿pero esto no funciona en una base de archivo por archivo? Como dije en mi pregunta, sé cómo hacerlo por archivo, pero hay demasiados archivos para que sea práctico. O tal vez estoy malinterpretando lo que está sucediendo aquí? –

6

He aquí cómo usted puede utilizar git del Filtro rama para deshacerse de todos los archivos que no desea:

  1. obtener una lista de los nombres de archivo que no desea que aparezcan en la historia tanto de los nombres antiguos y los nuevos nombres en caso de cambios de nombre . Por ejemplo ponerlos en un archivo llamado toberemoved.txt

  2. Ejecutar git filter-branch así:

    $ git filter-branch --tree-filter "rm -f `cat toberemoved.txt`" branch1 branch2 ... 
    

Aquí está la página del manual pertinente de git filter-branch:

--tree-filter <command> 
     This is the filter for rewriting the tree and its contents. The 
     argument is evaluated in shell with the working directory set to 
     the root of the checked out tree. The new tree is then used as-is 
     (new files are auto-added, disappeared files are auto-removed - 
     neither .gitignore files nor any other ignore rules HAVE ANY 
     EFFECT!). 

Así que solo asegúrese de que la lista de archivos que desea eliminar esté relacionada con la raíz del árbol desprotegido.

Actualización:

para obtener la lista de los archivos que estaban presentes en el pasado, pero no en el directorio de trabajo actual puede ejecutar el siguiente.Tenga en cuenta que usted tiene que hacer un mayor esfuerzo para mantener la "historia antes de cambiar el nombre" de archivos con otro nombre:

$ git log --raw |awk '/^:/ { if (! printed[$6]) { print $6; printed[$6] = 1 }}'|while read f;do if [ ! -f $f ]; then echo Deleted: $f;fi;done 

Esa $ 6 es el nombre del archivo que se vieron afectados en una muestra de comprometerse en el --raw modo de registro.

Consulte la opción --diff-filter para registrar git si desea saber qué sucedió ([D] elegida, [R] activada, [M] odiada, y así sucesivamente) en cada archivo para cada confirmación.

Tal vez otros puedan decir cómo encontrar el nombre anterior de un archivo rastreado en caso de cambiar el nombre.

+0

Gracias por la nueva respuesta. Cada vez más cerca, creo, no había pensado en usar 'cat' con filter-branch. Sin embargo, el bit que todavía no obtengo es cómo generar la lista de archivos, dado que solo estoy interesado en eliminar los archivos * no * en el directorio de trabajo (y, por lo tanto, no está disponible para la lista). ¿Alguna idea adicional? –

+0

He actualizado la respuesta para incluir el comando y obtener una lista de archivos eliminados. – holygeek

+0

Buena respuesta. Quité 'Deleted:' del oneliner para obtener la lista. Pero al usar esa lista, por alguna razón, la sintaxis de bash en el comando git filter-branch no funcionó. Entonces, en cambio usé 'git filter-branch --tree-filter" cat $ HOME/toberemoved.txt | xargs -I {} rm -f {} "' (Tenga en cuenta que 'toberemoved.txt' necesitaba estar fuera del directorio bajo control de versión. Es posible que esto haya causado el problema con la sintaxis '' cat 'toberemoved.txt'" 'también, pero no lo he comprobado). – jaimedash

1

Ayudando a la segunda respuesta: "Tal vez otros puedan decir cómo encontrar el nombre anterior de un archivo rastreado en caso de cambiar el nombre."

Esto devolverá los archivos en su proyecto y los archivos de los cuales se renombraron.

for file in `git ls-files`; do git log --follow --name-only --pretty=format: $file | sort -n -b | uniq | sed '/^\s*$/d'; done

Puede usarlos para excluir de la lista.

La solución general es:

for file in `git ls-files`; do git log --follow --name-only --pretty=format: $file | sort -n -b | uniq | sed '/^\s*$/d'; done > current.txt

git log --raw |awk '/^:/ { if (! printed[$6]) { print $6; printed[$6] = 1 }}'|while read f;do if [ ! -f $f ]; then echo $f;fi;done | sort > hist.txt

diff --new-line-format="" --unchanged-line-format="" hist.txt current.txt > for_remove.txt

Cuestiones relacionadas