Combiner plusieurs référentiels Git


207

Disons que j'ai une configuration qui ressemble à quelque chose

phd/code/
phd/figures/
phd/thesis/

Pour des raisons historiques, ils ont tous leurs propres référentiels git. Mais j'aimerais les combiner en un seul pour simplifier un peu les choses. Par exemple, en ce moment, je pourrais faire deux séries de changements et devoir faire quelque chose comme

cd phd/code
git commit 
cd ../figures
git commit

Ce serait (maintenant) agréable de simplement jouer

cd phd
git commit

Il semble y avoir deux façons de le faire en utilisant des sous-modules ou en tirant de mes sous-répertoires, mais c'est un peu plus complexe que je ne le recherche. À tout le moins, je serais heureux

cd phd
git init
git add [[everything that's already in my other repositories]]

mais cela ne semble pas être une ligne. Y a-t-il quelque chose gitqui puisse m'aider?


Considérez également cette excellente approche: stackoverflow.com/questions/1425892/…
Johan Sjöberg


Le script join-git-repos.py fait un bon travail si vous avez des référentiels séparés, chacun avec des branches principales que vous souhaitez combiner.
Mark

Réponses:


149

Voici une solution que j'ai donnée ici :

  1. Faites d'abord une sauvegarde complète de votre répertoire phd: je ne veux pas être tenu responsable de vos années de travail acharnées! ;-)

    $ cp -r phd phd-backup
    
  2. Déplacez le contenu de phd/codevers phd/code/codeet corrigez l'historique afin qu'il ressemble à ce qu'il a toujours été là (cela utilise la commande filter-branch de git ):

    $ cd phd/code
    $ git filter-branch --index-filter \
        'git ls-files -s | sed "s#\t#&code/#" |
         GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
         git update-index --index-info &&
         mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
    
  3. Idem pour le contenu de phd/figureset phd/thesis(remplacez simplement codepar figureset thesis).

    Maintenant, la structure de votre répertoire devrait ressembler à ceci:

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. Créez ensuite un référentiel git dans le répertoire racine, tirez tout dedans et supprimez les anciens référentiels:

    $ cd phd
    $ git init
    
    $ git pull code
    $ rm -rf code/code
    $ rm -rf code/.git
    
    $ git pull figures --allow-unrelated-histories
    $ rm -rf figures/figures
    $ rm -rf figures/.git
    
    $ git pull thesis --allow-unrelated-histories
    $ rm -rf thesis/thesis
    $ rm -rf thesis/.git
    

    Enfin, vous devriez maintenant avoir ce que vous vouliez:

    phd
      |_.git
      |_code
      |    |_(your code...)
      |_figures
      |    |_(your figures...)
      |_thesis
           |_(your thesis...)
    

Un bon côté de cette procédure est qu'elle laissera les fichiers et répertoires non versionnés en place.

J'espère que cela t'aides.


Juste un mot d'avertissement cependant: si votre coderépertoire a déjà un codesous - répertoire ou un fichier, les choses peuvent mal tourner (même chose figureset thesisbien sûr). Si c'est le cas, renommez simplement ce répertoire ou fichier avant de passer par toute cette procédure:

$ cd phd/code
$ git mv code code-repository-migration
$ git commit -m "preparing the code directory for migration"

Et lorsque la procédure est terminée, ajoutez cette dernière étape:

$ cd phd
$ git mv code/code-repository-migration code/code
$ git commit -m "final step for code directory migration"

Bien sûr, si le codesous - répertoire ou le fichier n'est pas versionné, utilisez simplement à la mvplace de git mv, et oubliez le git commits.


13
Merci pour cet extrait - il a fait exactement ce dont j'avais besoin (une fois que j'ai expliqué que Mac OS X sed ne traitait pas "\ t" (j'ai dû utiliser ^ V ^ I à la place).
Craig Trader

6
Je n'ai pas pu faire fonctionner cela au début et j'ai finalement trouvé la solution au problème sur un autre ancien babillard. Sur la dernière ligne, j'ai dû mettre des guillemets autour des noms de fichiers comme ceci: mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEADet ensuite cela a très bien fonctionné!
Jorin

3
La commande funky filter-branch provient des pages de manuel filter-branch de git. Vous devriez dire que: a) elle doit être attribuée correctement b) Je n'exécuterai pas une telle commande simplement parce que quelqu'un, même avec une grande réputation, l'a publiée sur StackOverflow. Sachant que c'est à partir des pages de manuel, je le ferai.
tymtam

5
FAIS ATTENTION! MacOS X n'utilise pas l'extension GNU de sed, il ne connaît donc pas la séquence \ t. Le résultat est une histoire foirée! Ma solution était de coller le code dans un fichier script et d'y écrire un vrai caractère <TAB>. Depuis le terminal, un onglet peut être saisi en appuyant sur ctrl + v puis en écrivant un <TAB>. Je n'ai pas essayé la solution de Craig
Gil Vegliach

5
ATTENTION (2)! Notez également que si certains fichiers ou répertoires contiennent des tirets ('-'), la commande sed échouera. Dans ce cas, vous pouvez le remplacer par quelque chose comme 's ~ \ t ~ & code / ~'. Ici, en appliquant la même logique,
faites

75

git-stitch-repotraitera la sortie de git-fast-export --all --date-ordersur les référentiels git donnés sur la ligne de commande, et créera un flux approprié pour git-fast-importcela créera un nouveau référentiel contenant toutes les validations dans une nouvelle arborescence de validations qui respecte l'historique de tous les référentiels sources.


33
Euh, c'est un outil tiers, qui ne fait pas partie de git… :-)
Aristote Pagaltzis

1
En effet, maintenant vous me dites :) Oh bien, je suppose que j'ai dû apprendre à installer des packages CPAN un jour…
Will Robertson

