ver repositorio remota ramas rama crear cambiar git merge repository git-subtree

repositorio - git pull



¿Cómo fusionar dos repositorios Git? (21)

Aquí hay dos soluciones posibles:

Submódulos

Copie el repositorio A en un directorio separado en el 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 de acoplamiento flexible, donde el desarrollo en el repositorio A continúa, y la mayor parte del desarrollo es un desarrollo independiente separado en A. Consulte también las páginas de 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.

git remote add -f Bproject /path/to/B git merge -s ours --allow-unrelated-histories --no-commit Bproject/master git read-tree --prefix=dir-B/ -u Bproject/master git commit -m "Merge B project as our subdirectory" git pull -s subtree Bproject master

(Se --allow-unrelated-histories opción --allow-unrelated-histories para Git> = 2.9.0.)

O puede usar la herramienta de subárbol git ( repositorio en GitHub ) de apenwarr (Avery Pennarun), anunciado por ejemplo en su blog. Una nueva alternativa a los submódulos de Git: subárbol de git .

Creo que en su caso (A es ser parte de un proyecto más grande B) la solución correcta sería utilizar la fusión de subárbol .

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 formara parte del proyecto B más grande, que tiene su propio gran repositorio. Ahora me gustaría agregar A como un subdirectorio de B.

¿Cómo fusiono A en B, sin perder el historial de ningún lado?


El enfoque de submódulo es bueno si desea mantener el proyecto por separado. Sin embargo, si realmente desea combinar ambos proyectos en el mismo repositorio, tiene un poco más de trabajo por hacer.

Lo primero sería usar git filter-branch para volver a escribir los nombres de todo lo que se encuentra en el segundo repositorio en el subdirectorio donde desea que terminen. Entonces, en lugar de foo.c , bar.html , tendrías projb/foo.c y projb/bar.html .

Entonces, deberías poder hacer algo como lo siguiente:

git remote add projb [wherever] git pull projb

El git pull hará una git fetch seguida de una git merge . No debe haber conflictos, si el repositorio al que está projb/ todavía no tiene un directorio projb/ .

La búsqueda adicional indica que se hizo algo similar para fusionar gitk en git . Junio ​​C Hamano escribe sobre esto aquí: http://www.mail-archive.com/[email protected]/msg03395.html


En mi caso, tenía un repositorio my-plugin y un repositorio de main-project , y quería fingir que my-plugin siempre se había desarrollado en el subdirectorio de plugins de main-project .

Básicamente, reescribí la historia del repositorio my-plugin para que pareciera que todo el desarrollo tuvo lugar en el subdirectorio plugins/my-plugin . Luego, agregué el historial de desarrollo de my-plugin en el historial del main-project y fusioné los dos árboles. Dado que no había un directorio de plugins/my-plugin ya presente en el repositorio del main-project , esta era una fusión trivial sin conflictos. El repositorio resultante contenía toda la historia de los dos proyectos originales y tenía dos raíces.

TL; DR

$ cp -R my-plugin my-plugin-dirty $ cd my-plugin-dirty $ git filter-branch -f --tree-filter "zsh -c ''setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)''" -- --all $ cd ../main-project $ git checkout master $ git remote add --fetch my-plugin ../my-plugin-dirty $ git merge my-plugin/master --allow-unrelated-histories $ cd .. $ rm -rf my-plugin-dirty

Versión larga

Primero, cree una copia del repositorio my-plugin , ya que vamos a reescribir el historial de este repositorio.

Ahora, navegue a la raíz del repositorio de my-plugin , revise su rama principal (probablemente la master ) y ejecute el siguiente comando. Por supuesto, deberías sustituir my-plugin y plugins cualesquiera que sean tus nombres reales.

$ git filter-branch -f --tree-filter "zsh -c ''setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)''" -- --all

Ahora para una explicación. git filter-branch --tree-filter (...) HEAD ejecuta el comando (...) en cada confirmación que sea accesible desde HEAD . Tenga en cuenta que esto funciona directamente en los datos almacenados para cada confirmación, por lo que no debemos preocuparnos por las nociones de "directorio de trabajo", "índice", "puesta en escena", etc.

