561 votes

Comment importer un dépôt Git existant dans un autre ?

J'ai un dépôt Git dans un dossier appelé XXX et j'ai un deuxième dépôt Git appelé YYY .

Je veux importer le XXX dans le référentiel YYY comme un sous-répertoire nommé ZZZ et ajouter tous les XXX de l'historique des modifications de l'utilisateur YYY .

Structure du dossier avant :

 XXX
    .git
    (project files)
 YYY
     .git
     (project files)

Structure des dossiers après :

YYY
 .git  <-- This now contains the change history from XXX
  ZZZ  <-- This was originally XXX
     (project files)
  (project files)

Est-ce possible, ou dois-je recourir à des sous-modules ?

2 votes

Sur Github, il est maintenant possible de le faire à partir de l'interface web lorsque vous créez un nouveau dépôt.

0 votes

490voto

ebneter Points 4834

Le moyen le plus simple serait probablement de tirer la XXX dans une branche de YYY et ensuite le fusionner dans master :

Sur YYY :

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

Je viens d'essayer cela avec quelques-uns de mes dépôts et cela fonctionne. Contrairement à La réponse de Jörg cela ne vous permettra pas de continuer à utiliser l'autre dépôt, mais je ne pense pas que vous l'ayez spécifié de toute façon.

Note : Depuis que cet article a été écrit en 2009, git a ajouté la fusion de sous-arbres mentionnée dans la réponse ci-dessous. J'utiliserais probablement cette méthode aujourd'hui, même si bien sûr cette méthode fonctionne toujours.

1 votes

Merci. J'ai utilisé une version légèrement modifiée de votre technique : J'ai créé une branche "staging" sur XXX où j'ai créé le dossier ZZZ, et y ai déplacé les "trucs". Puis j'ai fusionné XXX avec YYY.

1 votes

Cela a bien fonctionné pour moi. Les seules modifications que j'ai apportées sont : 1) "git branch -d ZZZ" avant le push car je ne voulais pas que cette branche temporaire traîne. 2) "git push" me donnait l'erreur suivante : "No refs in common and none specified ; doing nothing". Vous devriez peut-être spécifier une branche telle que 'master'". (L'origine vers laquelle je poussais était un dépôt nu vide.) Mais "git push --all" a fonctionné comme un champion.

0 votes

Je ne vois pas de raison pour laquelle vous ne pouvez pas continuer à utiliser l'autre dépôt. Il semble être possible de fusionner les modifications de l'autre dépôt à condition d'utiliser la fonction git merge -s subtree .

399voto

ColinM Points 2701

Si vous voulez conserver l'historique exact des livraisons du second dépôt et donc conserver la possibilité de fusionner facilement les modifications en amont à l'avenir, voici la méthode que vous voulez. Il en résulte un historique non modifié du sous-arbre qui est importé dans votre dépôt plus un commit de fusion pour déplacer le dépôt fusionné vers le sous-répertoire.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

Vous pouvez suivre les changements en amont de la manière suivante :

git pull -s subtree XXX_remote master

Git détermine de lui-même où se trouvent les racines avant de faire la fusion, donc vous n'avez pas besoin de spécifier le préfixe lors des fusions suivantes.

El inconvénient est que dans l'historique fusionné les fichiers ne sont pas fixés (pas dans un sous-répertoire). En conséquence git log ZZZ/a vous montrera tous les changements (s'il y en a) sauf ceux dans l'historique de la fusion. Vous pouvez le faire :

git log --follow -- a

mais cela ne montrera pas les changements autrement que dans l'historique de la fusion.

En d'autres termes, si vous ne changez pas ZZZ dans le référentiel XXX alors vous devez spécifier --follow et un chemin non fixé. Si vous les changez dans les deux dépôts, alors vous avez 2 commandes, dont aucune ne montre tous les changements.

Versions de Git antérieures à 2.9 : Vous n'avez pas besoin de passer le --allow-unrelated-histories option pour git merge .

La méthode de l'autre réponse qui utilise read-tree et saute le merge -s ours n'est pas différente de celle qui consiste à copier les fichiers avec cp et à valider le résultat.

La source originale provient de Article d'aide "Subtree Merge" de github . Et un autre lien utile .

11 votes

Cela ne semble pas avoir préservé l'histoire... si je fais une git log sur tous les fichiers que j'ai récupérés, je ne vois que le seul commit de fusion et rien de sa vie antérieure dans l'autre dépôt ? Git 1.8.0