1
Merci d'avoir signalé cette commande. Je viens de l'utiliser pour aider à déplacer quelques dépôts de SVN vers Git.
signine

1
AVERTISSEMENT peut ne pas fonctionner si vous avez des succursales / fusions! Depuis la page git-stich-repo : "git-stich-repo fonctionne parfaitement avec les référentiels qui ont un historique linéaire (pas de fusion). succursales et fusionne. "
Bryan P

6
Ceci est un script externe, la réponse est trop courte et pas vraiment utile, ce script a des problèmes avec les validations de fusion, peu de gens manipuleraient Perl ou CPAN et cela n'est pas bien expliqué dans la réponse. Alors ... -1, désolé.
Haralan Dobrev

20

Peut-être, simplement (de manière similaire à la réponse précédente, mais en utilisant des commandes plus simples) faire dans chacun des anciens référentiels séparés une validation qui déplace le contenu dans un sous-répertoire convenablement nommé, par exemple:

$ cd phd/code
$ mkdir code
# This won't work literally, because * would also match the new code/ subdir, but you understand what I mean:
$ git mv * code/
$ git commit -m "preparing the code directory for migration"

puis en fusionnant les trois dépôts séparés en un nouveau, en faisant smth comme:

$ cd ../..
$ mkdir phd.all
$ cd phd.all
$ git init
$ git pull ../phd/code
...

Ensuite, vous sauvegarderez vos historiques, mais vous continuerez avec un seul dépôt.


C'est ok, mais si vous fusionnez un dépôt dans un autre (c'est-à-dire que phd était un dépôt déjà vide), alors si phd avait des dossiers avec des noms identiques aux sous-dossiers du répertoire de code, vous rencontrerez des problèmes comme 'git pull .. / phd / code 'tire toutes les validations avec les chemins d'origine et seulement à la fin il applique la validation mv.
tymtam

1
@Tymek: mais cela fonctionnera toujours dans cette situation, sans problème. Ce qui ne sera pas sympa, c'est que les chemins de l'historique ne seront pas "corrects" (correspondent aux nouveaux chemins).
imz - Ivan Zakharyaschev

19

Vous pouvez essayer la stratégie de fusion des sous-arbres . Il vous permettra de fusionner le repo B dans le repo A. L'avantage git-filter-branchest qu'il ne vous oblige pas à réécrire votre historique du repo A (briser les sommes SHA1).