Si ejecuta un comando filter-branch que falla, dejará algunos archivos en el directorio .git y la próxima vez que intente filter-branch se quejará de esto, a menos que suministre la opción -f a filter-branch .

En cuanto al comando real, no tuve mucha suerte de que bash hiciera lo que quería, así que en lugar de eso uso zsh -c para hacer que zsh ejecute un comando. Primero configuro la opción extended_glob , que es lo que habilita la sintaxis ^(...) en el comando mv , así como la opción glob_dots , que me permite seleccionar archivos de puntos (como .gitignore ) con un glob ( ^(...) ).

A continuación, uso el comando mkdir -p para crear plugins y plugins/my-plugin al mismo tiempo.

Finalmente, uso la función zsh "negative glob" ^(.git|plugins) .git ^(.git|plugins) para hacer coincidir todos los archivos en el directorio raíz del repositorio, excepto .git y la carpeta my-plugin recién creada. (Excluir .git podría no ser necesario aquí, pero intentar mover un directorio a sí mismo es un error).

En mi repositorio, la confirmación inicial no incluía ningún archivo, por lo que el comando mv devolvió un error en la confirmación inicial (ya que no había nada disponible para mover). Por lo tanto, he añadido un || true || true para que git filter-branch no aborte.

La opción --all le dice a filter-branch que reescriba el historial de todas las sucursales en el repositorio, y el adicional -- es necesario que le diga a git que lo interprete como parte de la lista de opciones para que las ramas se reescriban, en lugar de como una opción Para filter-branch sí mismo.

Ahora, navegue hasta el repositorio de su main-project y verifique la rama en la que desea fusionarse. Agregue su copia local del repositorio my-plugin (con su historial modificado) como un control remoto del main-project con:

$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY

Ahora tendrá dos árboles no relacionados en su historial de compromisos, que puede visualizar bien usando:

$ git log --color --graph --decorate --all

Para fusionarlos, utiliza:

$ git merge my-plugin/master --allow-unrelated-histories

Tenga en cuenta que en Git anterior a 2.9.0, la --allow-unrelated-histories no existe. Si está utilizando una de estas versiones, simplemente omita la opción: el mensaje de error que --allow-unrelated-histories también se agregó en 2.9.0.

Usted no debe tener ningún conflicto de fusión. Si lo hace, probablemente significa que el comando filter-branch no funcionó correctamente o que ya existía un directorio plugins/my-plugin en main-project .

Asegúrese de ingresar un mensaje de confirmación explicativo para cualquier contribuyente futuro que se pregunte qué tipo de piratería estaba haciendo para crear 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 comando de git log anterior. Tenga en cuenta que solo se fusionará la rama master . Esto significa que si tiene un trabajo importante en otras ramas de my-plugin que desea combinar en el árbol del main-project , debe abstenerse de eliminar el control remoto de my-plugin hasta que haya realizado estas combinaciones. Si no lo hace, entonces las confirmaciones de esas sucursales seguirán estando en el repositorio del main-project , pero algunas serán inalcanzables y susceptibles de una posible recolección de basura. (Además, tendrá que referirse a ellos por SHA, porque eliminar un control remoto elimina sus sucursales de seguimiento remoto).

Opcionalmente, después de haber fusionado todo lo que desea evitar de my-plugin , puede quitar el control remoto de my-plugin utilizando:

$ git remote remove my-plugin

Ahora puede eliminar de forma segura la copia del repositorio de my-plugin cuyo historial ha cambiado. En mi caso, también agregué un aviso de desaprobación al repositorio de my-plugin real después de que la fusión se completara y se publicara.

Probado en Mac OS X El Capitan con git --version 2.9.0 y zsh --version 5.2 . Su experiencia puede ser diferente.

Referencias:


He estado tratando de hacer lo mismo durante días, estoy usando git 2.7.2. El subárbol no conserva la historia.

Puede utilizar este método si no va a utilizar el proyecto anterior de nuevo.

Sugeriría que bifurcas B primero y trabajes en la rama.

Aquí están los pasos sin bifurcar:

cd B # You are going to merge A into B, so first move all of B''s files into a sub dir mkdir B # Move all files to B, till there is nothing in the dir but .git and B git mv <files> B git add . git commit -m "Moving content of project B in preparation for merge from A" # Now merge A into B git remote add -f A <A repo url> git merge A/<branch> mkdir A # move all the files into subdir A, excluding .git git mv <files> A git commit -m "Moved A into subdir" # Move B''s files back to root git mv B/* ./ rm -rf B git commit -m "Reset B to original state" git push

Si ahora registra alguno de los archivos en el subdir A, obtendrá el historial completo

git log --follow A/<file>

Este fue el post que me ayudó a hacer esto:

saintgimp.org/2013/01/22/…


He reunido mucha información aquí en , etc., y he logrado armar un script que resuelve el problema para mí.

La advertencia es que solo toma en cuenta la rama de ''desarrollo'' 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 quieres.

El script incluso maneja ramas y etiquetas de características, al cambiarles el nombre en el nuevo proyecto para que sepa de dónde provienen.

#!/bin/bash # ################################################################################ ## Script to merge multiple git repositories into a new repository ## - The new repository will contain a folder for every merged repository ## - The script adds remotes for every project and then merges in every branch ##  and tag. These are renamed to have the origin project name as a prefix ## ## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst> ## - where <new_project> is the name of the new project to create ## - and <my_repo_urls.lst> is a file containing the URLs to the repositories ## which are to be merged on separate lines. ## ## Author: Robert von Burg ## [email protected] ## ## Version: 0.2.0 ## Created: 2015-06-17 ## ################################################################################ # # Disallow using undefined variables shopt -s -o nounset # Script variables declare SCRIPT_NAME="${0##*/}" declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)" declare ROOT_DIR="$PWD" # Detect proper usage if [ "$#" -ne "2" ] ; then echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>" exit 1 fi # Script functions function failed() { echo -e "ERROR: Merging of projects failed:" echo -e "$1" exit 1 } function commit_merge() { current_branch="$(git symbolic-ref HEAD 2>/dev/null)" CHANGES=$(git status | grep "working directory clean") MERGING=$(git status | grep "merging") if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then echo -e "INFO: No commit required." else echo -e "INFO: Committing ${sub_project}..." if ! git commit --quiet -m "[Project] Merged branch ''$1'' of ${sub_project}" ; then failed "Failed to commit merge of branch ''$1'' of ${sub_project} into ${current_branch}" fi fi } ## Script variables PROJECT_NAME="${1}" PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}" REPO_FILE="${2}" REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}" # Make sure the REPO_URL_FILE exists if [ ! -e "${REPO_URL_FILE}" ] ; then echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!" exit 1 fi # Make sure the required directories don''t exist if [ -e "${PROJECT_PATH}" ] ; then echo -e "ERROR: Project ${PROJECT_NAME} already exists!" exit 1 fi # Create the new project echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." echo -e "====================================================" cd ${ROOT_DIR} mkdir ${PROJECT_NAME} cd ${PROJECT_NAME} git init echo "Initial Commit" > initial_commit # Since this is a new repository we need to have at least one commit # thus were we create temporary file, but we delete it again. # Deleting it guarantees we don''t have conflicts later when merging git add initial_commit git commit --quiet -m "[Project] Initial Master Repo Commit" git rm --quiet initial_commit git commit --quiet -m "[Project] Initial Master Repo Commit" echo # Merge all projects into th branches of this project echo -e "INFO: Merging projects into new repository..." echo -e "====================================================" for url in $(cat ${REPO_URL_FILE}) ; do # Extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Project ${sub_project}" echo -e "----------------------------------------------------" # Fetch the project echo -e "INFO: Fetching ${sub_project}..." git remote add "${sub_project}" "${url}" if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then failed "Failed to fetch project ${sub_project}" fi # Add remote branches echo -e "INFO: Creating local branches for ${sub_project}..." while read branch ; do branch_ref=$(echo $branch | tr " " "/t" | cut -f 1) branch_name=$(echo $branch | tr " " "/t" | cut -f 2 | cut -d / -f 3-) echo -e "INFO: Creating branch ${branch_name}..." # Create and checkout new merge branch off of master git checkout --quiet -b "${sub_project}/${branch_name}" master git reset --hard --quiet git clean -d --force --quiet # Merge the project echo -e "INFO: Merging ${sub_project}..." if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then failed "Failed to merge branch ''remotes/${sub_project}/${branch_name}'' from ${sub_project}" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/${branch_name}" # Relocate projects files into own directory if [ "$(ls)" == "${sub_project}" ] ; then echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." else echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." mkdir ${sub_project} for f in $(ls -a) ; do if [[ "$f" == "${sub_project}" ]] || [[ "$f" == "." ]] || [[ "$f" == ".." ]] ; then continue fi git mv -k "$f" "${sub_project}/" done # Commit the moving if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then failed "Failed to commit moving of ${sub_project} files into sub directory" fi fi echo done < <(git ls-remote --heads ${sub_project}) # Checkout master of sub probject if ! git checkout "${sub_project}/master" 2>/dev/null ; then failed "sub_project ${sub_project} is missing master branch!" fi # Copy remote tags echo -e "INFO: Copying tags for ${sub_project}..." while read tag ; do tag_ref=$(echo $tag | tr " " "/t" | cut -f 1) tag_name=$(echo $tag | tr " " "/t" | cut -f 2 | cut -d / -f 3) # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0 tag_name="${tag_name%%^*}" tag_new_name="${sub_project}/${tag_name}" echo -e "INFO: Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..." if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then echo -e "WARN: Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}" fi done < <(git ls-remote --tags ${sub_project}) # Remove the remote to the old project echo -e "INFO: Removing remote ${sub_project}..." git remote rm ${sub_project} echo done # Now merge all project master branches into new master git checkout --quiet master echo -e "INFO: Merging projects master branches into new repository..." echo -e "====================================================" for url in $(cat ${REPO_URL_FILE}) ; do # extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Merging ${sub_project}..." if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then failed "Failed to merge branch ${sub_project}/master into master" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/master" echo done # Done cd ${ROOT_DIR} echo -e "INFO: Done." echo exit 0

También puede obtenerlo en http://paste.ubuntu.com/11732805

Primero cree un archivo con la URL a cada repositorio, por ejemplo:

[email protected]:eitchnet/ch.eitchnet.parent.git [email protected]:eitchnet/ch.eitchnet.utils.git [email protected]:eitchnet/ch.eitchnet.privilege.git

Luego llame a la secuencia de comandos dando un nombre al proyecto y la ruta a la secuencia de comandos:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

El script en sí tiene muchos comentarios que deberían explicar lo que hace.


Sé que pasó mucho tiempo después del hecho, pero no estaba contento con las otras respuestas que encontré aquí, así que escribí esto:

me=$(basename $0) TMP=$(mktemp -d /tmp/$me.XXXXXXXX) echo echo "building new repo in $TMP" echo sleep 1 set -e cd $TMP mkdir new-repo cd new-repo git init cd .. x=0 while [ -n "$1" ]; do repo="$1"; shift git clone "$repo" dirname=$(basename $repo | sed -e ''s//s/-/g'') if [[ $dirname =~ ^git:.*/.git$ ]]; then dirname=$(echo $dirname | sed s/.git$//) fi cd $dirname git remote rm origin git filter-branch --tree-filter / "(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ /;)" cd .. cd new-repo git pull --no-commit ../$dirname [ $x -gt 0 ] && git commit -m "merge made by $me" cd .. x=$(( x + 1 )) done


Seguí perdiendo el historial al usar la combinación, por lo que terminé usando rebase ya que en mi caso los dos repositorios son lo suficientemente diferentes como para no fusionarse en cada confirmación:

git clone git@gitorious/projA.git projA git clone git@gitorious/projB.git projB cd projB git remote add projA ../projA/ git fetch projA git rebase projA/master HEAD

=> resolver conflictos, luego continuar, tantas veces como sea necesario ...

git rebase --continue

Hacer esto lleva a un proyecto que tiene todas las confirmaciones de projA seguido de las confirmaciones de projB


Si ambos repositorios tienen el mismo tipo de archivos (como dos repositorios de Rails para diferentes proyectos), puede obtener datos del repositorio secundario en su repositorio actual:

git fetch git://repository.url/repo.git master:branch_name

y luego fusionarlo al repositorio actual:

git merge --allow-unrelated-histories branch_name

Si su versión de Git es más pequeña que 2.9, elimine --allow-unrelated-histories .

Después de esto, pueden ocurrir conflictos. Puedes resolverlos por ejemplo con git mergetool . kdiff3 se puede usar únicamente con el teclado, por lo que se necesitan 5 archivos de conflicto cuando se lee el código en solo unos minutos.

Recuerda terminar la fusión:

git commit


Si desea colocar los archivos de una rama en el repositorio B en un subárbol del repositorio A y también conservar el historial, siga leyendo. (En el ejemplo a continuación, asumo que queremos que la rama maestra del repositorio B se fusione en la rama maestra del repositorio A).

En el repositorio A, primero haga lo siguiente para que el repositorio B esté disponible:

git remote add B ../B # Add repo B as a new remote. git fetch B

Ahora creamos una rama nueva (con un solo compromiso) 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 se colocaron en un subdirectorio llamado path/to/b-files/ .

git checkout --orphan new_b_root master git rm -rf . # Remove all files. git cherry-pick -n `git rev-list --max-parents=0 B/master` mkdir -p path/to/b-files git mv README path/to/b-files/ git commit --date="$(git log --format=''%ai'' $(git rev-list --max-parents=0 B/master))"

Explicación: la opción --orphan para el comando de pago desprotege los archivos de la rama maestra de A pero no crea ningún compromiso. Podríamos haber seleccionado cualquier confirmación porque a continuación, de todos modos, eliminamos todos los archivos. Luego, sin cometer aún ( -n ), seleccionamos el primer compromiso de la rama maestra de B. (Cherry-pick conserva el mensaje de confirmación original que no parece realizar una verificación directa). Luego creamos el subárbol donde queremos colocar todos los archivos del repositorio B. Luego, debemos mover todos los archivos que se introdujeron en el cereza-pick al subárbol. En el ejemplo anterior, solo hay un archivo README para mover. Luego, confirmamos nuestra confirmación de raíz de B-repo y, al mismo tiempo, también conservamos la marca de tiempo de la confirmación original.

Ahora, crearemos una nueva rama B/master sobre la new_b_root recién creada. Llamamos a la nueva sucursal b :

git checkout -b b B/master git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root

Ahora, fusionamos nuestra rama b en A/master :

git checkout master git merge --allow-unrelated-histories --no-commit b git commit -m ''Merge repo B into repo A.''

Finalmente, puedes eliminar las ramas B remotas y temporales:

git remote remove B git branch -D new_b_root b

El gráfico final tendrá una estructura como esta:


Si desea fusionar project-a en project-b :

cd path/to/project-b git remote add project-a path/to/project-a git fetch project-a --tags git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge git remote remove project-a

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.

Nota: el --allow-unrelated-histories solo existe desde git> = 2.9. Ver Git - git merge Documentation / --allow-unrelated-histories

Actualización : Se --tags como lo sugiere @jstadler para mantener las etiquetas.


Si está intentando simplemente pegar dos repositorios juntos, los submódulos y las combinaciones de subárboles son la herramienta incorrecta para usar porque no conservan todo el historial de archivos (como las personas han notado en otras respuestas). Vea esta respuesta here para ver la manera simple y correcta de hacer esto.


Una sola rama de otro repositorio se puede colocar fácilmente en un subdirectorio que conserva su historial. Por ejemplo:

git subtree add --prefix=rails git://github.com/rails/rails.git master

Esto aparecerá como un solo compromiso donde todos los archivos de la rama maestra de Rails se agregarán al directorio "rails". Sin embargo, el título del compromiso contiene una referencia al árbol de historial antiguo:

Añadir ''rails /'' desde commit <rev>

Donde <rev> es un hash de confirmación SHA-1. Todavía se puede ver la historia, culpar a algunos cambios.

git log <rev> git blame <rev> -- README.md

Tenga en cuenta que no puede ver el prefijo de directorio desde aquí, ya que se trata de una antigua rama real intacta. Debería tratar esto como un compromiso de movimiento de archivos habitual: necesitará un salto adicional al alcanzarlo.

# finishes with all files added at once commit git log rails/README.md # then continue from original tree git log <rev> -- README.md

Hay soluciones más complejas como hacer esto manualmente o reescribir el historial como se describe en otras respuestas.

El comando git-subtree es una parte de git-contrib oficial, algunos administradores de paquetes lo instalan de forma predeterminada (OS X Homebrew). Pero es posible que tenga que instalarlo usted mismo además de git.


git-subtree es bueno, pero probablemente no sea el que quieres.

Por ejemplo, si el projectA es el directorio creado en B, después git subtree ,

git log projectA

listas solo una confirmación: la fusión. Las confirmaciones del proyecto fusionado son para diferentes rutas, por lo que no se muestran.

La respuesta de Greg Hewgill se acerca mucho, aunque en realidad no dice cómo reescribir los caminos.

La solución es sorprendentemente simple.

(1) En A,

PREFIX=projectA #adjust this git filter-branch --index-filter '' git ls-files -s | sed "s,/t,&''"$PREFIX"''/," | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE '' HEAD

Nota: Esto vuelve a escribir el historial, por lo que si pretende continuar usando este repositorio A, es posible que desee clonar (copiar) una copia desechable primero.

(2) Luego en B, corre

git pull path/to/A

Voila! Tiene un directorio projectA en B. Si ejecuta git log projectA , verá todas las confirmaciones de A.

En mi caso, quería dos subdirectorios, projectA y projectB . En ese caso, hice el paso (1) a B también.


Combino proyectos de forma ligeramente manual, lo que me permite evitar tener que lidiar con conflictos de combinación.

En primer lugar, copie en los archivos del otro proyecto como desee.

cp -R myotherproject newdirectory git add newdirectory

siguiente tirón en la historia

git fetch path_or_url_to_other_repo

Dile a Git que se fusione en la historia de la última cosa buscada.

echo ''FETCH_HEAD'' > .git/MERGE_HEAD

ahora cometa como lo harías normalmente

git commit


El comando dado es la mejor solución posible que sugiero.

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master


Fusionando 2 repos

git clone ssh://<project-repo> project1 cd project1 git remote add -f project2 project2 git merge --allow-unrelated-histories project2/master git remote rm project2 delete the ref to avoid errors git update-ref -d refs/remotes/project2/master


Para fusionar una A dentro de B:

1) En el proyecto A

