Comment fusionner deux dépôts Git?

envisager le scénario suivant:

j'ai développé un petit projet expérimental A dans sa propre git repo. Il a maintenant mûri, et J'aimerais que A fasse partie du plus grand projet B, qui a son propre grand dépôt. J'aimerais maintenant ajouter un sous-répertoire de B.

comment fusionner A en B, sans perdre l'histoire d'un côté ou de l'autre?

1265
demandé sur static_rtti 2009-09-15 12:31:54

21 réponses

une seule branche d'un autre dépôt peut être facilement placée sous un sous-répertoire conservant son historique. Par exemple:

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

ceci apparaîtra comme une commit unique où tous les fichiers de la branche maître de Rails sont ajoutés dans le répertoire" rails". Toutefois, le titre de la Commission contient une référence à l'arbre de l'histoire ancienne:

ajouter "rails /" de commit <rev>

<rev> est un SHA-1 commit hash. Vous pouvez encore voir l'histoire, blâmer certains changements.

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

notez que vous ne pouvez pas voir le préfixe de répertoire d'ici car il s'agit d'une ancienne branche réelle laissée intacte. Vous devez traiter cela comme un déplacement de fichier commettre: vous aurez besoin d'un supplément de sauter lors de l'atteindre.

# finishes with all files added at once commit
git log rails/README.md

# then continue from original tree
git log <rev> -- README.md

il y a des solutions plus complexes comme faire cela manuellement ou réécrire l'histoire comme décrit dans d'autres réponses.

la commande git-subtree fait partie du GIT-contrib officiel, certains gestionnaires de paquets l'installent par défaut (OS X Homebrew). Mais vous pourriez avoir à installer par vous-même en plus de git.

331
répondu Simon Perepelitsa 2018-05-07 10:44:36

si vous voulez fusionner 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

tiré de: git fusionner différents dépôts?

cette méthode a plutôt bien fonctionné pour moi, elle est plus courte et à mon avis beaucoup plus propre.

Note: le paramètre --allow-unrelated-histories n'existe que depuis git >= 2,9. Voir documentation de fusion Git - git / -- allow-unlikely-histories

1350
répondu Andresch Serj 2018-03-19 13:34:12

voici deux solutions possibles:

Submodules

soit copier le dépôt A dans un répertoire séparé du projet B, soit (peut-être mieux) cloner le dépôt A dans un sous-répertoire du projet B. puis utiliser sous-module git pour faire de ce dépôt un sous-module 1519120920" d'un dépôt B.

il s'agit d'une bonne solution pour dépôts, où le développement dans le dépôt A continue, et la majeure partie du développement est un développement autonome séparé dans A. Voir aussi SubmoduleSupport et GitSubmoduleTutorial pages sur Git Wiki.

sous-arbre de fusion

vous pouvez fusionner le dépôt A dans un sous-répertoire d'un projet B en utilisant la stratégie subtree merge . C'est décrit dans sous-Arborescence de la Fusion et de Vous par 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

(Option --allow-unrelated-histories est nécessaire pour Git >= 2.9.0.)

ou vous pouvez utiliser git subtree outil ( dépôt sur GitHub ) par apenwarr (Avery Pennarun), a annoncé par exemple dans son billet de blog une nouvelle alternative aux sous-modules Git: Git subtree .


je pense que dans votre cas (A doit faire partie du projet plus grand B) la solution correcte serait d'utiliser subtree merge .

585
répondu Jakub Narębski 2018-08-03 12:17:50

l'approche submodule est bonne si vous voulez maintenir le projet séparément. Cependant, si vous voulez vraiment fusionner les deux projets dans le même dépôt, alors vous avez un peu plus de travail à faire.

La première chose serait d'utiliser git filter-branch réécrire les noms de tout dans le deuxième référentiel d'être dans le sous-répertoire où vous voulez à la fin. Donc au lieu de foo.c , bar.html , vous auriez projb/foo.c et projb/bar.html .

alors, vous devriez être en mesure de faire quelque chose comme ce qui suit:

git remote add projb [wherever]
git pull projb

le git pull fera un git fetch suivi d'un git merge . Il ne devrait pas y avoir de conflits, si le dépôt vers lequel vous tirez n'a pas encore de répertoire projb/ .