Le lien ne fonctionne pas et cela ne préserverait pas l'histoire, n'est-ce pas?
tymtam

3
@Tymek (certaines parties de kernel.org sont toujours en panne après la faille de sécurité). Il casse les SHA1 du repo entrant B. Mais A reste intact.
Leif Gruenwoldt


1
@LeifGruenwoldt Le 1er lien fonctionne maintenant. Et le lien miroir a disparu, vous devriez le supprimer, je suppose.
Vadim Kotov

9

La solution git-filter-branch fonctionne bien, mais notez que si votre dépôt git provient d'une importation SVN, il peut échouer avec un message comme:

Rewrite 422a38a0e9d2c61098b98e6c56213ac83b7bacc2 (1/42)mv: cannot stat `/home/.../wikis/nodows/.git-rewrite/t/../index.new': No such file or directory

Dans ce cas, vous devez exclure la révision initiale de la branche de filtre - c'est-à-dire changer le HEADà la fin en [SHA of 2nd revision]..HEAD- voir:

http://www.git.code-experiments.com/blog/2010/03/merging-git-repositories.html


2
Je vous remercie! Je me suis gratté la tête pourquoi cela ne fonctionnait pas! Le repo provenait en effet de SVN.
Arthur Maltson

1
Même erreur quand je fais ça. J'ai mes espoirs. De plus, le lien est maintenant rompu.
Ryan

Pourriez-vous élaborer ce que vous vouliez dire par "changer la tête à ...", mon dépôt provient d'une importation SVN et je suis confronté exactement à ce problème, j'apprécierais beaucoup l'aide!

5

La solution @MiniQuark m'a beaucoup aidé, mais malheureusement, elle ne prend pas en compte les balises qui se trouvent dans les référentiels sources (au moins dans mon cas). Voici mon amélioration de la réponse @MiniQuark.

  1. Créez d'abord un répertoire qui contiendra le référentiel composé et les repos fusionnés, créez un répertoire pour chaque fusionné.

    $ mkdir new_phd
    $ mkdir new_phd / code
    $ mkdir new_phd / figures
    $ mkdir new_phd / thèse

  2. Faites une extraction de chaque référentiel et récupérez toutes les balises. (Présentation des instructions uniquement pour le codesous-répertoire)

    $ cd new_phd / code
    $ git init
    $ git pull ../../original_phd/code master
    $ git fetch ../../original_phd/code refs / tags / *: refs / tags / *

  3. (Il s'agit d'une amélioration au point 2 de la réponse MiniQuark) Déplacez le contenu de new_phd/codeto new_phd/code/codeet ajoutez un code_préfixe avant chaque balise

    $ git filter-branch --index-filter 'git ls-files -s | sed "s- \ t \" * - & code / - "| GIT_INDEX_FILE = $ GIT_INDEX_FILE.new git update-index --index-info && mv $ GIT_INDEX_FILE.new $ GIT_INDEX_FILE '--tag-name-filter' sed" s -. * - code _ & - "'TETE

  4. Après cela, il y aura deux fois plus de balises qu'avant la branche de filtrage. Les anciennes balises restent dans le référentiel et de nouvelles balises avec code_préfixe sont ajoutées.

    $ git tag
    mytag1
    code_mytag1

    Supprimez les anciennes balises manuellement:

    $ ls .git / refs / tags / * | grep -v "/ code_" | xargs rm

    Répétez le point 2,3,4 pour les autres sous-répertoires

  5. Maintenant, nous avons la structure des répertoires comme au point de réponse @MiniQuark 3.

  6. Faites comme au point 4 de la réponse de MiniQuark, mais après avoir fait un pull et avant de supprimer .gitdir, récupérez les balises:

    $ git fetch catalogue refs / tags / *: refs / tags / *

    Continuer..

Ceci est juste une autre solution. J'espère que cela aide quelqu'un, cela m'a aidé :)


5

git-stitch-repo d' Aristotle Pagaltzis 'réponse ne fonctionne que pour les référentiels avec une histoire simple et linéaire.

La réponse de MiniQuark fonctionne pour tous les référentiels, mais elle ne gère pas les balises et les branches.

J'ai créé un programme qui fonctionne de la même manière que décrit MiniQuark, mais il utilise un commit de fusion (avec N parents) et recrée également toutes les balises et branches pour pointer vers ces commits de fusion.

Voir le référentiel git-merge-repos pour des exemples d'utilisation.



3

En fait, git-stitch-repo prend désormais en charge les branches et les balises, y compris les balises annotées (j'ai trouvé un bogue que j'ai signalé et il a été corrigé). Ce que j'ai trouvé utile, c'est avec des balises. Comme les balises sont attachées aux validations, et certaines des solutions (comme l'approche d'Eric Lee) ne parviennent pas à gérer les balises. Vous essayez de créer une branche à partir d'une balise importée, et cela annulera toutes les fusions / mouvements de git et vous renverra comme le référentiel consolidé étant presque identique au référentiel d'où provient la balise. En outre, il existe des problèmes si vous utilisez la même balise dans plusieurs référentiels que vous avez «fusionné / consolidé». Par exemple, si vous disposez de l'annonce A du référentiel B, les deux ayant la balise rel_1.0. Vous fusionnez le référentiel A et le référentiel B dans le référentiel AB. Comme les balises rel_1.0 sont sur deux validations différentes (une pour A et une pour B), quelle balise sera visible dans AB? Soit l'étiquette du référentiel importé A, soit du référentiel importé B, mais pas les deux.

git-stitch-repo aide à résoudre ce problème en créant des balises rel_1.0-A et rel_1.0-B. Vous ne pourrez peut-être pas extraire la balise rel_1.0 et vous attendre aux deux, mais au moins vous pouvez voir les deux, et théoriquement, vous pouvez les fusionner dans une branche locale commune, puis créer une balise rel_1.0 sur cette branche fusionnée (en supposant que vous venez de fusionner et ne pas changer le code source). Il est préférable de travailler avec des succursales, car vous pouvez fusionner comme des succursales de chaque dépôt en succursales locales. (dev-a et dev-b peuvent être fusionnés en une branche de développement locale qui peut ensuite être poussée à l'origine).


2

La séquence que vous avez suggérée

git init
git add *
git commit -a -m "import everything"

fonctionnera, mais vous perdrez votre historique de validation.


Perdre l'historique n'est pas si mal, mais comme le référentiel est pour mon propre travail (c'est-à-dire qu'il est privé), il y a beaucoup de choses que je ne veux pas versionné ou qui n'est pas encore versionné.
Will Robertson

1

Pour fusionner un secondProject au sein d'un mainProject:

A) Dans le deuxième projet

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

B) Dans le projet principal:

git checkout -b secondProject
git fast-import --force < /tmp/secondProjectExport

Dans cette branche, effectuez toutes les transformations lourdes que vous devez effectuer et validez-les.

C) Puis revenons au master et une fusion classique entre les deux branches:

git checkout master
git merge secondProject

Cela fusionnerait tous les fichiers et dossiers à la racine des deux projets git en un seul projet. Je doute que personne ne veuille que cela se produise.
Clintm

0

Je jetterai ma solution ici aussi. C'est essentiellement un wrapper de script bash assez simplegit filter-branch . Comme d'autres solutions, il migre uniquement les branches principales et ne migre pas les balises. Mais les historiques de validation complets sont migrés et il s'agit d'un bref script bash, il devrait donc être relativement facile pour les utilisateurs de le réviser ou de le modifier.

https://github.com/Oakleon/git-join-repos


0

Ce script bash contourne le problème des caractères de l'onglet sed (sur MacOS par exemple) et le problème des fichiers manquants.

export SUBREPO="subrepo"; # <= your subrepository name here
export TABULATOR=`printf '\t'`;
FILTER='git ls-files -s | sed "s#${TABULATOR}#&${SUBREPO}/#" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
  git update-index --index-info &&
  if [ -f "$GIT_INDEX_FILE.new" ]; then mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE; else echo "git filter skipped missing file: $GIT_INXEX_FILE.new"; fi'

git filter-branch --index-filter "$FILTER" HEAD

Ceci est une combinaison de miniquark , marius-Butuc et ryan messages de. Bravo à eux!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.