Cree un repositorio de submódulos desde una carpeta y mantenga su historial de confirmaciones de git

111

Tengo una aplicación web que explora otras aplicaciones web de una manera particular. Contiene algunas demostraciones web en una demoscarpeta y una de las demostraciones ahora debería tener su propio repositorio. Me gustaría crear un repositorio separado para esta aplicación de demostración y convertirlo en unsubpaquete submódulo del repositorio principal sin perder su historial de confirmaciones.

¿Es posible mantener el historial de confirmaciones de los archivos en la carpeta de un repositorio y crear un repositorio a partir de él y usarlo como un submódulo ?

GabLeRoux
fuente
He estado buscando cómo mover el directorio 1 del repositorio A de Git al repositorio B de Git. +1 para el enlace al artículo.
Chetabahana
Sí, esto es de hecho muy similar, las soluciones difieren un poco, gracias por compartir esto
GabLeRoux

Respuestas:

191

Solución detallada

Vea la nota al final de esta respuesta (último párrafo) para una alternativa rápida a los submódulos de git usando npm;)

En la siguiente respuesta, sabrá cómo extraer una carpeta de un repositorio y hacer un repositorio git de él y luego incluirlo como un submódulo en lugar de una carpeta.

Inspirado en el artículo de Gerg Bayer Mover archivos de un repositorio de Git a otro, preservando la historia

Al principio, tenemos algo como esto:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

En los pasos siguientes, me referiré a esto someLibcomo <directory 1>.

Al final, tendremos algo como esto:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Cree un nuevo repositorio de git desde una carpeta en otro repositorio

Paso 1

Obtenga una copia nueva del repositorio para dividir.

git clone <git repository A url>
cd <git repository A directory>

Paso 2

La carpeta actual será el nuevo repositorio, así que elimine el control remoto actual.

git remote rm origin

Paso 3

Extraiga el historial de la carpeta deseada y consúltelo

git filter-branch --subdirectory-filter <directory 1> -- --all

Ahora debería tener un repositorio de git con los archivos directory 1en la raíz de su repositorio con todo el historial de confirmaciones relacionado.

Paso 4

¡Cree su repositorio en línea y envíe su nuevo repositorio!

git remote add origin <git repository B url>
git push

Es posible que deba configurar la upstreamrama para su primer impulso

git push --set-upstream origin master

Limpio <git repository A>(opcional, ver comentarios)

Queremos eliminar los rastros (archivos e historial de confirmaciones) de, <git repository B>de <git repository A>modo que el historial de esta carpeta solo esté allí una vez.

Esto se basa en Eliminar datos confidenciales de github.

Vaya a una nueva carpeta y

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Reemplazar <directory 1>por la carpeta que desea eliminar. -rlo hará de forma recursiva dentro del directorio especificado :). Ahora empuja origin/mastercon--force

git push origin master --force

Etapa de jefe (ver nota a continuación)

Cree un submódulo desde <git repository B>en<git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Verifique si todo funcionó como se esperaba y push

git push origin master

Nota

Después de hacer todo esto, me di cuenta de que en mi caso era más apropiado usar npm para administrar mis propias dependencias. Podemos especificar las URL y versiones de git, ver las URL de git package.json como dependencias .

Si lo haces de esta manera, el repositorio que desea utilizar como requisito debe ser un módulo de NPM por lo que debe contener un package.jsonarchivo o tendrá que obtener este error: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

tldr (solución alternativa)

Puede que le resulte más fácil usar npm y administrar dependencias con git urls :

  • Mover carpeta a un nuevo repositorio
  • ejecutar npm initdentro de ambos repositorios
  • ejecuta npm install --save git://github.com/user/project.git#commit-ishdonde quieras que se instalen tus dependencias
GabLeRoux
fuente
39
Debe evitarse el paso "Limpiar <git repositorio A>". Al hacer esto, no puede restaurar / verificar por completo versiones / confirmaciones anteriores de su historial. Debería simplemente git rm la carpeta y agregar el submódulo. Por lo tanto, asegúrese de tener una copia completamente funcional cuando revise las confirmaciones anteriores.
Cybot
¿No debería hacerlo cd someLibantes del paso 2? Dices "La carpeta actual será el nuevo repositorio" pero en realidad no lo será; el nuevo repositorio (submódulo) está dentro de esa carpeta.
Jago
1
confirmando: sí, funciona para más de un submódulo. Muchas gracias por la respuesta detallada. Además, no tenía que usar npm.
Breno Inojosa
2
Agregaría información sobre el refs/original/...que se crea en el paso 3.
Emile Bergeron
6
GitHub hizo un artículo sobre cómo lograr la extracción de una carpeta en un nuevo repositorio: help.github.com/articles/…
jrobichaud
9

La solución de @GabLeRoux aplasta las ramas y las confirmaciones relacionadas.

Una forma sencilla de clonar y mantener todas esas ramas y confirmaciones adicionales:

1 - Asegúrate de tener este alias de git

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - Clone el control remoto, tire de todas las ramas, cambie el control remoto, filtre su directorio, presione

git clone [email protected]:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin [email protected]:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags
oodavid
fuente
3

La solución de GabLeRoux funciona bien, excepto si usa git lfsy tiene archivos grandes en el directorio que desea separar. En ese caso, después del paso 3, todos los archivos grandes seguirán siendo archivos de puntero en lugar de archivos reales. Supongo que probablemente se deba a que el .gitattributesarchivo se eliminó en el proceso de la rama del filtro.

Al darme cuenta de esto, encuentro que la siguiente solución funciona para mí:

cp .gitattributes .git/info/attributes

Copiando .gitattributesqué git lfs usa para rastrear archivos grandes al .git/directorio para evitar ser eliminados.

Cuando termine la rama de filtro, no olvide volver a colocar .gitattributessi aún desea usar git lfs para el nuevo repositorio:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
ls.
fuente