2251 votes

Comment fusionner deux dépôts Git ?

Considérons le scénario suivant :

J'ai développé un petit projet expérimental A dans son propre repo Git. Il est maintenant arrivé à maturité, et j'aimerais que A fasse partie d'un projet plus important, B, qui a son propre grand dépôt. J'aimerais maintenant ajouter A en tant que sous-répertoire de B.

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

17 votes

Si vous essayez simplement de combiner deux dépôts en un seul, sans avoir besoin de conserver les deux dépôts, jetez un coup d'œil à cette question : stackoverflow.com/questions/13040958/

0 votes

Pour fusionner le repo git dans un répertoire personnalisé en sauvegardant tous les comits, utilisez stackoverflow.com/a/43340714/1772410

2849voto

Andresch Serj Points 3216

Si vous souhaitez fusionner 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

Tiré de : git merge different repositories ?

Cette méthode a très bien fonctionné pour moi, elle est plus courte et, à mon avis, beaucoup plus propre.

Si vous souhaitez mettre project-a dans un sous-répertoire, vous pouvez utiliser git-filter-repo ( filter-branch es découragé ). Exécutez les commandes suivantes avant les commandes ci-dessus :

cd path/to/project-a
git filter-repo --to-subdirectory-filter project-a

Un exemple de fusion de 2 gros dépôts, en plaçant l'un d'entre eux dans un sous-répertoire : https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

Nota: Les --allow-unrelated-histories n'existe que depuis git >= 2.9. Voir Git - git merge Documentation / --allow-unrelated-histories

Mise à jour : Ajouté --tags comme suggéré par @jstadler afin de conserver les balises.

2 votes

Il semble qu'il faille une copie de travail : fatal: This operation must be run in a work tree Je souhaite fusionner deux dépôts git nus.

2 votes

@LiuYan : Désolé, c'est un peu tard, mais vous pouvez créer une copie de travail en consultant l'un des projets et en procédant comme décrit.

2 votes

Notez que cela ne fusionne pas les sous-modules.

662voto

Jakub Narębski Points 87537

Voici deux solutions possibles :

Sous-modules

Soit vous copiez le référentiel A dans un répertoire distinct du projet B, soit (ce qui est peut-être mieux) vous clonez le référentiel A dans un sous-répertoire du projet B. Ensuite, vous utilisez sous-module git pour faire de ce dépôt un sous-module d'un dépôt B.

C'est une bonne solution pour les référentiels faiblement couplés, où le développement dans le référentiel A se poursuit, et où la majeure partie du développement est un développement autonome séparé dans A. Voir aussi Support de sous-module y Didacticiel GitSubmodule sur Git Wiki.

Fusion de sous-arbres

Vous pouvez fusionner le référentiel A dans un sous-répertoire d'un projet B à l'aide de la commande fusion de sous-arbres stratégie. Celle-ci est décrite dans Fusion de sous-arbres et 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).

Vous pouvez également utiliser git subtree ( sur GitHub ) par apenwarr (Avery Pennarun), annoncée 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 d'un projet plus vaste, B), la bonne solution serait d'utiliser fusion de sous-arbres .

0 votes

J'ai trouvé tout ce dont j'avais besoin pour placer mon repo local dans un sous-répertoire d'un repo distant en suivant le HOWTO "how to use the subtree merge strategy".

0 votes

Hmm 'git pull -s subtree Bproject master' ne met pas à jour le tag Bproject/master dans l'historique vers le nouveau Bproject HEAD

1 votes

Cela fonctionne et semble préserver l'historique, mais pas au point de pouvoir l'utiliser pour différencier des fichiers ou couper la fusion. Ai-je oublié une étape ?

520voto

Semyon Perepelitsa Points 7592

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

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

Cela apparaîtra comme un commit unique où tous les fichiers de la branche master de Rails seront ajoutés dans le répertoire "rails". Cependant, le titre du commit contient une référence à l'ancien arbre historique :

Ajouter 'rails/' au commit <rev>

