2011-06-21 19 views
64

Me gustaría fusionar un repositorio git remoto en mi repositorio git activo como subdirectorio de él. Me gustaría que el repositorio resultante contenga el historial fusionado de los dos repositorios y también que cada archivo del repositorio fusionado conserve su historial tal como estaba en el repositorio remoto. Intenté usar la estrategia del subárbol como se menciona en How to use the subtree merge strategy, pero después de seguir ese procedimiento, aunque el repositorio resultante contiene de hecho el historial fusionado de los dos repositorios, los archivos individuales que provienen del remoto no han conservado su historial (`git log 'en cualquiera de ellos solo muestra un mensaje "Sucursal fusionada ...").Merge repositorio de git en el subdirectorio

Tampoco quiero usar submódulos porque no quiero que los dos repositorios de git combinados estén separados.

¿Es posible fusionar un repositorio git remoto en otro como un subdirectorio con archivos individuales procedentes del repositorio remoto que conserva su historial?

Muchas gracias por cualquier ayuda.

EDIT: Actualmente estoy probando una solución que usa git filter-branch para reescribir el historial del repositorio fusionado. Parece que funciona, pero necesito probarlo un poco más. Volveré para informar sobre mis hallazgos.

EDIT 2: Espero ser más claro. Doy los comandos exactos que utilicé con la estrategia de subárbol de git, que dan como resultado una aparente pérdida de historial de los archivos del repositorio remoto. Deje que A sea el repositorio git en el que estoy trabajando actualmente y B el repositorio git que me gustaría incorporar a A como subdirectorio de él. Se hizo lo siguiente:

git remote add -f B <url-of-B> 
git merge -s ours --no-commit B/master 
git read-tree --prefix=subdir/Iwant/to/put/B/in/ -u B/master 
git commit -m "Merge B as subdirectory in subdir/Iwant/to/put/B/in." 

Después de estos comandos y entrar en el directorio subdirectorio/Iwant/a/poner/B/en, veo todos los archivos de B, pero git log en cualquiera de ellos muestra sólo el mensaje de confirmación "Fusionar B como subdirectorio en subdir/Iwant/to/put/B/in". Su historial de archivos como está en B se pierde.

Lo que parece para trabajar (ya que soy un principiante en git puedo estar equivocado) es la siguiente:

git remote add -f B <url-of-B> 
git checkout -b B_branch B/master # make a local branch following B's master 
git filter-branch --index-filter \ 
    'git ls-files -s | sed "s-\t\"*-&subdir/Iwant/to/put/B/in/-" | 
     GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ 
       git update-index --index-info && 
     mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD 
git checkout master 
git merge B_branch 

El comando anterior para el filtro se toma la rama del git help filter-branch, en el que yo solo cambió la ruta del subdirectorio.

+0

¿Qué dice 'gitk' sobre la historia? He usado la fusión de subárbol git con éxito en el pasado. ¿Quizás puedas revelar tus comandos exactos? No estoy seguro de que git-filter-branch sea el enfoque correcto. Podría recomendar probar git-fast-export y git-fast-import para sintetizar un nuevo historial. –

+0

Después de hacer el procedimiento del subárbol 'gitk' muestra los dos repos combinados en sus consejos y no relacionados en sus confirmaciones iniciales. (¿Ayudaría si publico capturas de pantalla de la vista de historial de gitk? ¿Puedo?) Lamentablemente, los archivos individuales del repositorio remoto no han conservado su historial si lo hago en el terminal 'git log '. Miro en 'git-fast-export' y' git-fast-import'; Soy muy nuevo en git. Editaré mi pregunta para mostrar exactamente qué comandos usé con el subárbol de git. Muchas gracias por tu respuesta. – christosc

+0

@christosc: su segundo método funcionó de manera hermosa y muy simple, ¡muchas gracias! Solo tuve que cambiar subdir/Iwant/to/put/B/in/y hacerlo un oneliner (porque msysgit en Windows parece no admitir retornos de línea en comandos con \): git filter-branch --index- filtrar 'git ls-files -s | sed "s- \ t \" * - & subdir/Iwant/to/put/B/in/- "| GIT_INDEX_FILE = $ GIT_INDEX_FILE.nuevo git update-index --index-info && mv" $ GIT_INDEX_FILE.new "" $ GIT_INDEX_FILE "'HEAD – gaborous

Respuesta

29

Después de obtener la explicación más completa de lo que está pasando, creo que lo entiendo y, en cualquier caso, en la parte inferior tengo una solución. Específicamente, creo que lo que está sucediendo es que la detección de cambio de nombre está siendo engañada por el subárbol se funden con --prefix. Aquí está mi caso de prueba:

mkdir -p z/a z/b 
cd z/a 
git init 
echo A>A 
git add A 
git commit -m A 
echo AA>>A 
git commit -a -m AA 
cd ../b 
git init 
echo B>B 
git add B 
git commit -m B 
echo BB>>B 
git commit -a -m BB 
git commit -a -m BB 
cd ../a 
git remote add -f B ../b 
git merge -s ours --no-commit B/master 
git read-tree --prefix=bdir -u B/master 
git commit -m "subtree merge B into bdir" 
cd bdir 
echo BBB>>B 
git commit -a -m BBB 

Hacemos los directorios de git ayb con varios compromisos cada uno. Hacemos una fusión de subárbol, y luego hacemos un compromiso final en el nuevo subárbol.

Ejecutando gitk (en z/a) muestra que el historial aparece, podemos verlo. Al ejecutar git log, se muestra que el historial sí aparece. Sin embargo, al mirar un archivo específico tiene un problema: git log bdir/B

Bueno, hay un truco que podemos jugar. Podemos ver el historial previo al cambio de nombre de un archivo específico usando --follow. git log --follow -- B. Esto es bueno, pero no es excelente, ya que no puede vincular el historial de la fusión previa con la fusión posterior.

Intenté jugar con -M y -C, pero no pude lograr que siguiera un archivo específico.

Por lo tanto, la solución, creo, es decirle a git sobre el cambio de nombre que se llevará a cabo como parte de la fusión del subárbol. Desafortunadamente git-read-tree es bastante quisquilloso con las combinaciones de subárboles, así que tenemos que trabajar a través de un directorio temporal, pero eso puede desaparecer antes de comprometernos. Luego, podemos ver la historia completa.

primer lugar, crear un repositorio "A" y hacer que algunos se compromete:

mkdir -p z/a z/b 
cd z/a 
git init 
echo A>A 
git add A 
git commit -m A 
echo AA>>A 
git commit -a -m AA 

En segundo lugar, crear un repositorio "B" y hacen que algunos se compromete:

cd ../b 
git init 
echo B>B 
git add B 
git commit -m B 
echo BB>>B 
git commit -a -m BB 

Y el truco para hacer este trabajo: obligue a Git a reconocer el cambio de nombre creando un subdirectorio y moviendo los contenidos dentro de él.

mkdir bdir 
git mv B bdir 
git commit -a -m bdir-rename 

Volver al repositorio de "A" a buscar y combinar el contenido de "B":

cd ../a 
git remote add -f B ../b 
git merge -s ours --no-commit B/master 
git read-tree --prefix= -u B/master 
git commit -m "subtree merge B into bdir" 

Para demostrar que son ahora fusionaron:

cd bdir 
echo BBB>>B 
git commit -a -m BBB 

Para probar la el historial completo se conserva en una cadena conectada:

git log --follow B 

Obtenemos la historia después de hacer esto, pero el problema es que si realmente conserva el antiguo repositorio "b" y ocasionalmente se fusiona (supongamos que es un repositorio mantenido por terceros), tiene problemas desde ese tercer momento. Parte no habrá hecho el cambio de nombre. Debes tratar de combinar nuevos cambios en tu versión de b con el cambio de nombre y me temo que eso no irá bien. Pero si b se va, tú ganas.

+0

De hecho, eso funciona @Seth! Y no tuve que recurrir a la reescritura de la historia como con filter-branch, lo que lo convierte en un historial algo engañoso (por ejemplo, al visualizar 'git log --stat'). Además, no había notado el modificador '--follow' en la documentación de Git Log; parece muy útil con los cambios de nombre. ¡Muchas gracias por su respuesta tan detallada e informativa! – christosc

+2

Esta respuesta sería mucho más útil si el código de ejemplo se dividiera en líneas legibles en lugar de un único trazo separado por un punto y coma. ;) – jwadsack

+0

Me gustaría fusionar "b" en "a" para mantener su historial completo. ¿Cómo podría hacer eso? – Emerald214

2

¿Has intentado agregar el repositorio adicional como un submódulo git? No fusionará la historia con el repositorio contenedor, de hecho, será un repositorio independiente.

Lo menciono porque usted no tiene.

+1

Gracias por la respuesta Abizern. En realidad, sí quiero los dos repositorios historias para fusionar en una, ya no quiero que se separen más, es por eso que no mencioné los submódulos. – christosc

4

Si realmente quieres unir cosas, busca injertos. También debería usar git rebase --preserve-merges --onto. También hay una opción para mantener la fecha del autor para la información del committer.

+0

@adymitruk Gracias, por su respuesta. Soy realmente nuevo en git, así que investigaré el solución que propones. Intenté 'git filter-branch' y parece funcionar, pero quizás la tuya sea mejor. Lo intentaré – christosc

+0

@adymitruk ¿Puedo usar rebase con dos repositorios que no están interrelacionados como ramas? Me refiero a que los dos repositorios que quiero fusionar no tienen confirmaciones iniciales comunes ... – christosc

+1

si. El "--onto" es lo que quiere –

2

Encontré la siguiente solución viable para mí. Primero voy al proyecto B, creo una nueva rama en la que ya todos los archivos se moverán al nuevo subdirectorio. Luego empujo esta nueva rama al origen. A continuación voy a proyectar A, agregar y buscar la distancia de B, luego de partida de los rama movido, vuelvo al maestro y se fusionan:

# in local copy of project B 
git checkout -b prepare_move 
mkdir subdir 
git mv <files_to_move> subdir/ 
git commit -m 'move files to subdir' 
git push origin prepare_move 

# in local copy of project A 
git remote add -f B_origin <remote-url> 
git checkout -b from_B B_origin/prepare_move 
git checkout master 
git merge from_B 

Si voy al subdirectorio subdir, puedo usar git log --follow y todavía tengo la historia.

No soy un experto en git, por lo que no puedo comentar si esta es una solución particularmente buena o si tiene advertencias, pero hasta ahora parece estar bien.

+0

Parece que la gente está subiendo de tono este enfoque aquí: http://stackoverflow.com/questions/1683531/how -to-import-existing-git-repository-into-another – nacross

35

git-subtree es una secuencia de comandos diseñada exactamente para este caso de fusión de varios repositorios en uno preservando el historial (y/o dividiendo el historial de subárboles, aunque eso parece ser irrelevante para esta pregunta). Se distribuye como parte del árbol de git since release 1.7.11.

Para fusionar un repositorio en la revisión <repo><rev> como subdirectorio <prefix>, utilice git subtree add de la siguiente manera:

git subtree add -P <prefix> <repo> <rev> 

git-subárbol implementa el subtree merge strategy en un fácil de usar de forma más.

+0

¡Agradable! Esto es exactamente lo que necesitaba en una línea. Gracias, el futuro! – iameli

+0

Esta es la solución perfecta para combinar otro repositorio en mi repositorio en una dirección secundaria. – eitch

Cuestiones relacionadas