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.3
por... v1.8.3
).git log rails/somefile
no 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-branch
en el repositorio que desea incluir.git subtree
puede que no hagas lo que piensas! Vea aquí para una solución más completa.Si quieres unirte
project-a
aproject-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-a
en un subdirectorio, puede utilizargit-filter-repo
(filter-branch
se 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-histories
parámetro solo existe desde git> = 2.9. Ver Git - Documentación de combinación de git / --allow-non-related-histororiesActualización : Agregado
--tags
como lo sugiere @jstadler para mantener las etiquetas.fuente
git mv source-dir/ dest/new-source-dir
git merge
paso falla aquí confatal: refusing to merge unrelated histories
;--allow-unrelated-histories
corrige eso como se explica en los documentos .--allow-unrelated-histories
fue 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-histories
Se 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/somefile
no 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-branch
para reescribir los nombres de todo en el segundo repositorio para estar en el subdirectorio donde desea que terminen. Entonces, en lugar defoo.c
,bar.html
tendríasprojb/foo.c
yprojb/bar.html
.Entonces, deberías poder hacer algo como lo siguiente:
El
git pull
hará ungit fetch
seguido 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
gitk
engit
. Junio C Hamano escribe sobre esto aquí: http://www.mail-archive.com/[email protected]/msg03395.htmlfuente
git filter-branch
para 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-subtree
es agradable, pero probablemente no sea el que quieres.Por ejemplo, si
projectA
es 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
projectA
directorio en B. Si ejecutagit log projectA
, verá todas las confirmaciones de A.En mi caso, quería dos subdirectorios,
projectA
yprojectB
. 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
.kdiff3
se 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-plugin
repositorio y unmain-project
repositorio, y quería fingir quemy-plugin
siempre se había desarrollado en elplugins
subdirectorio demain-project
.Básicamente, reescribí la historia del
my-plugin
repositorio para que pareciera que todo el desarrollo tuvo lugar en elplugins/my-plugin
subdirectorio. Luego, agregué el historial de desarrollo demy-plugin
lamain-project
historia y fusioné los dos árboles. Como no había ningúnplugins/my-plugin
directorio ya presente en elmain-project
repositorio, 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-plugin
repositorio, porque vamos a reescribir la historia de este repositorio.Ahora, navegue hasta la raíz del
my-plugin
repositorio, revise su rama principal (probablementemaster
) y ejecute el siguiente comando. Por supuesto, debe sustituirmy-plugin
yplugins
cualesquiera que sean sus nombres reales.Ahora para una explicación.
git filter-branch --tree-filter (...) HEAD
ejecuta 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-branch
comando que falla, dejará algunos archivos en el.git
directorio y la próxima vez que lo intentefilter-branch
se quejará de esto, a menos que proporcione la-f
opciónfilter-branch
.En cuanto al comando real, no tuve mucha suerte
bash
para hacer lo que quería, así que en lugar de eso utilizozsh -c
parazsh
ejecutar un comando. Primero configuro laextended_glob
opción, que es lo que habilita la^(...)
sintaxis en elmv
comando, así como laglob_dots
opción, que me permite seleccionar archivos de puntos (como.gitignore
) con un globo (^(...)
).A continuación, uso el
mkdir -p
comando para crear ambosplugins
yplugins/my-plugin
al 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.git
lamy-plugin
carpeta recién creada . (Es.git
posible 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
mv
comando devolvió un error en la confirmación inicial (ya que no había nada disponible para mover). Por lo tanto, agregué un|| true
para quegit filter-branch
no abortara.La
--all
opción le dicefilter-branch
que reescriba el historial de todas las ramas en el repositorio, y el extra--
es necesario paragit
que lo interprete como parte de la lista de opciones para que las ramas se reescriban, en lugar de ser una opción parafilter-branch
sí mismo.Ahora, navegue a su
main-project
repositorio y revise en qué rama desea fusionarse. Agregue su copia local delmy-plugin
repositorio (con su historial modificado) como un control remotomain-project
con: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-histories
opción no existe. Si está utilizando una de estas versiones, simplemente omita la opción: el mensaje de error que--allow-unrelated-histories
impide 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-branch
comando no funcionó correctamente o que ya había unplugins/my-plugin
directoriomain-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 log
comando anterior . Tenga en cuenta que solomaster
se fusionará la rama . Esto significa que si tiene un trabajo importante en otrasmy-plugin
ramas que desea fusionar en elmain-project
árbol, debe abstenerse de eliminar elmy-plugin
control remoto hasta que haya realizado estas fusiones. Si no lo hace, entonces los commits de esas ramas aún estarán en elmain-project
repositorio, 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-plugin
control remoto utilizando:Ahora puede eliminar de forma segura la copia del
my-plugin
repositorio cuyo historial ha cambiado. En mi caso, también agregué un aviso de desaprobación almy-plugin
repositorio 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.0
yzsh --version 5.2
. Su experiencia puede ser diferente.Referencias
fuente
--allow-unrelated-histories
vienenman 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
--orphan
opció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 unREADME
archivo 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/master
rama encima de la recién creadanew_b_root
. Llamamos a la nueva sucursalb
:Ahora, fusionamos nuestra
b
sucursal enA/master
:Finalmente, puede eliminar las
B
ramas 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$ ]]; then
Si 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/grafts
con 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..head
como 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 log
se 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-histories
pará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