une recherche plus poussée indique que quelque chose de similaire a été fait pour fusionner gitk en git . Junio Camano en parle ici: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html

189
répondu Greg Hewgill 2009-09-15 08:38:49

git-subtree est agréable, mais ce n'est probablement pas celui que vous voulez.

par exemple, si projectA est le répertoire créé en B, après git subtree ,

git log projectA

listes seule s'engager: la fusion. Les propagations du projet fusionné sont pour des chemins différents, donc elles n'apparaissent pas.

la réponse de Greg Hewgill se rapproche le plus, bien qu'elle ne effectivement dire comment réécrire les chemins.


la solution est étonnamment simple.

(1),

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

Note: Ceci réécrit l'histoire, donc si vous avez l'intention de continuer à utiliser cette réponse, vous pouvez vouloir cloner (copier) une copie de sortie de celui-ci d'abord.

(2) puis en B, exécuter

git pull path/to/A

voilà! Vous avez un projectA répertoire en B. Si vous lancez git log projectA , vous verrez toutes les propagations de A.


dans mon cas, je voulais deux sous-répertoires, projectA et projectB . Dans ce cas, j'ai fait le pas (1) à B aussi.

63
répondu Paul Draper 2017-08-01 15:06:06

si les deux dépôts ont le même type de fichiers (comme deux dépôts sur Rails pour des projets différents), vous pouvez récupérer les données du dépôt secondaire dans votre dépôt actuel:

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

et la fusionner avec le dépôt actuel:

git merge --allow-unrelated-histories branch_name

si votre version Git est plus petite que 2.9, supprimer --allow-unrelated-histories .

après cela, des conflits peuvent survenir. Vous pouvez les résoudre par exemple avec git mergetool . kdiff3 peut être utilisé uniquement avec le clavier, de sorte que 5 fichiers de conflit prend lors de la lecture du code quelques minutes seulement.

N'oubliez pas de terminer la fusion:

git commit
41
répondu Smar 2018-01-19 13:05:55

j'ai continué à perdre l'historique en utilisant la fusion, donc j'ai fini par utiliser rebase car dans mon cas les deux dépôts sont assez différents pour ne pas finir par fusionner à chaque propagation:

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

= > résoudre les conflits, puis continuer, autant de fois que nécessaire...

git rebase --continue

cela conduit à un projet ayant toutes les commissions de projA suivies de commits de projB

22
répondu Calahad 2014-02-27 03:09:17

dans mon cas, j'avais un dépôt my-plugin et un dépôt main-project , et je voulais prétendre que my-plugin avait toujours été développé dans le sous-répertoire plugins de main-project .

fondamentalement, j'ai réécrit l'histoire du dépôt my-plugin de sorte qu'il semble que tout le développement a eu lieu dans le sous-répertoire plugins/my-plugin . Puis, j'ai ajouté l'histoire du développement de my-plugin dans l'histoire de main-project , et ai fusionné les deux les arbres ensemble. Comme il n'y avait pas de répertoire plugins/my-plugin déjà présent dans le dépôt main-project , il s'agissait d'une fusion sans conflit. Le dépôt résultant contenait toute l'histoire des deux projets originaux, et avait deux racines.

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

version longue

tout d'abord, créez une copie du dépôt my-plugin , parce que nous allons réécrire l'histoire de ce dépôt.

maintenant, naviguez jusqu'à la racine du dépôt my-plugin , vérifiez votre branche principale (probablement master ), et lancez la commande suivante. Bien sûr, vous devez remplacer my-plugin et plugins quels que soient vos noms.

$ 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

maintenant une explication. git filter-branch --tree-filter (...) HEAD exécute la commande (...) sur chaque commit qui est accessible à partir de HEAD . Notez que cela fonctionne directement sur les données stockées pour chaque commit, donc nous n'avons pas à nous soucier des notions de "répertoire de travail", "index", "mise en scène", et ainsi de suite.

si vous exécutez une commande filter-branch qui échoue, elle laissera quelques fichiers dans le répertoire .git et la prochaine fois que vous essayez filter-branch elle se plaindra à ce sujet, à moins que vous fournissiez l'option -f à filter-branch .