git fast-export --all --date-order > /tmp/ProjectAExport

2) En el proyecto B

git checkout -b projectA git fast-import --force < /tmp/ProjectAExport

En esta rama haz todas las operaciones que necesites hacer y confícalas.

C) Luego de vuelta al maestro y una combinación clásica entre las dos ramas:

git checkout master git merge projectA


Similar a @Smar pero usa rutas del sistema de archivos, establecidas en PRIMARIO y SECUNDARIO:

PRIMARY=~/Code/project1 SECONDARY=~/Code/project2 cd $PRIMARY git remote add test $SECONDARY && git fetch test git merge test/master

Entonces te fusionas manualmente.

(Adaptado del post por Anar Manafov )


Cuando desee fusionar tres o más proyectos en un solo compromiso, siga los pasos descritos en las otras respuestas ( remote add -f, merge). Luego, (suave) restablezca el índice a la cabecera anterior (donde no ocurrió la fusión). Agregue todos los archivos ( git add -A) y confírmelos (mensaje "Combinando los proyectos A, B, C y D en un proyecto). Este es ahora el ID de compromiso del maestro.

Ahora, crea .git/info/graftscon el siguiente contenido:

<commit-id of master> <list of commit ids of all parents>

Ejecutar git filter-branch -- head^..head head^2..head head^3..head. Si tiene más de tres ramas, solo agregue tanto head^n..headcomo tenga ramas. Para actualizar etiquetas, agregue --tag-name-filter cat. No siempre agregue eso, porque esto podría causar una reescritura de algunos confirmaciones. Para obtener más información, consulte la página de manual de filter-branch , busque "injertos".

Ahora, tu último compromiso tiene los padres correctos asociados.


Esta función clonará el repositorio remoto en el directorio de repositorio local, después de fusionar todas las confirmaciones se guardarán, git logse mostrarán las confirmaciones originales y las rutas adecuadas:

function git-add-repo { repo="$1" dir="$(echo "$2" | sed ''s///$//'')" path="$(pwd)" tmp="$(mktemp -d)" remote="$(echo "$tmp" | sed ''s/////g''| sed ''s//./_/g'')" git clone "$repo" "$tmp" cd "$tmp" git filter-branch --index-filter '' git ls-files -s | sed "s,/t,&''"$dir"''/," | GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" '' HEAD cd "$path" git remote add -f "$remote" "file://$tmp/.git" git pull "$remote/master" git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master" git remote remove "$remote" rm -rf "$tmp" }

Cómo utilizar:

cd current/package git-add-repo https://github.com/example/example dir/to/save

Si realiza pequeños cambios, incluso puede mover los archivos / dirs del repositorio fusionado a diferentes rutas, por ejemplo:

repo="https://github.com/example/example" path="$(pwd)" tmp="$(mktemp -d)" remote="$(echo "$tmp" | sed ''s/////g'' | sed ''s//./_/g'')" git clone "$repo" "$tmp" cd "$tmp" GIT_ADD_STORED="" function git-mv-store { from="$(echo "$1" | sed ''s//.///./'')" to="$(echo "$2" | sed ''s//.///./'')" GIT_ADD_STORED+=''s,/t''"$from"'',/t''"$to"'',;'' } # NOTICE! This paths used for example! Use yours instead! git-mv-store ''public/index.php'' ''public/admin.php'' git-mv-store ''public/data'' ''public/x/_data'' git-mv-store ''public/.htaccess'' ''.htaccess'' git-mv-store ''core/config'' ''config/config'' git-mv-store ''core/defines.php'' ''defines/defines.php'' git-mv-store ''README.md'' ''doc/README.md'' git-mv-store ''.gitignore'' ''unneeded/.gitignore'' git filter-branch --index-filter '' git ls-files -s | sed "''"$GIT_ADD_STORED"''" | GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" '' HEAD GIT_ADD_STORED="" cd "$path" git remote add -f "$remote" "file://$tmp/.git" git pull "$remote/master" git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master" git remote remove "$remote" rm -rf "$tmp"

Avisos Las
rutas se reemplazan a través sed, así que asegúrese de que se haya movido en las rutas adecuadas después de la fusión.
El --allow-unrelated-historiesparámetro solo existe desde git> = 2.9.


Tuve un desafío similar, pero en mi caso, habíamos desarrollado una versión del código base en el repositorio A, y luego la clonamos en un nuevo repositorio, repo B, para la nueva versión del producto. Después de corregir algunos errores en el repositorio A, tuvimos que FI los cambios en el repositorio B. Terminé haciendo lo siguiente:

  1. Agregando un control remoto al repositorio B que apunta al repositorio A (agregar git remoto ...)
  2. Tirando de la rama actual (no estábamos usando el maestro para corregir errores) (git pull remoteForRepoA bugFixBranch)
  3. Empujando se fusiona con github

Trabajó un regalo :)