Dans mon cas, j'avais un my-pluginréférentiel et un main-projectréférentiel, et je voulais faire comme si my-pluginj'avais toujours été développé dans le pluginssous - répertoire de main-project.
Fondamentalement, j'ai réécrit l'histoire du my-pluginréférentiel afin qu'il apparaisse que tout le développement a eu lieu dans le plugins/my-pluginsous - répertoire. Ensuite, j'ai ajouté l'historique de développement de my-plugindans l' main-projecthistoire et j'ai fusionné les deux arbres ensemble. Puisqu'aucun plugins/my-pluginrépertoire n'était déjà présent dans le main-projectréférentiel, il s'agissait d'une fusion triviale sans conflits. Le référentiel 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|plugins) 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 my-pluginréférentiel, car nous allons réécrire l'historique de ce référentiel.
Maintenant, accédez à la racine du my-pluginréférentiel, consultez votre branche principale (probablement master) et exécutez la commande suivante. Bien sûr, vous devez remplacer my-pluginet pluginsquels que soient vos noms réels.
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
Maintenant pour une explication. git filter-branch --tree-filter (...) HEADexécute la (...)commande sur chaque commit accessible depuis HEAD. Notez que cela fonctionne directement sur les données stockées pour chaque commit, nous n'avons donc pas à nous soucier des notions de "répertoire de travail", "index", "staging", etc.
Si vous exécutez une filter-branchcommande qui échoue, elle laissera des fichiers dans le .gitrépertoire et la prochaine fois que vous l'essayerez, filter-branchelle s'en plaindra, sauf si vous fournissez l' -foption à filter-branch.
Quant à la commande proprement dite, je n'ai pas eu beaucoup de chance bashpour faire ce que je voulais, donc j'utilise plutôt zsh -cpour faire zshexécuter une commande. J'ai d'abord défini l' extended_globoption, qui active la ^(...)syntaxe dans la mvcommande, ainsi que l' glob_dotsoption, qui me permet de sélectionner des fichiers dot (tels que .gitignore) avec un glob ( ^(...)).
Ensuite, j'utilise la mkdir -pcommande pour créer les deux pluginset plugins/my-pluginen même temps.
Enfin, j'utilise la fonction zsh"glob négatif" pour ^(.git|plugins)faire correspondre tous les fichiers du répertoire racine du référentiel à l'exception de .gitet du my-plugindossier nouvellement créé . (L'exclusion .gitpeut ne pas être nécessaire ici, mais essayer de déplacer un répertoire en lui-même est une erreur.)
Dans mon référentiel, la validation initiale ne comprenait aucun fichier, donc la mvcommande a renvoyé une erreur sur la validation initiale (car rien n'était disponible pour se déplacer). Par conséquent, j'ai ajouté un || trueafin de git filter-branchne pas abandonner.
L' --alloption indique filter-branchde réécrire l'historique de toutes les branches dans le référentiel, et le supplément --est nécessaire de dire gitde l'interpréter comme une partie de la liste d'options pour les branches à réécrire, plutôt que comme une option pour filter-branchlui-même.
Maintenant, accédez à votre main-projectréférentiel et découvrez la branche dans laquelle vous souhaitez fusionner. Ajoutez votre copie locale du my-pluginréférentiel (avec son historique modifié) en tant que télécommande de main-projectavec:
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
Vous aurez maintenant deux arborescences indépendantes dans votre historique de commit, que vous pouvez visualiser correctement en utilisant:
$ git log --color --graph --decorate --all
Pour les fusionner, utilisez:
$ git merge my-plugin/master --allow-unrelated-histories
Notez que dans la version antérieure à 2.9.0 Git, l' --allow-unrelated-historiesoption n'existe pas. Si vous utilisez l'une de ces versions, omettez simplement l'option: le message d'erreur qui --allow-unrelated-historiesempêche a également été ajouté dans 2.9.0.
Vous ne devriez pas avoir de conflits de fusion. Si vous le faites, cela signifie probablement que la filter-branchcommande n'a pas fonctionné correctement ou qu'il y avait déjà un plugins/my-pluginrépertoire dans main-project.
Assurez-vous d'entrer un message de validation explicatif pour tous les futurs contributeurs se demandant quel piratage était en cours pour créer un référentiel à deux racines.
Vous pouvez visualiser le nouveau graphique de validation, qui devrait avoir deux validations racine, à l'aide de la git logcommande ci-dessus . Notez que seule la masterbranche sera fusionnée . Cela signifie que si vous avez un travail important sur d'autres my-pluginbranches que vous souhaitez fusionner dans l' main-projectarborescence, vous devez vous abstenir de supprimer la my-plugintélécommande jusqu'à ce que vous ayez effectué ces fusions. Si vous ne le faites pas, les validations de ces branches seront toujours dans le main-projectréférentiel, mais certaines seront inaccessibles et susceptibles d'être éventuellement récupérées. (De plus, vous devrez vous y référer par SHA, car la suppression d'une télécommande supprime ses branches de suivi à distance.)
Facultativement, après avoir fusionné tout ce que vous souhaitez conserver my-plugin, vous pouvez supprimer la my-plugintélécommande en utilisant:
$ git remote remove my-plugin
Vous pouvez désormais supprimer en toute sécurité la copie du my-pluginréférentiel dont vous avez modifié l'historique. Dans mon cas, j'ai également ajouté un avis de dépréciation au my-pluginréférentiel réel une fois la fusion terminée et poussée.
Testé sur Mac OS X El Capitan avec git --version 2.9.0et zsh --version 5.2. Votre kilométrage peut varier.
Références: