¿Cómo se fusionan dos repositorios Git?


Considere el siguiente escenario:

He desarrollado un pequeño proyecto experimental A en su propio repositorio Git. Ahora ha madurado, y me gustaría A ser parte de un proyecto más grande B, que tiene su propio gran repositorio. Ahora me gustaría añadir A como subdirectorio de B.

¿Cómo puedo fusionar A en B, sin perder la historia en ningún lado?

Author: static_rtti, 2009-09-15

21 answers

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

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

Esto aparecerá como un solo commit donde todos los archivos de Rails master branch se añaden al directorio "rails". Sin embargo, el título del commit contiene una referencia al viejo árbol de la historia:

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

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

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

Tenga en cuenta que no puede ver el prefijo del directorio desde aquí, ya que esta es una rama antigua real que queda intacta. Deberías tratar esto como un commit de movimiento de archivo habitual: necesitarás un salto extra cuando lo alcances.

# 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 la historia como se describe en otras respuestas.

El comando git-subárbol es parte del git-contrib oficial, algunos gestores de paquetes lo instalan por defecto (OS X Homebrew). Pero es posible que tenga que instalarlo usted mismo además de git.

 331
Author: Simon Perepelitsa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 10:44:36

Si quieres fusionar project-a en project-b:

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

Tomado de: git merge 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 parámetro --allow-unrelated-histories solo existe desde git >= 2.9. Ver Git-git merge Documentation / allow allow-unrelated-histories

 1350
Author: Andresch Serj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-19 13:34:12

Aquí hay dos soluciones posibles:

Submódulos

Copie el repositorio A en un directorio separado en un proyecto B más grande, o (quizás mejor) clone el repositorio A en un subdirectorio en el proyecto B. Luego use el submódulo de git para hacer que este repositorio A 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 ver también SubmoduleSupporty GitSubmoduleTutorial páginas en Git Wiki.

Subárbol merge

Puede fusionar el repositorio A en un subdirectorio de un proyecto B utilizando el fusión de subárbol estrategia. Esto se describe en Fusión de Subárbol y Usted 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 necesita la opción --allow-unrelated-histories para Git >= 2.9.0.)

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


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

 585
Author: Jakub Narębski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-03 12:17:50

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, entonces tiene un poco más de trabajo por hacer.

Lo primero sería usar git filter-branch para reescribir los nombres de todo en el segundo repositorio para estar en el subdirectorio donde te gustaría que terminaran. Así que en lugar de foo.c, bar.html, tendrías projb/foo.c y projb/bar.html.

Entonces, usted debe ser capaz de hacer algo como el siguiente:

git remote add projb [wherever]
git pull projb

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

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

 189
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-15 08:38:49

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

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

git log projectA

Lists only one commit: the merge. Las confirmaciones del proyecto fusionado son para rutas diferentes, por lo que no aparecen.

La respuesta de Greg Hewgill se acerca más, aunque en realidad no dice cómo reescribir las rutas.


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 reescribe la historia, por lo que si tiene la intención de continuar usando este repositorio A, es posible que desee clonar (copiar) una copia desechable de la misma primero.

(2) Luego en B, ejecute

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 paso (1) a B también.

 63
Author: Paul Draper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-01 15:06:06

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

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

Y luego fusionarlo con el repositorio actual:

git merge --allow-unrelated-histories branch_name

Si tu versión de Git es menor que 2.9, elimina --allow-unrelated-histories.

Después de esto, pueden ocurrir conflictos. Puede resolverlos, por ejemplo, con git mergetool. kdiff3 se puede utilizar únicamente con el teclado, por lo que 5 archivo de conflicto toma al leer el código solo unos pocos minuto.

Recuerda terminar la fusión:

git commit
 41
Author: Smar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-19 13:05:55

Seguí perdiendo el historial al usar merge, 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:

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 que un proyecto tenga todas las confirmaciones de ProjA seguidas de confirmaciones de ProjB

 22
Author: Calahad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-27 03:09:17

En mi caso, tenía un repositorio my-plugin y un repositorio main-project, y quería pretender que my-plugin siempre se había desarrollado en el subdirectorio 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é la historia de desarrollo de my-plugin en la historia de main-project, y fusioné los dos árboles juntos. Dado que no había un directorio plugins/my-plugin ya presente en el repositorio main-project, esto 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

$ 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|my-plugin) 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, crea una copia del repositorio my-plugin, porque vamos a reescribir el historial de este repositorio.

Ahora, navega a la raíz del repositorio my-plugin, echa un vistazo a tu rama principal (probablemente master), y ejecuta el siguiente comando. Por supuesto, usted debe sustituir my-plugin y plugins sean cuales sean sus nombres reales.

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

Ahora para una explicación. git filter-branch --tree-filter (...) HEAD ejecuta el comando (...) en cada commit que es accesible desde HEAD. 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", "preparación", 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 proporcione la opción -f a filter-branch.

En cuanto al comando real, no tuve mucha suerte de conseguir bash hacer lo que quería, así que en su lugar uso zsh -c para hacer zsh ejecutar un comando. Primero establezco 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, utilizo el comando mkdir -p para crear plugins y plugins/my-plugin al mismo tiempo tiempo.

Finalmente, utilizo la característica zsh "glob negativo" ^(.git|my-plugin) para hacer coincidir todos los archivos en el directorio raíz del repositorio, excepto .git y la carpeta recién creada my-plugin. (Excluir .git podría no ser necesario aquí, pero intentar mover un directorio dentro de 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 para que git filter-branch no abortara.

La opción --all le dice a filter-branch que reescriba el historial de todas las ramas en el repositorio, y el extra -- es necesario decirle 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.

Ahora, navega a tu repositorio main-project y echa un vistazo a cualquier rama en la que quieras fusionar. Agregue su copia local del repositorio my-plugin (con su historial modificado) como un control remoto de main-project con:

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

Ahora tendrá dos árboles no relacionados en su historial de confirmaciones, que puede visualizar muy bien usando:{[76]]}

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

Para fusionarlos, use:

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

Tenga en cuenta que en Git pre-2.9.0, la opción --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 previene fue también agregado en 2.9.0.

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

, asegúrese de que introduce una explicación del mensaje de confirmación para los futuros contribuyentes preguntándose qué trucos iba a hacer un repositorio con dos raíces.

Puede visualizar el nuevo gráfico de commits, que debería tener dos commits root, usando el comando anterior git log. Tenga en cuenta que solo la rama master se fusionará. Esto significa que si usted tiene importante trabaje en otras ramas my-plugin que desee fusionar en el árbol main-project, debe abstenerse de eliminar el control remoto my-plugin hasta que haya realizado estas fusiones. Si no lo hace, entonces las confirmaciones de esas ramas todavía estarán en el repositorio main-project, pero algunas serán inalcanzables y susceptibles a una eventual recolección de basura. (También, usted tendrá que referirse a ellos por SHA, porque la eliminación de un control remoto elimina sus ramas de seguimiento remoto.)

Opcionalmente, después de haber fusionado todo lo que si desea guardar desde my-plugin, puede eliminar el control remoto my-plugin usando:

$ git remote remove my-plugin

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


Probado en Mac OS X El Capitan con git --version 2.9.0 y zsh --version 5.2. Su kilometraje puede variar.

Referencias:

 14
Author: Radon Rosborough,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:02

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

Puede usar este método si no volverá a usar el proyecto anterior.

Le sugeriría que primero ramifique B y trabaje en la rama.

Aquí están los pasos sin ramificación:

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 cualquiera de los archivos en el sub-sub a obtendrá el historial completo

git log --follow A/<file>

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

Http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/

 8
Author: Rian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-10 12:51:08

Sé que es mucho 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
 6
Author: jettero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-07 13:38:36

He reunido mucha información aquí sobre Stack OverFlow, etc., y han logrado armar un guión que resuelve el problema para mí.

La advertencia es que solo tiene en cuenta la rama 'develop' 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 de características y etiquetas, renombrándolas en el nuevo proyecto para sabes de dónde vienen.

#!/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 puedes obtenerlo de http://paste.ubuntu.com/11732805

Primero cree un archivo con la URL de 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 al script dando un nombre del proyecto y la ruta al script:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

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

 6
Author: eitch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-03 12:21:22

Si estás intentando simplemente unir dos repositorios, las fusiones de submódulos y subárboles son la herramienta incorrecta porque no conservan todo el historial de archivos (como la gente ha observado en otras respuestas). Vea esta respuesta aquí para la manera simple y correcta de hacer esto.

 5
