tag - ¿Cómo extraer un subdirectorio git y crear un submódulo?
submodulos git (4)
Checkout git filter-branch .
La sección de Examples
de la página man muestra cómo extraer un subdirectorio en su propio proyecto, conservando todo su historial y descartando el historial de otros archivos / directorios (justo lo que está buscando).
Para reescribir el repositorio para ver si
foodir/
había sido su raíz de proyecto, y descartar el resto de la historia:
git filter-branch --subdirectory-filter foodir -- --all
Por lo tanto, puede, por ejemplo, convertir un subdirectorio de biblioteca en un repositorio propio.
Tenga en cuenta que--
separafilter-branch
opciones de lafilter-branch
opciones de revisión, y--all
para reescribir todas las ramas y etiquetas.
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.
"Proyecto" está GITificado y ha habido muchas confirmaciones en el directorio "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?
Hoy en día hay una manera mucho más fácil de hacerlo que manualmente usando 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 quieres las páginas man y todo
make doc
make install
Uso
Divida un trozo más grande en trozos más pequeños:
# 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 man), lea git-subtree.txt
.
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:
- Crear nuevo repositorio.
- 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.
Sería 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.
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 el archivo / carpetas que desea conservar.
Por ejemplo, tengo un proyecto desde el cual deseo 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
Utiliza git filter-branch --tree-filter
para pasar por cada confirmación, ejecutar el comando y volver a enviar el contenido de los directorios resultantes. 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 solo ejecuta el siguiente script de shell en cada revisión, que deberá modificar por supuesto (para hacer que excluya 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 todos los mensajes de confirmación, incluso si no están relacionados con el archivo restante. El script git-remove-empty-commits , corrige esto ..
git filter-branch --commit-filter ''if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi''
Necesita usar el argumento -f
force ejecutar filter-branch
nuevamente con cualquier cosa en refs/original/
(que básicamente es una copia de seguridad)
Por supuesto, esto nunca será perfecto, por ejemplo, si sus mensajes de confirmación mencionan otros archivos, pero es lo más aproximado que permite una corriente de git (hasta donde yo sé).
Nuevamente, ¡solo ejecute esto en una copia de su repositorio! - pero en resumen, para eliminar todos los archivos excepto "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 "$@"; else git commit-tree "$@"; fi''