Considérez le scénario suivant:
J'ai développé un petit projet expérimental A dans son propre référentiel Git. Il a maintenant mûri et j'aimerais que A fasse partie d'un plus grand projet B, qui a son propre grand référentiel. Je voudrais maintenant ajouter A comme sous-répertoire de B.
Comment puis-je fusionner A en B, sans perdre l'historique de quelque côté que ce soit?
git
merge
repository
git-subtree
static_rtti
la source
la source
Réponses:
Une seule branche d'un autre référentiel peut être facilement placée sous un sous-répertoire conservant son historique. Par exemple:
Cela apparaîtra comme un commit unique où tous les fichiers de la branche principale de Rails sont ajoutés dans le répertoire "rails". Cependant, le titre du commit contient une référence à l'ancien arbre d'historique:
Où se
<rev>
trouve un hachage de validation SHA-1. Vous pouvez toujours voir l'historique, blâmer certains changements.Notez que vous ne pouvez pas voir le préfixe de répertoire d'ici car il s'agit d'une ancienne branche réelle laissée intacte. Vous devez traiter cela comme un commit de déplacement de fichier habituel: vous aurez besoin d'un saut supplémentaire pour l'atteindre.
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 officielle, certains gestionnaires de paquets l'installent par défaut (OS X Homebrew). Mais vous devrez peut-être l'installer vous-même en plus de git.
la source
git co v1.7.11.3
par... v1.8.3
).git log rails/somefile
n'affichera pas l'historique des validations de ce fichier, sauf la validation de la fusion. Comme l'a suggéré @artfulrobot, vérifiez la réponse de Greg Hewgill . Et vous devrez peut-être utilisergit filter-branch
le référentiel que vous souhaitez inclure.git subtree
ne faites pas ce que vous pensez! Voir ici pour une solution plus complète.Si vous souhaitez fusionner
project-a
enproject-b
:Tiré de: git fusionner différents référentiels?
Cette méthode a plutôt bien fonctionné pour moi, elle est plus courte et à mon avis beaucoup plus propre.
Dans le cas où vous souhaitez mettre
project-a
dans un sous-répertoire, vous pouvez utilisergit-filter-repo
(filter-branch
est déconseillé ). Exécutez les commandes suivantes avant les commandes ci-dessus:Un exemple de fusion de 2 grands référentiels, en plaçant l'un d'entre eux dans un sous-répertoire: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Remarque: Le
--allow-unrelated-histories
paramètre existe uniquement depuis git> = 2.9. Voir Git - git merge Documentation / --allow-unrelated-historiesMise à jour : ajoutée
--tags
comme suggéré par @jstadler afin de conserver les tags.la source
git mv source-dir/ dest/new-source-dir
git merge
étape échoue ici avecfatal: refusing to merge unrelated histories
;--allow-unrelated-histories
corrige cela comme expliqué dans la documentation .--allow-unrelated-histories
a été introduit dans git 2.9 . Dans les versions antérieures, c'était un comportement par défaut.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
.Voici deux solutions possibles:
Sous-modules
Soit copier le référentiel A dans un répertoire séparé dans le projet B plus grand, ou (peut-être mieux) cloner le référentiel A dans un sous-répertoire du projet B.Utilisez ensuite le sous-module git pour faire de ce référentiel un sous - module d'un référentiel B.
Ceci est une bonne solution pour les dépôts faiblement couplés, où le développement dans le référentiel A continue, et la majeure partie du développement est un développement autonome séparé A. Voir aussi SubmoduleSupport et GitSubmoduleTutorial pages Wiki Git.
Fusion de sous-arborescence
Vous pouvez fusionner le référentiel A dans un sous-répertoire d'un projet B à l'aide de la stratégie de fusion des sous-arbres . Ceci est décrit dans Subtree Merging and You par Markus Prinz.
(L'option
--allow-unrelated-histories
est nécessaire pour Git> = 2.9.0.)Ou vous pouvez utiliser l' outil git subtree ( référentiel sur GitHub ) par apenwarr (Avery Pennarun), annoncé par exemple dans son article de blog Une nouvelle alternative aux sous-modules Git: git subtree .
Je pense que dans votre cas (A doit faire partie d'un plus grand projet B), la bonne solution serait d'utiliser la fusion des sous-arbres .
la source
git log dir-B/somefile
ne montrera rien sauf la fusion. Voir la réponse de Greg Hewgill fait référence à ce problème important.L'approche par sous-module est bonne si vous souhaitez gérer le projet séparément. Cependant, si vous voulez vraiment fusionner les deux projets dans le même référentiel, vous avez encore un peu de travail à faire.
La première chose serait d'utiliser
git filter-branch
pour réécrire les noms de tout dans le deuxième référentiel pour être dans le sous-répertoire où vous souhaitez qu'ils se retrouvent. Donc au lieu defoo.c
,bar.html
vous auriezprojb/foo.c
etprojb/bar.html
.Ensuite, vous devriez pouvoir faire quelque chose comme ceci:
Le
git pull
fera ungit fetch
suivi d'ungit merge
. Il ne devrait pas y avoir de conflits, si le référentiel vers lequel vous tirez n'a pas encore deprojb/
répertoire.En outre la recherche indique que quelque chose de semblable a été fait pour la fusion
gitk
engit
. Junio C Hamano écrit à ce sujet ici: http://www.mail-archive.com/[email protected]/msg03395.htmlla source
git filter-branch
pour y parvenir. Dans la page de manuel, il est question du contraire: faire de subdir / devenir la racine, mais pas l'inverse.git-subtree
c'est bien, mais ce n'est probablement pas celui que vous voulez.Par exemple, si
projectA
le répertoire est créé en B, aprèsgit subtree
,répertorie un seul commit: la fusion. Les validations du projet fusionné sont pour des chemins différents, donc elles n'apparaissent pas.
La réponse de Greg Hewgill est la plus proche, même si elle ne dit pas comment réécrire les chemins.
La solution est étonnamment simple.
(1) Dans A,
Remarque: Cela réécrit l'historique, donc si vous avez l'intention de continuer à utiliser ce dépôt A, vous pouvez d'abord en cloner (copier) une copie jetable.
Remarque Bene: vous devez modifier le script de remplacement à l'intérieur de la commande sed dans le cas où vous utilisez des caractères non ascii (ou des caractères blancs) dans les noms de fichier ou le chemin. Dans ce cas, l'emplacement du fichier dans un enregistrement produit par "ls-files -s" commence par un guillemet.
(2) Puis en B, exécutez
Voila! Vous avez un
projectA
répertoire en B. Si vous exécutezgit log projectA
, vous verrez toutes les validations de A.Dans mon cas, je voulais deux sous-répertoires,
projectA
etprojectB
. Dans ce cas, j'ai également effectué l'étape (1) vers B.la source
"$GIT_INDEX_FILE"
doit être cité (deux fois), sinon votre méthode échouera si par exemple le chemin contient des espaces.Ctrl-V <tab>
Si les deux référentiels ont 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:
puis fusionnez-le dans le référentiel actuel:
Si votre version de Git est inférieure à 2,9, supprimez
--allow-unrelated-histories
.Après cela, des conflits peuvent survenir. Vous pouvez les résoudre par exemple avec
git mergetool
.kdiff3
peut être utilisé uniquement avec le clavier, donc 5 fichiers de conflit prennent lors de la lecture du code quelques minutes seulement.N'oubliez pas de terminer la fusion:
la source
J'ai continué à perdre l'historique lors de l'utilisation de la fusion, j'ai donc fini par utiliser le rebase car dans mon cas, les deux référentiels sont suffisamment différents pour ne pas finir par fusionner à chaque validation:
=> résoudre les conflits, puis continuer, autant de fois que nécessaire ...
Faire ceci conduit à un projet ayant toutes les validations de projA suivies de validations de projB
la source
Dans mon cas, j'avais un
my-plugin
référentiel et unmain-project
référentiel, et je voulais faire comme simy-plugin
j'avais toujours été développé dans leplugins
sous - répertoire demain-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 leplugins/my-plugin
sous - répertoire. Ensuite, j'ai ajouté l'historique de développement demy-plugin
dans l'main-project
histoire et j'ai fusionné les deux arbres ensemble. Puisqu'aucunplugins/my-plugin
répertoire n'était déjà présent dans lemain-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
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 (probablementmaster
) et exécutez la commande suivante. Bien sûr, vous devez remplacermy-plugin
etplugins
quels que soient vos noms réels.Maintenant pour une explication.
git filter-branch --tree-filter (...) HEAD
exécute la(...)
commande sur chaque commit accessible depuisHEAD
. 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ôtzsh -c
pour fairezsh
exécuter une commande. J'ai d'abord défini l'extended_glob
option, qui active la^(...)
syntaxe dans lamv
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 deuxplugins
etplugins/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 dumy-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 degit filter-branch
ne pas abandonner.L'
--all
option indiquefilter-branch
de réécrire l'historique de toutes les branches dans le référentiel, et le supplément--
est nécessaire de diregit
de l'interpréter comme une partie de la liste d'options pour les branches à réécrire, plutôt que comme une option pourfilter-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 dumy-plugin
référentiel (avec son historique modifié) en tant que télécommande demain-project
avec:Vous aurez maintenant deux arborescences indépendantes dans votre historique de commit, que vous pouvez visualiser correctement en utilisant:
Pour les fusionner, utilisez:
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à unplugins/my-plugin
répertoire dansmain-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 lamaster
branche sera fusionnée . Cela signifie que si vous avez un travail important sur d'autresmy-plugin
branches que vous souhaitez fusionner dans l'main-project
arborescence, vous devez vous abstenir de supprimer lamy-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 lemain-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 lamy-plugin
télécommande en utilisant: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 aumy-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
etzsh --version 5.2
. Votre kilométrage peut varier.Références:
la source
--allow-unrelated-histories
viens-tu?man git-merge
. Par défaut, la commande git merge refuse de fusionner les historiques qui ne partagent pas un ancêtre commun. Cette option peut être utilisée pour remplacer cette sécurité lors de la fusion des historiques de deux projets qui ont commencé leur vie de manière indépendante. Comme il s'agit d'une occasion très rare, aucune variable de configuration pour l'activer par défaut n'existe et ne sera pas ajoutée.git version 2.7.2.windows.1
?J'essaie de faire la même chose depuis des jours, j'utilise git 2.7.2. Subtree ne conserve pas l'histoire.
Vous pouvez utiliser cette méthode si vous n'utilisez plus l'ancien projet.
Je vous suggère de commencer par la branche B et de travailler dans la branche.
Voici les étapes sans branchement:
Si vous enregistrez maintenant l'un des fichiers dans le sous-répertoire A, vous obtiendrez l'historique complet
Ce fut le poste qui m'aide à le faire:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
la source
Si vous souhaitez placer les fichiers d'une branche dans le référentiel B dans un sous - arbre du référentiel A et également conserver l'historique, continuez à lire. (Dans l'exemple ci-dessous, je suppose que nous voulons fusionner la branche principale du repo B dans la branche principale du repo A.)
Dans le référentiel A, procédez d'abord comme suit pour rendre le référentiel B disponible:
Nous créons maintenant une toute nouvelle branche (avec un seul commit) dans le repo A que nous appelons
new_b_root
. Le commit résultant aura les fichiers qui ont été validés dans le premier commit de la branche master du repo B mais placés dans un sous-répertoire appelépath/to/b-files/
.Explication: L'
--orphan
option de la commande d'extraction extrait les fichiers de la branche principale de A mais ne crée aucun commit. Nous aurions pu sélectionner n'importe quel commit car nous effacerons tous les fichiers de toute façon. Ensuite, sans commettre encore (-n
), nous sélectionnons le premier commit de la branche principale de B. (La cerise sur le gâteau préserve le message de validation d'origine, ce qu'une extraction directe ne semble pas faire.) Ensuite, nous créons le sous-arbre où nous voulons placer tous les fichiers du repo B. Nous devons ensuite déplacer tous les fichiers qui ont été introduits dans le cueillir le sous-arbre. Dans l'exemple ci-dessus, il n'y a qu'unREADME
fichier à déplacer. Ensuite, nous validons notre commit racine B-repo et, en même temps, nous préservons également l'horodatage du commit d'origine.Maintenant, nous allons créer une nouvelle
B/master
branche au-dessus de la nouvellement crééenew_b_root
. Nous appelons la nouvelle brancheb
:Maintenant, nous fusionnons notre
b
branche enA/master
:Enfin, vous pouvez supprimer les
B
branches distantes et temporaires:Le graphique final aura une structure comme celle-ci:
la source
J'ai rassemblé beaucoup d'informations ici sur Stack OverFlow, etc., et j'ai réussi à créer un script qui résout le problème pour moi.
La mise en garde est qu'elle ne prend en compte que la branche «développer» de chaque référentiel et la fusionne dans un répertoire séparé dans un référentiel complètement nouveau.
Les balises et autres branches sont ignorées - ce n'est peut-être pas ce que vous voulez.
Le script gère même les branches de fonctionnalités et les balises - en les renommant dans le nouveau projet afin que vous sachiez d'où elles viennent.
Vous pouvez également l'obtenir sur http://paste.ubuntu.com/11732805
Créez d'abord un fichier avec l'URL de chaque référentiel, par exemple:
Appelez ensuite le script en donnant un nom au projet et le chemin d'accès au script:
Le script lui-même contient beaucoup de commentaires qui devraient expliquer ce qu'il fait.
la source
Je sais que c'est longtemps après le fait, mais je n'étais pas satisfait des autres réponses que j'ai trouvées ici, alors j'ai écrit ceci:
la source
if [[ $dirname =~ ^.*\.git$ ]]; then
Si vous essayez simplement de coller deux référentiels ensemble, les fusions de sous-modules et de sous-arborescences ne sont pas le bon outil à utiliser car elles ne préservent pas tout l'historique des fichiers (comme les gens l'ont noté dans d'autres réponses). Voir cette réponse ici pour la manière simple et correcte de le faire.
la source
J'avais un défi similaire, mais dans mon cas, nous avions développé une version de la base de code dans le référentiel A, puis cloné cela dans un nouveau référentiel, le référentiel B, pour la nouvelle version du produit. Après avoir corrigé quelques bugs dans le référentiel A, nous avions besoin de FI les modifications dans le référentiel B. Nous avons fini par faire ce qui suit:
A travaillé un régal :)
la source
Similaire à @Smar mais utilise des chemins de système de fichiers, définis dans PRIMARY et SECONDARY:
Ensuite, vous fusionnez manuellement.
(adapté de l' article d'Anar Manafov )
la source
Fusion de 2 dépôts
la source
Lorsque vous souhaitez fusionner trois projets ou plus en une seule validation, procédez comme décrit dans les autres réponses (
remote add -f
,merge
). Ensuite, (soft) réinitialise l'index à l'ancienne tête (où aucune fusion n'a eu lieu). Ajoutez tous les fichiers (git add -A
) et validez-les (message "Fusion des projets A, B, C et D en un seul projet). Il s'agit désormais de l'ID de validation de master.Maintenant, créez
.git/info/grafts
avec le contenu suivant:Courez
git filter-branch -- head^..head head^2..head head^3..head
. Si vous avez plus de trois branches, ajoutez simplement autanthead^n..head
que vous avez de branches. Pour mettre à jour les balises, ajoutez--tag-name-filter cat
. N'ajoutez pas toujours cela, car cela pourrait entraîner une réécriture de certains commits. Pour plus de détails, voir la page de manuel de filter-branch , recherchez "greffe".Maintenant, votre dernier commit a les bons parents associés.
la source
Pour fusionner un A dans B:
1) Dans le projet A
2) Dans le projet B
Dans cette branche, effectuez toutes les opérations nécessaires et validez-les.
C) Puis revenons au master et une fusion classique entre les deux branches:
la source
Cette fonction clonera le repo distant dans le répertoire repo local, après la fusion de toutes les validations seront enregistrées,
git log
affichera les validations d'origine et les chemins d'accès appropriés:Comment utiliser:
Si vous apportez quelques modifications, vous pouvez même déplacer des fichiers / répertoires du référentiel fusionné dans différents chemins, par exemple:
Avis Les
chemins remplacent via
sed
, assurez-vous donc qu'il s'est déplacé dans les bons chemins après la fusion.Le
--allow-unrelated-histories
paramètre n'existe que depuis git> = 2.9.la source
La commande donnée est la meilleure solution possible que je suggère.
la source
Je fusionne les projets légèrement manuellement, ce qui me permet d'éviter d'avoir à gérer les conflits de fusion.
copiez d'abord les fichiers de l'autre projet comme vous le souhaitez.
prochaine traction dans l'histoire
dire à git de fusionner dans l'histoire de la dernière chose récupérée
engagez maintenant mais vous vous engagez normalement
la source
Je voulais déplacer un petit projet vers un sous-répertoire d'un plus grand. Comme mon petit projet n'avait pas beaucoup de commits, j'ai utilisé
git format-patch --output-directory /path/to/patch-dir
. Ensuite, sur le plus grand projet, j'ai utiliségit am --directory=dir/in/project /path/to/patch-dir/*
.Cela semble beaucoup moins effrayant et beaucoup plus propre qu'une branche de filtre. Certes, elle peut ne pas s'appliquer à tous les cas.
la source