Dónde <rev> est un hachage d'engagement SHA-1. Vous pouvez toujours voir l'historique, en accusant certains changements.

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

Notez que vous ne pouvez pas voir le préfixe du répertoire d'ici puisqu'il s'agit d'une ancienne branche laissée intacte. Vous devez traiter ce commit comme un déplacement de fichier habituel : vous aurez besoin d'un saut supplémentaire pour 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 existe des solutions plus complexes, comme le faire manuellement ou réécrire l'historique, comme décrit dans d'autres réponses.

La commande git-subtree fait partie de git-contrib officiel, certains gestionnaires de paquets l'installent par défaut (OS X Homebrew). Mais il se peut que vous deviez l'installer vous-même en plus de git.

3 votes

Voici les instructions pour installer Git SubTree (en date de juin 2013) : stackoverflow.com/a/11613541/694469 (et j'ai remplacé git co v1.7.11.3 con ... v1.8.3 ).

1 votes

Merci de m'avoir prévenu de la réponse ci-dessous. Depuis git 1.8.4, 'subtree' n'est toujours pas inclus (du moins pas sur le ppa git d'Ubuntu 12.04 (ppa:git-core/ppa)).

1 votes

Je peux le confirmer après cela, git log rails/somefile n'affichera pas l'historique des commits de ce fichier, à l'exception du commit de fusion. Comme @artfulrobot l'a suggéré, vérifiez Réponse de Greg Hewgill . Et vous pourriez avoir besoin d'utiliser git filter-branch sur le repo que vous voulez inclure.

214voto

Greg Hewgill Points 356191

L'approche par sous-module est intéressante si vous souhaitez maintenir le projet séparément. Cependant, si vous voulez vraiment fusionner les deux projets dans le même dépôt, vous avez un peu plus de travail à faire.

La première chose à faire est d'utiliser git filter-branch pour réécrire les noms de tout ce qui se trouve dans le deuxième référentiel afin qu'ils se trouvent dans le sous-répertoire où vous souhaitez qu'ils aboutissent. Ainsi, au lieu de foo.c , bar.html vous auriez projb/foo.c y projb/bar.html .

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

git remote add projb [wherever]
git pull projb

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

Des recherches plus approfondies indiquent que quelque chose de similaire a été fait pour fusionner gitk en git . Junio C Hamano en parle ici : http://www.mail-archive.com/git@vger.kernel.org/msg03395.html

1 votes

N'ayant pas essayé moi-même, le git pull peut se plaindre de l'impossibilité de trouver un ancêtre commun. D'autres détails peuvent également devoir être réglés.

4 votes

La fusion de sous-arbres serait une meilleure solution, et ne nécessite pas de réécrire l'historique du projet inclus

9 votes

J'aimerais savoir comment utiliser git filter-branch pour y parvenir. Dans la page de manuel, il est question de l'inverse : faire en sorte que le sous-répertoire/ devienne la racine, mais pas l'inverse.

69voto

Smar Points 2420

Si les deux référentiels contiennent le même type de fichiers (comme deux référentiels Rails pour des projets différents), vous pouvez récupérer les données du référentiel secondaire dans votre référentiel actuel :

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

puis de le fusionner avec le dépôt actuel :

git merge --allow-unrelated-histories branch_name

Si votre version de Git est inférieure à 2.9, supprimez --allow-unrelated-histories .

Ensuite, 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 le fichier 5 conflits ne prend que quelques minutes lors de la lecture du code.

N'oubliez pas de terminer la fusion :

git commit

2 votes

J'aime la simplicité de cette solution, et elle semble correspondre à ce que je recherche, mais n'est-ce pas en fait l'équivalent d'une fonction git pull --allow-unrelated-histories ?

1 votes

@Prometheus En quelque sorte. Je ne l'ai pas testé maintenant, mais il aurait probablement fallu ajouter le dépôt distant comme un vrai dépôt distant, où cela ne fait que récupérer le contenu nécessaire à une branche et fusionner ce contenu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X