0 votes

Hmm, d'un autre côté, je vois les commits du dépôt importé si j'ai simplement git log au niveau de la racine, il s'agit donc d'une limitation de la façon dont j'ai essayé de visualiser l'historique des fichiers importés - y a-t-il un moyen ?

9 votes

Aha ! si j'utilise l'ancien chemin du fichier importé, c'est-à-dire que j'omets le sous-répertoire dans lequel il a été importé, alors git log me donnera l'historique des commit, par exemple git log -- myfile au lieu de git log -- rack/myfile

52voto

Jörg W Mittag Points 153275

Il y a un exemple bien connu de cela dans le dépôt Git lui-même, qui est connu collectivement dans la communauté Git sous le nom de " la fusion la plus cool de tous les temps "(après la ligne d'objet que Linus Torvalds a utilisé dans l'e-mail à la liste de diffusion Git qui décrit cette fusion). Dans ce cas, le gitk Git GUI, qui fait maintenant partie de Git proprement dit, était en fait un projet séparé. Linus a réussi à fusionner ce dépôt avec le dépôt Git d'une manière qui

  • il apparaît dans le dépôt Git comme s'il avait toujours été développé dans le cadre de Git,
  • toute l'histoire est conservée intacte et
  • il peut toujours être développé de manière indépendante dans son ancien dépôt, les modifications étant tout simplement git pull ed.

L'e-mail contient les étapes nécessaires à la reproduction, mais ce n'est pas pour les âmes sensibles : d'abord, Linus a écrit Git, donc il en sait probablement un peu plus que vous ou moi, et deuxièmement, c'était il y a presque 5 ans et Git s'est amélioré considérablement depuis lors, alors peut-être que c'est maintenant beaucoup plus facile.

En particulier, je suppose qu'aujourd'hui on utiliserait un submodule gitk, dans ce cas précis.

3 votes

Par ailleurs, la stratégie utilisée pour les fusions ultérieures (s'il y en a) est appelée sous-arbre fusionner, et il y a une tierce partie git-subtree qui peut vous aider dans cette tâche : github.com/apenwarr/git-subtree

0 votes

Merci, j'avais oublié ça. Le site subtree stratégie de fusion, notamment en liaison avec la git-subtree est une alternative intéressante, voire supérieure, aux submodules.

13voto

Damien R. Points 141

La façon la plus simple de le faire est d'utiliser git format-patch.

Supposons que nous ayons 2 dépôts git foo y bar .

foo contient :

  • foo.txt
  • .git

bar contient :

  • bar.txt
  • .git

et nous voulons nous retrouver avec foo contenant le bar l'histoire et ces dossiers :

  • foo.txt
  • .git
  • foobar/bar.txt

Donc pour faire ça :

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

Et si nous voulons réécrire tous les messages commits de bar, nous pouvons le faire, par exemple sous Linux :

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

Cela ajoutera "[bar]" au début de chaque message de livraison.

0 votes

Si le dépôt d'origine contenait des branches et des fusions, git am échouera probablement.

1 votes

Petit problème : git am enlève tout ce qui se trouve dans le fichier [ ] du message de livraison. Vous devez donc utiliser un marqueur différent de [bar]

0 votes

Cela n'a pas fonctionné pour moi. J'ai obtenu "error : foobar/mySubDir/test_host1 : does not exist in index. La copie du patch qui a échoué se trouve dans : /home/myuser/src/proj/.git/rebase-apply/patch Lorsque vous avez résolu ce problème, exécutez "git am --continue". Ceci après avoir appliqué 11 correctifs (sur 60).

3voto

gaoithe Points 377

J'ajoute une autre réponse car je pense que c'est un peu plus simple. Un pull de repo_dest est fait dans repo_to_import et ensuite un push --set-upstream url:repo_dest master est fait.

Cette méthode a fonctionné pour moi en important plusieurs petits dépôts dans un plus grand.

Comment importer : repo1_to_import vers repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Renommez ou déplacez les fichiers et répertoires à l'endroit désiré dans le dépôt d'origine avant de procéder à l'importation.

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

La méthode décrite au lien suivant a inspiré cette réponse. Elle m'a plu car elle me semblait plus simple. MAIS attention ! Il y a des dragons ! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest pousse l'historique et l'état de votre repo local vers le distant (url:repo_dest). MAIS il supprime l'ancien historique et l'état de la version distante. On s'amuse bien ! :-E

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