Dans mon cas, j'avais un my-plugin
référentiel et un main-project
référentiel, et je voulais faire comme si my-plugin
j'avais toujours été développé dans le plugins
sous - répertoire de main-project
.
Fondamentalement, j'ai réécrit l'histoire du my-plugin
référentiel afin qu'il apparaisse que tout le développement a eu lieu dans le plugins/my-plugin
sous - répertoire. Ensuite, j'ai ajouté l'historique de développement de my-plugin
dans l' main-project
histoire et j'ai fusionné les deux arbres ensemble. Puisqu'aucun plugins/my-plugin
répertoire n'était déjà présent dans le main-project
ré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-plugin
référentiel, car nous allons réécrire l'historique de ce référentiel.
Maintenant, accédez à la racine du my-plugin
référentiel, consultez votre branche principale (probablement master
) et exécutez la commande suivante. Bien sûr, vous devez remplacer my-plugin
et plugins
quels 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 (...) HEAD
exé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-branch
commande qui échoue, elle laissera des fichiers dans le .git
répertoire et la prochaine fois que vous l'essayerez, filter-branch
elle s'en plaindra, sauf si vous fournissez l' -f
option à filter-branch
.
Quant à la commande proprement dite, je n'ai pas eu beaucoup de chance bash
pour faire ce que je voulais, donc j'utilise plutôt zsh -c
pour faire zsh
exécuter une commande. J'ai d'abord défini l' extended_glob
option, qui active la ^(...)
syntaxe dans la mv
commande, ainsi que l' glob_dots
option, qui me permet de sélectionner des fichiers dot (tels que .gitignore
) avec un glob ( ^(...)
).
Ensuite, j'utilise la mkdir -p
commande pour créer les deux plugins
et plugins/my-plugin
en 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 .git
et du my-plugin
dossier nouvellement créé . (L'exclusion .git
peut 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 mv
commande a renvoyé une erreur sur la validation initiale (car rien n'était disponible pour se déplacer). Par conséquent, j'ai ajouté un || true
afin de git filter-branch
ne pas abandonner.
L' --all
option indique filter-branch
de réécrire l'historique de toutes les branches dans le référentiel, et le supplément --
est nécessaire de dire git
de l'interpréter comme une partie de la liste d'options pour les branches à réécrire, plutôt que comme une option pour filter-branch
lui-même.
Maintenant, accédez à votre main-project
référentiel et découvrez la branche dans laquelle vous souhaitez fusionner. Ajoutez votre copie locale du my-plugin
référentiel (avec son historique modifié) en tant que télécommande de main-project
avec:
$ 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-histories
option n'existe pas. Si vous utilisez l'une de ces versions, omettez simplement l'option: le message d'erreur qui --allow-unrelated-histories
empê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-branch
commande n'a pas fonctionné correctement ou qu'il y avait déjà un plugins/my-plugin
ré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 log
commande ci-dessus . Notez que seule la master
branche sera fusionnée . Cela signifie que si vous avez un travail important sur d'autres my-plugin
branches que vous souhaitez fusionner dans l' main-project
arborescence, vous devez vous abstenir de supprimer la my-plugin
té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-project
ré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-plugin
télécommande en utilisant:
$ git remote remove my-plugin
Vous pouvez désormais supprimer en toute sécurité la copie du my-plugin
référentiel dont vous avez modifié l'historique. Dans mon cas, j'ai également ajouté un avis de dépréciation au my-plugin
ré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.0
et zsh --version 5.2
. Votre kilométrage peut varier.
Références: