2011-09-11 14 views
23

Mi situación era, tengo un git repo convertido de SVN a HG a GIT, y quería extraer solo un archivo fuente. También tenía caracteres extraños como aÌ (un desajuste de codificación corrompido Unicode ä) y espacios en los nombres de archivo.Cómo extraer un archivo con historial de commit de un git repo con index-filter & co

Parece que no es particularmente fácil, y esa es la razón por la que responderé mi propia pregunta a pesar de muchas preguntas similares con respecto a git [filtro-índice | subdirectorio-filtro | árbol-filtro], ya que necesitaba usar todo el ¡lograr esto!

Entonces la pregunta es: "¿Cómo puedo extraer un archivo de un repositorio y colocarlo en la raíz del nuevo repositorio?"

+0

¿Por qué quiere extraer una sola fila de la cesión temporal? – svick

+1

Es todo lo que necesito. Y, por cierto, http://stackoverflow.com/questions/5998987/splitting-a-set-of-files-within-a-git-repo-into-their-own-repository-preserving no es un clon de ningún subdirectorio- pregunta filtrante La extracción de archivos requiere tanto --subdirectory-filter step como --index-filter o --tree-filter. – peterhil

+1

O más bien todo lo que quiero, porque haré un paquete del único archivo que proporciona un trie. Quiero usarlo en otros proyectos también, y publicar en Github y tengo algún código en el repositorio que no quiero hacer de código abierto (al menos todavía). – peterhil

Respuesta

10

Primero una nota rápida, que incluso un hechizo como en un comentario en Splitting a set of files within a git repo into their own repository, preserving relevant history

SPELL='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "trie.lisp" | tr "\n" "\0" | xargs -0 git rm --cached -r --ignore-unmatch' 
git filter-branch --prune-empty --index-filter "$SPELL" -- --all 

no ayudará con los archivos denominados como imaging/DrinkkejaI<0300>$'\302\210'.txt_74x2032.gif. La parte aI<0300>$'\302\210' una vez era una sola letra: ä.

Así que con el fin de extraer un único archivo, además de filtrar rama también que tenía que hacer:

git filter-branch -f --subdirectory-filter lisp/source/model HEAD 

Como alternativa, puede utilizar --tree-filtro: (es necesaria la prueba, porque el archivo se encontraba en otro directorio anterior, véase: How can I move a directory in a Git repo for all commits?)

MV_FILTER='test -f source/model/trie.lisp && mv ./source/model/trie.lisp . || echo "Nothing to do."' 
git filter-branch --tree-filter $MV_FILTER HEAD --all 

para ver todos los nombres de un archivo de haber tenido, utilice:

git log --pretty=oneline --follow --name-only git-path/to/file | grep -v ' ' | sort -u 

Como se describe en http://whileimautomaton.net/2010/04/03012432

también sigue los pasos de la tarde:

$ git reset --hard 
$ git gc --aggressive 
$ git prune 
$ git remote rm origin # Otherwise changes will be pushed to where the repo was cloned from 
+3

No estoy seguro de cómo seguir estas instrucciones, el texto de esta respuesta parece plantear varias rutas posibles. No veo ningún procedimiento. – ThorSummoner

+0

Quizás debería ver la documentación de git sobre el comando filter-branch y sobre el historial de reescritura: - http://git-scm.com/docs/git-filter-branch - http://git-scm.com/ book/es/v2/Git-Tools-Rewriting-History – peterhil

8

Tenga en cuenta que las cosas se ponen mucho más fácil si se combina esto con el paso adicional de mover el archivo (s) deseado en una nuevo directorio.

Esto podría ser un caso de uso bastante común (por ejemplo, mover el único archivo deseado al directorio raíz).
lo hice (usando git 1,9) como éste (que se mueve primero el archivo (s), a continuación, eliminar los árboles viejos):

git filter-branch -f --tree-filter 'mkdir -p new_path && git mv -k -f old_path/to/file new_path/' 
git filter-branch -f --prune-empty --index-filter 'git rm -r --cached --ignore-unmatch old_path' 

Usted puede usar incluso fácilmente comodines para los archivos deseados (sin jugar un poco con grep -v).

Creo que esto ('mv' y 'rm') también podría hacerse en una rama de filtro pero no funcionó para mí.

No lo probé con personajes extraños, pero espero que ayude de todos modos. Hacer las cosas más fáciles siempre me parece una buena idea.

Sugerencia:
Esta es una acción que consume tiempo en grandes repositorios. Entonces, si quiere hacer varias acciones (como obtener un grupo de archivos y luego reorganizarlos en 'new_path/subdirs'), es una buena idea hacer la parte 'rm' lo antes posible para obtener un árbol más pequeño y más rápido.

+0

También lo probé en ubuntu 12.04 y git 1.7.x con los siguientes resultados: * el problema de denegación de permiso también aparece en ubuntu * git 1.7.x didn ' Me va bien con los comandos que mencioné anteriormente (ya que solo 1 archivo coincide siempre se renombró al directorio en el que debería moverse. Por lo tanto, recomiendo git 1.9.x que estoy ejecutando en mi máquina de Windows – Roman

+0

volví a trabajar mi publicación porque la mayoría de mis problemas parecen ser causados ​​por mis habilidades bash inexistentes -> usando '&&' en lugar de '|' para combinar comandos ahora – Roman

+0

El primer paso no funciona k para mí en git 2.2.1. No hay cambio en el repositorio. – xixixao

21

Un rápido y más fácil de entender filtro que consigue el mismo efecto:

git filter-branch --index-filter ' 
         git read-tree --empty 
         git reset $GIT_COMMIT -- $your $files $here 
       ' \ 
     -- --all -- $your $files $here 
+0

Esta es la mejor – podarok

+0

Esto funcionó perfectamente para mí. Agregué un argumento '--prune-empty' para eliminar cualquier commit vacío. –

+0

@AaronJensen El '--todos - $ sus $ archivos $ aquí' en la última línea se pasa al' git rev-list' que 'filter-branch' ejecuta, por lo que los commits filter-branch seen ya han sido podados . Eso es mucho más rápido que hacer que filter-branch cargue el índice sin sentido y ejecute el filtro y cree nuevos árboles y una confirmación antes de descartarlo por commits que no tocaron esos archivos. Aún así, no hace daño agregarlo. – jthill

Cuestiones relacionadas