Author: Eric Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:51

Si desea poner los archivos de una rama en repo B en a subárbol de repo A y también preservar la historia, seguir leyendo. (En el ejemplo a continuación, estoy asumiendo que queremos que la rama master de repo B se fusione con la rama master de repo A.)

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

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

Ahora creamos una nueva rama (con solo un commit) en repo A que llamamos new_b_root. La confirmación resultante tendrá los archivos que fueron confirmados en la primera confirmación de la rama maestra de repo B pero que se pusieron 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 del comando checkout comprueba los archivos de la rama maestra de A pero no crea ninguna confirmación. Podríamos haber seleccionado cualquier commit porque a continuación borramos todos los archivos de todos modos. Entonces, sin confirmar todavía (-n), elegimos la primera confirmación de la rama maestra de B. (El cherry-pick conserva el mensaje de confirmación original lo que un pago directo no parece hacer.) Luego creamos el subárbol donde queremos poner todos los archivos del repositorio B. Luego tenemos que mover todos los archivos que se introdujeron en el cherry-pick al subárbol. En el ejemplo anterior, solo hay un archivo README para mover. Luego confirmamos nuestro commit raíz B-repo, y, al mismo tiempo, también preservamos la marca de tiempo del commit original.

Ahora, crearemos una nueva rama B/master encima de la recién creada new_b_root. Llamamos a la nueva rama 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, puede 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:

introduzca la descripción de la imagen aquí

 5
Author: Finn Haakansson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-05 16:44:28

Tuve un desafío similar, pero en mi caso, habíamos desarrollado una versión del código base en repo A, luego clonado en un nuevo repo, repo B, para la nueva versión del producto. Después de corregir algunos errores en repo A, necesitábamos FI los cambios en repo B. Terminamos haciendo lo siguiente:

  1. Añadiendo un remoto al repositorio B que apuntaba al repositorio A (git remote add...)
  2. Tirando de la rama actual (no estábamos usando master para corregir errores) (git pull remoteForRepoA bugFixBranch)
  3. Empujando fusiones a github

Funcionó un placer:)

 4
Author: David Lemphers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-18 19:36:17

Similar a @Smar pero utiliza rutas del sistema de archivos, establecidas en PRIMARIA y SECUNDARIA:

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

Luego se fusiona manualmente.

(adaptado de post de Anar Manafov )

 3
Author: Turadg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-12 15:15:48

Cuando desee combinar tres o más proyectos en una confirmación única, realice los pasos descritos en las otras respuestas (remote add -f, merge). A continuación, (suave) restablecer el índice a la cabeza antigua (donde no se produjo ninguna fusión). Agregue todos los archivos (git add -A) y confírmelos(mensaje " Fusionando proyectos A, B, C y D en un proyecto). Este es ahora el commit-id de master.

Ahora, crea .git/info/grafts con 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, simplemente agregue tanto head^n..head como tienes ramas. Para actualizar las etiquetas, añada --tag-name-filter cat. No siempre agregue eso, porque esto podría causar una reescritura de algunas confirmaciones. Para más detalles vea página de manual de filter-branch, busque "injertos".

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

 3
Author: koppor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-09 23:22:01

Para fusionar a 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 envíalas.

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

git checkout master
git merge projectA
 3
Author: user123568943685,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-28 16:47:33

Fusionando 2 repositorios

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
 3
Author: RahulMohan Kolakandy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-14 14:02:01

Dado comando es la mejor solución posible sugiero.

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master
 0
Author: Praveen Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-24 05:47:32

Esta función clonará el repositorio remoto en el directorio de repositorio local, después de fusionar todas las confirmaciones se guardarán, git log se 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 usar:

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

Si realiza pequeños cambios, incluso puede mover archivos / dirs de repo fusionado en 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"

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

 0
Author: Andrey Izman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-11 08:47:56

Fusiono proyectos ligeramente manualmente, lo que me permite evitar tener que lidiar con conflictos de fusión.

Primero, copie los archivos del otro proyecto como quiera.

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 el historial de last fetched thing

echo 'FETCH_HEAD' > .git/MERGE_HEAD

Ahora commit como normalmente lo harías

git commit
 0
Author: Collin Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-15 17:38:07