Considere el siguiente escenario:
He desarrollado un pequeño proyecto experimental A en su propio repositorio de Git. Ahora ha madurado, y me gustaría que A sea parte de un proyecto más grande B, que tiene su propio gran repositorio. Ahora me gustaría agregar A como un subdirectorio de B.
¿Cómo fusiono A con B, sin perder el historial en ningún lado?
git
merge
repository
git-subtree
static_rtti
fuente
fuente

Respuestas:
Una sola rama de otro repositorio se puede colocar fácilmente bajo un subdirectorio que conserva su historial. Por ejemplo:
Esto aparecerá como una confirmación única donde todos los archivos de la rama maestra Rails se agregan al directorio "rails". Sin embargo, el título del commit contiene una referencia al antiguo árbol de historia:
¿Dónde
<rev>está un hash SHA-1 commit? Todavía puedes ver la historia, culpar a algunos cambios.Tenga en cuenta que no puede ver el prefijo de directorio desde aquí, ya que esta es una rama antigua real que queda intacta. Debe tratar esto como una confirmación de movimiento de archivo habitual: necesitará un salto adicional al alcanzarlo.
Hay soluciones más complejas como hacer esto manualmente o reescribir el historial como se describe en otras respuestas.
El comando git-subtree es parte de git-contrib oficial, algunos administradores de paquetes lo instalan por defecto (OS X Homebrew). Pero es posible que deba instalarlo usted mismo además de git.
fuente
git co v1.7.11.3por... v1.8.3).git log rails/somefileno se mostrará el historial de confirmaciones de ese archivo, excepto la confirmación de fusión. Como sugirió @artfulrobot, verifique la respuesta de Greg Hewgill . Y es posible que deba usargit filter-branchen el repositorio que desea incluir.git subtreepuede que no hagas lo que piensas! Vea aquí para una solución más completa.Si quieres unirte
project-aaproject-b:Tomado de: git fusionar diferentes repositorios?
Este método funcionó bastante bien para mí, es más corto y, en mi opinión, mucho más limpio.
En caso de que quiera poner
project-aen un subdirectorio, puede utilizargit-filter-repo(filter-branchse desanime ). Ejecute los siguientes comandos antes de los comandos anteriores:Un ejemplo de fusión de 2 grandes repositorios, colocando uno de ellos en un subdirectorio: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Nota: El
--allow-unrelated-historiesparámetro solo existe desde git> = 2.9. Ver Git - Documentación de combinación de git / --allow-non-related-histororiesActualización : Agregado
--tagscomo lo sugiere @jstadler para mantener las etiquetas.fuente
git mv source-dir/ dest/new-source-dirgit mergepaso falla aquí confatal: refusing to merge unrelated histories;--allow-unrelated-historiescorrige eso como se explica en los documentos .--allow-unrelated-historiesfue introducido en git 2.9 . En versiones anteriores era comportamiento predeterminado.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD.Aquí hay dos posibles soluciones:
Submódulos
Copie el repositorio A en un directorio separado en un proyecto más grande B, o (quizás mejor) clone el repositorio A en un subdirectorio en el proyecto B. Luego use el submódulo git para hacer de este repositorio un submódulo de un repositorio B.
Esta es una buena solución para repositorios poco acoplados, donde el desarrollo en el repositorio A continúa, y la mayor parte del desarrollo es un desarrollo independiente separado en A. Vea también las páginas SubmoduleSupport y GitSubmoduleTutorial en Git Wiki.
Fusión de subárbol
Puede fusionar el repositorio A en un subdirectorio de un proyecto B utilizando la estrategia de fusión de subárbol . Esto se describe en Subtree Merging and You por Markus Prinz.
(
--allow-unrelated-historiesSe necesita la opción para Git> = 2.9.0.)O puede usar la herramienta git subtree ( repositorio en GitHub ) de apenwarr (Avery Pennarun), anunciada, por ejemplo, en su blog Una nueva alternativa a los submódulos Git: git subtree .
Creo que en su caso (A es parte de un proyecto más grande B) la solución correcta sería utilizar la combinación de subárbol .
fuente
git log dir-B/somefileno mostrará nada excepto la combinación. Ver la respuesta de Greg Hewgill hace referencia a este importante tema.El enfoque de submódulo es bueno si desea mantener el proyecto por separado. Sin embargo, si realmente desea fusionar ambos proyectos en el mismo repositorio, entonces tiene que trabajar un poco más.
Lo primero sería usar
git filter-branchpara reescribir los nombres de todo en el segundo repositorio para estar en el subdirectorio donde desea que terminen. Entonces, en lugar defoo.c,bar.htmltendríasprojb/foo.cyprojb/bar.html.Entonces, deberías poder hacer algo como lo siguiente:
El
git pullhará ungit fetchseguido de ungit merge. No debería haber conflictos, si el repositorio al que está llegando todavía no tiene unprojb/directorio.Una búsqueda adicional indica que algo similar se hizo para la fusión
gitkengit. Junio C Hamano escribe sobre esto aquí: http://www.mail-archive.com/[email protected]/msg03395.htmlfuente
git filter-branchpara lograr esto. En la página de manual dice lo contrario: hacer que subdir / se convierta en la raíz, pero no al revés.git-subtreees agradable, pero probablemente no sea el que quieres.Por ejemplo, si
projectAes el directorio creado en B, despuésgit subtree,enumera solo una confirmación: la fusión. Los commits del proyecto fusionado son para diferentes caminos, por lo que no se muestran.
La respuesta de Greg Hewgill se acerca más, aunque en realidad no dice cómo reescribir los caminos.
La solución es sorprendentemente simple.
(1) En A,
Nota: Esto reescribe el historial, por lo que si tiene la intención de continuar usando este repositorio A, es posible que desee clonar (copiar) primero una copia desechable.
Nota Bene: debe modificar la secuencia de comandos de sustitución dentro del comando sed en el caso de que utilice caracteres no ascii (o caracteres blancos) en los nombres de archivo o en la ruta. En ese caso, la ubicación del archivo dentro de un registro producido por "ls-files -s" comienza con comillas.
(2) Luego en B, corre
Voila! Tiene un
projectAdirectorio en B. Si ejecutagit log projectA, verá todas las confirmaciones de A.En mi caso, quería dos subdirectorios,
projectAyprojectB. En ese caso, hice el paso (1) a B también.fuente
"$GIT_INDEX_FILE"debe ser citado (dos veces), de lo contrario su método fallará si, por ejemplo, la ruta contiene espacios.Ctrl-V <tab>Si ambos repositorios tienen el mismo tipo de archivos (como dos repositorios Rails para diferentes proyectos), puede buscar datos del repositorio secundario en su repositorio actual:
y luego fusionarlo con el repositorio actual:
Si su versión de Git es menor a 2.9, elimínela
--allow-unrelated-histories.Después de esto, pueden ocurrir conflictos. Puede resolverlos, por ejemplo, con
git mergetool.kdiff3se puede usar únicamente con el teclado, por lo que se requieren 5 archivos de conflicto al leer el código solo unos minutos.Recuerda terminar la fusión:
fuente
Seguí perdiendo el historial al usar la combinación, así que terminé usando rebase ya que en mi caso los dos repositorios son lo suficientemente diferentes como para no terminar fusionándose en cada confirmación:
=> resolver conflictos, luego continuar, tantas veces como sea necesario ...
Hacer esto lleva a un proyecto que tiene todos los commits de projA seguidos de commits de projB
fuente
En mi caso, tenía un
my-pluginrepositorio y unmain-projectrepositorio, y quería fingir quemy-pluginsiempre se había desarrollado en elpluginssubdirectorio demain-project.Básicamente, reescribí la historia del
my-pluginrepositorio para que pareciera que todo el desarrollo tuvo lugar en elplugins/my-pluginsubdirectorio. Luego, agregué el historial de desarrollo demy-pluginlamain-projecthistoria y fusioné los dos árboles. Como no había ningúnplugins/my-plugindirectorio ya presente en elmain-projectrepositorio, esta fue una fusión trivial sin conflictos. El repositorio resultante contenía toda la historia de ambos proyectos originales y tenía dos raíces.TL; DR
Versión larga
Primero, cree una copia del
my-pluginrepositorio, porque vamos a reescribir la historia de este repositorio.Ahora, navegue hasta la raíz del
my-pluginrepositorio, revise su rama principal (probablementemaster) y ejecute el siguiente comando. Por supuesto, debe sustituirmy-pluginypluginscualesquiera que sean sus nombres reales.Ahora para una explicación.
git filter-branch --tree-filter (...) HEADejecuta el(...)comando en cada confirmación a la que se puede accederHEAD. Tenga en cuenta que esto opera directamente en los datos almacenados para cada confirmación, por lo que no tenemos que preocuparnos por las nociones de "directorio de trabajo", "índice", "puesta en escena", etc.Si ejecuta un
filter-branchcomando que falla, dejará algunos archivos en el.gitdirectorio y la próxima vez que lo intentefilter-branchse quejará de esto, a menos que proporcione la-fopciónfilter-branch.En cuanto al comando real, no tuve mucha suerte
bashpara hacer lo que quería, así que en lugar de eso utilizozsh -cparazshejecutar un comando. Primero configuro laextended_globopción, que es lo que habilita la^(...)sintaxis en elmvcomando, así como laglob_dotsopción, que me permite seleccionar archivos de puntos (como.gitignore) con un globo (^(...)).A continuación, uso el
mkdir -pcomando para crear ambospluginsyplugins/my-pluginal mismo tiempo.Finalmente, utilizo la función
zsh"glob negativo"^(.git|plugins)para hacer coincidir todos los archivos en el directorio raíz del repositorio, excepto para.gitlamy-plugincarpeta recién creada . (Es.gitposible que no sea necesario excluir aquí, pero intentar mover un directorio en sí mismo es un error).En mi repositorio, la confirmación inicial no incluía ningún archivo, por lo que el
mvcomando devolvió un error en la confirmación inicial (ya que no había nada disponible para mover). Por lo tanto, agregué un|| truepara quegit filter-branchno abortara.La
--allopción le dicefilter-branchque reescriba el historial de todas las ramas en el repositorio, y el extra--es necesario paragitque lo interprete como parte de la lista de opciones para que las ramas se reescriban, en lugar de ser una opción parafilter-branchsí mismo.Ahora, navegue a su
main-projectrepositorio y revise en qué rama desea fusionarse. Agregue su copia local delmy-pluginrepositorio (con su historial modificado) como un control remotomain-projectcon:Ahora tendrá dos árboles no relacionados en su historial de confirmación, que puede visualizar muy bien usando:
Para fusionarlos, use:
Tenga en cuenta que en Git anterior a 2.9.0, la
--allow-unrelated-historiesopción no existe. Si está utilizando una de estas versiones, simplemente omita la opción: el mensaje de error que--allow-unrelated-historiesimpide también se agregó en 2.9.0.No debe tener ningún conflicto de fusión. Si lo hace, probablemente significa que el
filter-branchcomando no funcionó correctamente o que ya había unplugins/my-plugindirectoriomain-project.Asegúrate de ingresar un mensaje de confirmación explicativo para cualquier contribuyente futuro que se pregunte qué hacker está sucediendo para hacer un repositorio con dos raíces.
Puede visualizar el nuevo gráfico de confirmación, que debe tener dos confirmaciones de raíz, utilizando el
git logcomando anterior . Tenga en cuenta que solomasterse fusionará la rama . Esto significa que si tiene un trabajo importante en otrasmy-pluginramas que desea fusionar en elmain-projectárbol, debe abstenerse de eliminar elmy-plugincontrol remoto hasta que haya realizado estas fusiones. Si no lo hace, entonces los commits de esas ramas aún estarán en elmain-projectrepositorio, pero algunos serán inalcanzables y susceptibles a una eventual recolección de basura. (Además, tendrá que referirse a ellos por SHA, porque al eliminar un control remoto se eliminan sus ramas de seguimiento remoto).Opcionalmente, después de haber fusionado todo lo que desea evitar
my-plugin, puede eliminar elmy-plugincontrol remoto utilizando:Ahora puede eliminar de forma segura la copia del
my-pluginrepositorio cuyo historial ha cambiado. En mi caso, también agregué un aviso de desaprobación almy-pluginrepositorio real después de que la fusión se completó y se empujó.Probado en Mac OS X El Capitan con
git --version 2.9.0yzsh --version 5.2. Su experiencia puede ser diferente.Referencias
fuente
--allow-unrelated-historiesvienenman git-merge. Por defecto, el comando git merge se niega a fusionar historias que no comparten un antepasado común. Esta opción se puede utilizar para anular esta seguridad al fusionar historias de dos proyectos que comenzaron sus vidas de forma independiente. Como es una ocasión muy rara, no existe ninguna variable de configuración para habilitar esto de manera predeterminada y no se agregará.git version 2.7.2.windows.1?He estado tratando de hacer lo mismo durante días, estoy usando git 2.7.2. Subtree no conserva la historia.
Puede usar este método si no volverá a usar el proyecto anterior.
Te sugiero que ramifiques B primero y trabajes en la rama.
Estos son los pasos sin ramificación:
Si ahora registra alguno de los archivos en el subdirectorio A, obtendrá el historial completo
Esta fue la publicación que me ayudó a hacer esto:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
fuente
Si desea colocar los archivos de una rama en el repositorio B en un subárbol del repositorio A y también preservar el historial, siga leyendo. (En el ejemplo a continuación, supongo que queremos fusionar la rama maestra del repo B en la rama maestra del repo A).
En el repositorio A, primero haga lo siguiente para que el repositorio B esté disponible:
Ahora creamos una nueva sucursal (con solo una confirmación) en el repositorio A que llamamos
new_b_root. La confirmación resultante tendrá los archivos que se confirmaron en la primera confirmación de la rama maestra del repositorio B pero que se colocaron en un subdirectorio llamadopath/to/b-files/.Explicación: La
--orphanopción para el comando de pago extrae los archivos de la rama maestra de A pero no crea ninguna confirmación. Podríamos haber seleccionado cualquier confirmación porque de todos modos borramos todos los archivos. Luego, sin comprometernos aún (-n), seleccionamos la primera confirmación de la rama maestra de B. (La selección de cereza conserva el mensaje de confirmación original que no parece hacer un pago directo). Luego creamos el subárbol donde queremos colocar todos los archivos del repositorio B. Luego tenemos que mover todos los archivos que se introdujeron en el selección de cereza al subárbol. En el ejemplo anterior, solo hay unREADMEarchivo para mover. Luego confirmamos nuestra confirmación de raíz B-repo y, al mismo tiempo, también conservamos la marca de tiempo de la confirmación original.Ahora, crearemos una nueva
B/masterrama encima de la recién creadanew_b_root. Llamamos a la nueva sucursalb:Ahora, fusionamos nuestra
bsucursal enA/master:Finalmente, puede eliminar las
Bramas remotas y temporales:El gráfico final tendrá una estructura como esta:
fuente
He reunido mucha información aquí sobre Stack OverFlow, etc., y he logrado armar un script que resuelve el problema para mí.
La advertencia es que solo tiene en cuenta la rama 'desarrollar' de cada repositorio y la fusiona en un directorio separado en un repositorio completamente nuevo.
Las etiquetas y otras ramas se ignoran; esto podría no ser lo que desea.
El script incluso maneja ramas y etiquetas de características, renombrándolas en el nuevo proyecto para que sepa de dónde provienen.
También puede obtenerlo en http://paste.ubuntu.com/11732805
Primero cree un archivo con la URL a cada repositorio, por ejemplo:
Luego llame al script dando un nombre del proyecto y la ruta al script:
El script en sí tiene muchos comentarios que deberían explicar lo que hace.
fuente
Sé que es mucho después del hecho, pero no estaba contento con las otras respuestas que encontré aquí, así que escribí esto:
fuente
if [[ $dirname =~ ^.*\.git$ ]]; thenSi está tratando de pegar simplemente dos repositorios juntos, los submódulos y las fusiones de subárbol son la herramienta incorrecta para usar porque no conservan todo el historial de archivos (como la gente ha notado en otras respuestas). Vea esta respuesta aquí para ver la forma simple y correcta de hacer esto.
fuente
Tuve un desafío similar, pero en mi caso, desarrollamos una versión de la base de código en el repositorio A, luego la clonamos en un nuevo repositorio, repositorio B, para la nueva versión del producto. Después de corregir algunos errores en el repositorio A, necesitábamos FI los cambios en el repositorio B. Terminamos haciendo lo siguiente:
Trabajó un placer :)
fuente
Similar a @Smar pero usa rutas de sistema de archivos, establecidas en PRIMARIO y SECUNDARIO:
Luego se fusionan manualmente.
(Adaptado de la publicación de Anar Manafov )
fuente
Fusionando 2 repositorios
fuente
Cuando desee fusionar tres o más proyectos en una sola confirmación, siga los pasos que se describen en las otras respuestas (
remote add -f,merge). Luego, (suave) restablece el índice a la cabeza anterior (donde no ocurrió fusión). Agregar todos los archivos (git add -A) y confírmelos (mensaje "Fusionar proyectos A, B, C y D en un proyecto). Este es ahora el id de confirmación del maestro.Ahora, cree
.git/info/graftscon el siguiente contenido:Ejecutar
git filter-branch -- head^..head head^2..head head^3..head. Si tiene más de tres ramas, solo agregue tantohead^n..headcomo tenga ramas. Para actualizar etiquetas, agregue--tag-name-filter cat. No siempre agregue eso, porque esto podría causar una reescritura de algunas confirmaciones. Para más detalles, vea la página del manual de filter-branch , busque "grafts".Ahora, su último compromiso tiene los padres correctos asociados.
fuente
Para fusionar una A dentro de B:
1) En el proyecto A
2) En el proyecto B
En esta rama, haga todas las operaciones que necesita hacer y comprométalas.
C) Luego de vuelta al maestro y una fusión clásica entre las dos ramas:
fuente
Esta función clonará el repositorio remoto en el directorio de repositorio local, después de fusionar se guardarán todas las confirmaciones,
git logse mostrarán las confirmaciones originales y las rutas adecuadas:Cómo utilizar:
Si realiza pequeños cambios, incluso puede mover archivos / directorios de repositorio fusionado a diferentes rutas, por ejemplo:
Notices
Paths reemplaza via
sed, así que asegúrese de que se movió en las rutas adecuadas después de la fusión.El
--allow-unrelated-historiesparámetro solo existe desde git> = 2.9.fuente
El comando dado es la mejor solución posible, sugiero.
fuente
Fusiono proyectos ligeramente de forma manual, lo que me permite evitar tener que lidiar con conflictos de fusión.
primero, copie los archivos del otro proyecto como desee.
siguiente tirón en la historia
dile a git que se fusione en la historia de la última cosa traída
ahora se compromete sin embargo normalmente lo haría
fuente
Quería mover un proyecto pequeño a un subdirectorio de uno más grande. Como mi pequeño proyecto no tenía muchos commits, solía hacerlo
git format-patch --output-directory /path/to/patch-dir. Luego, en el proyecto más grande, solíagit am --directory=dir/in/project /path/to/patch-dir/*.Esto se siente mucho menos aterrador y mucho más limpio que una rama de filtro. Por supuesto, puede no ser aplicable a todos los casos.
fuente