quant à la commande actuelle, je n'ai pas eu beaucoup de chance d'obtenir bash pour faire ce que je voulu, donc à la place j'utilise zsh -c pour faire zsh exécuter une commande. J'ai d'abord défini l'option extended_glob , qui permet la syntaxe ^(...) dans la commande mv , ainsi que l'option glob_dots , qui me permet de sélectionner des dotfiles (comme .gitignore ) avec un glob ( ^(...) ).

ensuite, j'utilise la commande mkdir -p pour créer à la fois plugins et plugins/my-plugin en même temps.

Enfin, j'utilise la fonction zsh "négatif glob " ^(.git|my-plugin) pour apparier tous les fichiers du répertoire racine du dépôt à l'exception de .git et du dossier nouvellement créé my-plugin . (Exclure .git pourrait ne pas être nécessaire ici, mais essayer de déplacer un répertoire dans lui-même est une erreur.)

dans mon dépôt, la propagation initiale n'incluait aucun fichier, de sorte que la commande mv a renvoyé une erreur sur la propagation initiale (puisque rien n'était disponibles pour se déplacer). Par conséquent, j'ai ajouté un || true de sorte que git filter-branch ne serait pas avorter.

l'option --all indique filter-branch pour réécrire l'historique de toutes branches dans le dépôt, et l'extra -- est nécessaire pour dire git pour l'interpréter comme une partie de la liste d'options pour les branches à réécrire, au lieu d'une option à filter-branch elle-même.

maintenant, naviguez vers votre main-project référentiel et vérifiez dans quelle branche vous voulez fusionner. Ajouter votre copie locale du dépôt my-plugin (avec son historique modifié) comme une télécommande de main-project avec:

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

vous avez maintenant deux arbres sans rapport dans votre histoire de propagation, que vous pouvez visualiser en utilisant:

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

pour les fusionner, utilisez:

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

noter qu'en pré-2.9.0 Git, le L'option --allow-unrelated-histories n'existe pas. Si vous utilisez une de ces versions, omettez simplement l'option: le message d'erreur --allow-unrelated-histories empêche était aussi ajouté en 2.9.0.

vous ne devriez pas avoir de conflits de fusion. Si vous le faites, cela signifie probablement que la commande filter-branch n'a pas fonctionné correctement ou qu'il y avait déjà un répertoire plugins/my-plugin dans main-project .

assurez-vous d'entrer un message d'engagement explicatif pour les futurs contributeurs se demandant ce que hackery allait faire pour un dépôt avec deux racines.

vous pouvez visualiser le nouveau graphe de commit, qui devrait avoir deux commits root, en utilisant la commande git log ci-dessus. Notez que seulement la master branche sera fusionnée . Cela signifie que si vous avez un travail important sur d'autres my-plugin branches que vous voulez fusionner dans l'arbre main-project , vous devez évitez de supprimer la télécommande my-plugin jusqu'à ce que vous ayez fait ces fusions. Si vous ne le faites pas, alors les propagations de ces branches seront toujours dans le dépôt main-project , mais certaines seront inaccessibles et susceptibles d'être récupérées. (De plus, vous devrez vous y référer par SHA, parce que supprimer une télécommande supprime ses branches de suivi à distance.)

en option, après avoir fusionné tout ce que vous voulez garder de my-plugin , vous pouvez supprimer le my-plugin à distance à l'aide:

$ git remote remove my-plugin

vous pouvez maintenant supprimer en toute sécurité la copie du dépôt my-plugin dont vous avez modifié l'historique. Dans mon cas, j'ai aussi ajouté un avis de dépréciation au véritable dépôt my-plugin après que la fusion ait été terminée et poussée.


testé sur Mac OS X El Capitan avec git --version 2.9.0 et zsh --version 5.2 . Votre kilométrage peut varier.

, les Références:

14
répondu Radon Rosborough 2017-05-23 11:55:02

j'ai essayé de faire la même chose pendant des jours, j'utilise git 2.7.2. Subtree ne préserve pas l'histoire.

Vous pouvez utiliser cette méthode si vous n'utilisez pas l'ancien projet de nouveau.

je vous suggère d'abord la branche B et de travailler dans la branche.

Voici les étapes sans ramification:

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 vous enregistrez maintenant l'un des fichiers dans la subdivision A, vous obtiendrez l'historique complet

git log --follow A/<file>

C'était le post qui m'aident à le faire:

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

8
répondu Rian 2016-03-10 12:51:08

je sais que c'est longtemps après les faits, mais je n'étais pas heureux avec les autres réponses que j'ai trouvé ici, donc j'ai écrit ceci:

me=$(basename "151900920")

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 "" ]; do
    repo=""; 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
répondu jettero 2012-05-07 13:38:36

j'ai rassemblé beaucoup d'informations ici sur le débordement de la pile, etc., et avoir réussi à mettre en place un script qui résout le problème pour moi.

la mise en garde est qu'elle ne prend en compte que la branche "développement" de chaque dépôt et la fusionne dans un répertoire séparé dans un dépôt complètement nouveau.

Les étiquettes

et les autres branches sont ignorées - ce n'est peut-être pas ce que vous voulez.

le script gère même la fonctionnalité branches et tags-les renommer dans le nouveau projet afin que vous sachiez d'où ils viennent.

#!/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
##            eitch@eitchnet.ch
##
## 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: "151900920" <new_project> <my_repo_urls.lst>"
  exit 1
fi


# Script functions
function failed() {
  echo -e "ERROR: Merging of projects failed:"
  echo -e ""
  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 '' of ${sub_project}" ; then
      failed "Failed to commit merge of branch '' of ${sub_project} into ${current_branch}"
    fi
  fi
}


## Script variables
PROJECT_NAME=""
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
REPO_FILE=""
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

vous pouvez aussi l'obtenir de http://paste.ubuntu.com/11732805

créez D'abord un fichier avec L'URL de chaque dépôt, par exemple:

git@github.com:eitchnet/ch.eitchnet.parent.git
git@github.com:eitchnet/ch.eitchnet.utils.git
git@github.com:eitchnet/ch.eitchnet.privilege.git

ensuite, appelez le script en donnant le nom du projet et le chemin vers le script:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

le script lui-même a beaucoup de commentaires qui devraient expliquer ce qu'elle fait.

6
répondu eitch 2018-08-03 12:21:22

si vous essayez simplement de coller deux dépôts ensemble, les sous-modules et les fusions de sous-arborescence sont le mauvais outil à utiliser parce qu'ils ne préservent pas toute l'histoire du fichier (comme les gens ont noté sur d'autres réponses). Voir cette réponse ici pour la façon simple et correcte de le faire.

5
répondu Eric Lee 2017-05-23 12:34:51

si vous voulez mettre les fichiers d'une branche dans un repo B dans un subtree de repo A et aussi préserver l'histoire, continuez à lire. (Dans l'exemple ci-dessous, je suppose que nous voulons que la branche principale de la banque soit fusionnée à la branche principale de la banque.)

Dans Un repo, d'abord faire ce qui suit pour faire pensions de B:

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

maintenant nous créer une toute nouvelle branche (avec un seul commit) dans la réplique que nous appelons new_b_root . La propagation résultante aura les fichiers qui ont été engagés dans la première propagation de la branche principale du rapport B mais mis dans un sous-répertoire appelé 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))"

explication: l'option --orphan de la commande checkout vérifie les fichiers de la branche maître de A mais ne crée pas de commit. Nous aurions pu sélectionner n'importe quel commit car ensuite nous éliminons tous les fichiers de toute façon. Ensuite, sans commettre encore ( -n ), nous sélectionnons le premier commit de la branche principale de B. (Le choix préserve le message de commit original, ce qu'un simple checkout ne semble pas faire.) Puis nous créons le sous-arborescence où nous voulons mettre tous les fichiers de repo B. Nous devons ensuite déplacer tous les fichiers qui ont été introduits dans le "cherry-pick" vers le sous-arborescence. Dans l'exemple ci-dessus, il n'y a qu'un fichier README à déplacer. Puis nous commettons notre B-repo root commit, et, en même temps, nous conservons également l'horodatage du commit original.

maintenant, nous allons créer une nouvelle branche B/master en plus de la nouvelle branche new_b_root . Nous appelons la nouvelle branche b :

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

maintenant, nous fusionnons notre b branche dans A/master :

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

enfin, vous pouvez supprimer le B branches distantes et temporaires:

git remote remove B
git branch -D new_b_root b

le graphique final aura une structure comme celle-ci:

enter image description here

5
répondu Finn Haakansson 2018-05-05 16:44:28

j'ai eu un défi similaire, mais dans mon cas, nous avions développé une version du codebase en repo A, puis nous l'avons cloné dans un nouveau repo, repo B, pour la nouvelle version du produit. Après avoir corrigé quelques bugs dans repo A, Nous avons dû corriger les changements dans repo B. Nous avons fini par faire ce qui suit:

  1. ajout d'une télécommande à la position repo B pointant vers la position repo a (git remote add...)
  2. tirant la branche courante (nous n'utilisions pas master pour corriger les bogues) (git pull remoteForRepoA bugFixBranch)
  3. Pousser fusionne à github

ouvré:)

4
répondu David Lemphers 2011-11-18 19:36:17

similaire à @Smar mais utilise des chemins de système de fichiers, définis en primaire et secondaire:

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

puis vous fusionnez manuellement.

(adapté de post by Anar Manafov )

3
répondu Turadg 2011-08-12 15:15:48

lorsque vous voulez fusionner trois ou plusieurs projets dans une single commit, faites les étapes décrites dans les autres réponses ( remote add -f , merge ). Puis, (soft) réinitialiser l'index à la vieille tête (où aucune fusion ne s'est produite). Ajoutez tous les fichiers ( git add -A ) et engagez-les (message " fusionner les projets A, B, C et D en un seul projet). C'est maintenant le commit-id de maître.

maintenant, créer .git/info/grafts avec le contenu suivant:

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

Exécuter git filter-branch -- head^..head head^2..head head^3..head . Si vous avez plus de trois branches, ajoutez autant de head^n..head que vous avez de branches. Pour mettre à jour les tags, ajoutez --tag-name-filter cat . N'ajoutez pas toujours cela, car cela pourrait provoquer une réécriture de certaines propagations. Pour plus de détails voir man page de filter-branch , rechercher "grafts".

maintenant, votre dernier engagement a les bons parents associés.

3
répondu koppor 2013-05-09 23:22:01

pour fusionner a à l'intérieur de B:

1) dans le projet a

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

2) dans le projet B

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

dans cette branche faites toutes les opérations que vous devez faire et engagez-les.

C) puis retour au maître et fusion classique entre les deux branches:

git checkout master
git merge projectA
3
répondu user123568943685 2015-07-28 16:47:33

de la Fusion de 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
3
répondu RahulMohan Kolakandy 2017-08-14 14:02:01

est la meilleure solution possible.

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master
0
répondu Praveen Kumar 2015-11-24 05:47:32

cette fonction clonera Remote repo dans repo dir local, après la fusion toutes les propagations seront sauvegardées, git log affichera les propagations originales et les chemins appropriés:

function git-add-repo
{
    repo=""
    dir="$(echo "" | 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"
}

comment utiliser:

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

si vous faites quelques changements, vous pouvez même déplacer des fichiers / DRS de repo fusionnés dans des chemins différents, par exemple:

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 "" | sed 's/\./\./')"
    to="$(echo "" | 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"

Avis

Chemin remplace via sed , donc assurez-vous qu'il s'est déplacé dans les chemins appropriés après la fusion.

Le paramètre --allow-unrelated-histories n'existe que depuis git >= 2.9.

0
répondu Andrey Izman 2017-04-11 08:47:56

je fusionne les projets légèrement manuellement, ce qui me permet d'éviter d'avoir à gérer les conflits de fusion.

d'abord, copiez dans les fichiers de l'autre projet comme vous le souhaitez.

cp -R myotherproject newdirectory
git add newdirectory

pull suivante dans l'histoire

git fetch path_or_url_to_other_repo

indiquer à git pour fusionner dans l'histoire de la dernière extraction chose

echo 'FETCH_HEAD' > .git/MERGE_HEAD

maintenant commettre toutefois vous le feriez normalement s'engager

git commit
0
répondu Collin Anderson 2018-02-15 17:38:07