2009-05-28 20 views
105

Comencé un proyecto hace algunos meses y almacené todo dentro de un directorio principal. En mi directorio principal "Proyecto" hay varios subdirectorios que contienen cosas diferentes: Proyecto/papel contiene un documento escrito en LaTeX Proyecto/código fuente/RailsApp contiene mi aplicación de rieles.¿Cómo extraer un subdirectorio git y crear un submódulo?

"Proyecto" está GITificado y ha habido muchas confirmaciones en los directorios "papel" y "RailsApp". Ahora, como me gustaría usar cruisecontrol.rb para mi "RailsApp", me pregunto si hay una forma de hacer un submódulo de "RailsApp" sin perder el historial.

¿Alguna sugerencia?

+2

También una muy buena respuesta: http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository –

Respuesta

3

Si desea transferir un subconjunto de archivos a un nuevo repositorio pero conservar el historial, básicamente terminará con un historial completamente nuevo. La forma en que esto funcionaría es básicamente la siguiente:

  1. Crear nuevo repositorio.
  2. Para cada revisión de su antiguo repositorio, combine los cambios de su módulo en el nuevo repositorio. Esto creará una "copia" de su historial de proyectos existente.

Debe ser algo sencillo automatizar esto si no te importa escribir un script pequeño pero peludo. Directo, sí, pero también doloroso. La gente ha reescrito la historia en Git en el pasado, puedes hacer una búsqueda para eso.

Alternativamente: clone el repositorio y elimine el papel en el clon, elimine la aplicación en el original. Esto tomaría un minuto, está garantizado que funciona, y puedes volver a cosas más importantes que tratar de purificar tu historial de git. Y no se preocupe por el espacio en el disco duro ocupado por las copias redundantes del historial.

35

Pago git filter-branch.

El Examples section de la página man muestra cómo extraer un subdirectorio en su propio proyecto, manteniendo todo su historial y descartando el historial de otros archivos/directorios (justo lo que está buscando).

de reescribir el repositorio a parecer como si foodir/ había sido su raíz del proyecto, y desechar el resto de la historia:

git filter-branch --subdirectory-filter foodir -- --all 

Así se puede, por ejemplo, convertir un subdirectorio biblioteca en un repositorio de su propia .
Tenga en cuenta que -- separa las opciones filter-branch de las opciones de revisión y --all para volver a escribir todas las ramas y etiquetas.

+1

http://linux.die.net/man/1/git-filter -branch – schoetbi

+1

Esto funcionó bien para mí. El único inconveniente que noté fue que el resultado fue una sola rama principal con todas las confirmaciones. – aceofspades

+0

@aceofspades: ¿por qué es eso un inconveniente? – naught101

13

Una forma de hacerlo es la inversa: elimine todo menos el archivo que desea conservar.

Básicamente, haga una copia del repositorio, luego use git filter-branch para eliminar todo menos los archivos/carpetas que desea conservar.

Por ejemplo, tengo un proyecto a partir del cual deseo para extraer el archivo tvnamer.py a un nuevo repositorio:

git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD 

que utiliza git filter-branch --tree-filter que pasar por cada confirmación, ejecute el comando y renovar el contenido de directorios resultante . Esto es extremadamente destructivo (¡así que solo debe hacer esto en una copia de su repositorio!), Y puede tomar un tiempo (aproximadamente 1 minuto en un repositorio con 300 confirmaciones y aproximadamente 20 archivos)

El comando anterior simplemente ejecuta el siguiente script de shell en cada revisión, que habría que modificar, por supuesto, (para que sea excluye su subdirectorio en lugar de tvnamer.py):

for f in *; do 
    if [ $f != "tvnamer.py" ]; then 
     rm -rf $f; 
    fi; 
done 

El mayor problema obvio es que deja a todos los mensajes de confirmación, incluso si no están relacionados con el archivo restante. El guión git-remove-empty-commits, soluciona este ..

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "[email protected]"; else git commit-tree "[email protected]"; fi' 

Es necesario utilizar el argumento de la fuerza -f plazo filter-branch de nuevo con nada en refs/original/ (que básicamente una copia de seguridad)

Por supuesto, esto nunca será perfecto, por ejemplo, si los mensajes de confirmación mencionan otros archivos, pero es lo más aproximado que permite una corriente de git (hasta donde yo sé).

De nuevo, ¡solo ejecute esto en una copia de su repositorio! - pero en resumen, para eliminar todos los archivos, pero "thisismyfilename.txt":

git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD 
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "[email protected]"; else git commit-tree "[email protected]"; fi' 
+4

'git filter-branch' tiene (¿hoy en día?) Una opción incorporada para eliminar commits vacíos, a saber' --prune-empty'. Una mejor guía para 'git filter-branch' está en las respuestas a esta pregunta: http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository – Blaisorblade

108

Hoy en día hay una manera mucho más fácil de hacerlo que de forma manual utilizando git filter-branch: git subtree

instalación

git clone https://github.com/apenwarr/git-subtree.git 

cd git-subtree 
sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree 

O si desea que las páginas de manual y todas

make doc 
make install 

Uso

Dividir una más grande en partes más pequeñas:

# Go into the project root 
cd ~/my-project 

# Create a branch which only contains commits for the children of 'foo' 
git subtree split --prefix=foo --branch=foo-only 

# Remove 'foo' from the project 
git rm -rf ./foo 

# Create a git repo for 'foo' (assuming we already created it on github) 
mkdir foo 
pushd foo 
git init 
git remote add origin [email protected]:my-user/new-project.git 
git pull ../ foo-only 
git push origin -u master 
popd 

# Add 'foo' as a git submodule to `my-project` 
git submodule add [email protected]:my-user/new-project.git foo 

Para obtener documentación detallada (página del manual), por favor, lea git-subtree.txt.

+9

git subtree rocks! –

+3

¿Pero no es el punto de git-subárbol evitar el uso de submódulos?Quiero decir, de hecho eres el autor del subárbol de git (a menos que haya una colisión de apodo), pero parece que el subárbol de git ha cambiado, aunque el comando que muestres parece todavía válido. ¿Estoy obteniendo esto bien? – Blaisorblade

+15

git-subfree ahora es parte de git (si instala contrib) desde 1.7.11 – Jeremy

Cuestiones relacionadas