Comment faire pour déplacer des fichiers d'un repo git à l'autre (pas un clone), la préservation de l'histoire
Nos dépôts Git a commencé comme des parties d'un seul monstre dépôt SVN où les projets individuels avaient chacun leur propre arbre de la sorte:
project1/branches
/tags
/trunk
project2/branches
/tags
/trunk
De toute évidence, il était assez facile de déplacer des fichiers de l'un à l'autre avec svn mv
. Mais dans Git, chaque projet est dans son propre référentiel, et aujourd'hui on m'a demandé de déplacer un sous-répertoire de project2
à project1
. J'ai fait quelque chose comme ceci:
$ git clone project2
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin # so I don't accidentally the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do
> git mv $f deeply/buried/different/java/source/directory/B
> done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9
$ git remote rm p2
$ git push
Mais qui semble assez compliquée. Est-il une meilleure façon de faire ce genre de chose en général? Ou ai-je adopté la bonne approche?
Note que cela implique la fusion de l'histoire dans un référentiel existant, plutôt que de simplement créer un nouveau autonome référentiel de la partie d'un autre (comme dans une question précédente).
- Qui sonne comme une approche raisonnable pour moi; je ne peux pas penser à une façon évidente d'améliorer de façon significative votre méthode. C'est bien que Git ne fait rendre cela facile (je ne voudrais pas essayer de déplacer un répertoire de fichiers entre différents référentiels dans la Subversion, par exemple).
- Aussi je ne voudrais pas essayer de déplacer un répertoire entre les deux svn repos! (J'imagine que certains cauchemar impliquant svnadmin dump et svn dumpfilter, bleah.)
- J'ai fait cela (déplacé l'histoire d'un repo svn à l'autre) manuellement, à l'aide de scripts shell. Fondamentalement, j'ai relu l'histoire (des diffs, journaux de messages) à partir de fichiers/répertoires dans un deuxième référentiel.
- Je me demande pourquoi vous ne le faites pas
git fetch p2 && git merge p2
au lieu degit fetch p2 && git branch .. && git merge p2
? Edit: bon, il semble que vous souhaitez pour obtenir les changements dans une nouvelle branche nommée p2, pas la branche courante. - À seulement porter sur les commits de l'autre repo qui implique le répertoire que vous vous déplacez, vous devez ajouter
--prune-empty
à lagit filter-branch
- J'ai également trouvé ce thread dans le cas où les fichiers que vous avez déplacé à partir de project2 eu beaucoup de l'histoire et que vous vouliez en permanence effacer tous les enregistrements d'entre eux, puisque vous avez l'histoire dans un deuxième projet: dalibornasevic.com/posts/...
- Il n'existe aucun moyen pour empêcher l' --filter-branch de détruire la structure de répertoire? Que "git mv" l'étape des résultats dans un massif de commettre pleine des suppressions de fichiers et les fichiers de créations.
- Noter que git 2.9 fusion sans rapport avec l'histoire, interdites par défaut. Pour le faire fonctionner, ajouter
--allow-unrelated-histories
à la dernièregit merge
pour le faire fonctionner. - Double Possible de Détacher (se déplacer) sous-répertoire en séparer dépôt Git
- ce n'est pas vraiment un doublon, car cela implique la fusion de choses dans un des pensions de l'histoire.
Vous devez vous connecter pour publier un commentaire.
Yep, frapper sur le
--subdirectory-filter
defilter-branch
a été la clé. Le fait que vous avez utilisé essentiellement, il s'avère il n'y a pas de moyen plus facile - vous n'avait pas le choix, mais de réécrire l'histoire, puisque vous avez voulu terminer avec seulement un (renommé) sous-ensemble des fichiers, et ce, par définition, change les valeurs de hachage. Car aucune des commandes standard (par exemplepull
) réécrire l'histoire, il n'y a aucun moyen que vous pourriez utiliser pour accomplir cette tâche.Vous a permis d'affiner les détails, bien sûr - certains de vos clonage et la ramification n'était pas strictement nécessaire - mais l'ensemble de la démarche est la bonne! C'est une honte, c'est compliqué, mais bien sûr, le point de git n'est pas à prendre, il est facile de réécrire l'histoire.
--index-filter
dans lefilter-branch
page de manuel.Si votre histoire est sain d'esprit, vous pouvez prendre l'engage comme patch et de les appliquer dans le nouveau référentiel:
Ou dans une ligne
(Prises de Exherbo de docs)
git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch
. Fonctionne sans problèmes AFAICT.--committer-date-is-author-date
option pour conserver l'original de commettre date au lieu de la date où les fichiers ont été déplacés.--follow
option pourgit log
(qui ne fonctionne qu'avec un seul fichier à la fois).--color=never
àgit log
si vous utilisez--color=always
quelle que soit la raison de votre gitconfig.sed
expression. Mais depuis, il devrait y avoir des balises qui ne sont pas pertinents (tant que vous ne bougez pas nécessaire s'engage), j'aimerais trouver ce genre de pratiques dangereuses.git am
mais j'ai couru dans laPatch is empty. Was it split wrong?
. L'erreur n'était pas levée immédiatement, mais après l'application de certaines modifications.git apply
oupatch -p1
aide.-m --first-parent
d'avoir ces différences dans votre patch, comme Gábor Lipták suggèregit log
, de sorte qu'il ne fonctionne pas avec les deux--follow
et--reverse
correctement). J'ai utilisé cette réponse voici un script complet que j'utilise maintenant pour déplacer des fichiers--committer-date-is-author-date
, voir le commentaire de @darrenmcAvoir essayé différentes approches pour déplacer un fichier ou un dossier à partir d'un dépôt Git à l'autre, le seul qui semble fonctionner de manière fiable, est décrite ci-dessous.
Il consiste à cloner le dépôt dans lequel vous souhaitez déplacer le fichier ou le dossier, le déplacement de ce fichier ou un dossier à la racine, la réécriture historique de Git, le clonage de la cible de dépôt et d'extraire le fichier ou le dossier avec l'histoire directement dans ce référentiel cible.
Scène Un
Faire une copie d'Un référentiel comme les étapes suivantes majeure
modifications apportées à cette copie, vous ne devez pas pousser!
cd en il
Supprimer le lien vers l'original de référentiel pour éviter accidentellement
toute prise de distance des modifications (par exemple. en poussant)
Passer par votre histoire et vos fichiers, en supprimant tout ce qui n'est pas dans
directory 1. Le résultat est le contenu du répertoire 1 vomie
sur la base du référentiel de A.
Pour seul fichier seulement se déplacer: ce que la gauche et retirez
tout, sauf le fichier désiré. (Vous devrez peut-être supprimer les fichiers
vous ne voulez pas avec le même nom et valider.)
Scène Deux
Étape de nettoyage
Étape de nettoyage
Étape de nettoyage
Vous pouvez importer ces fichiers dans le référentiel B dans un répertoire non la racine:
Faire ce répertoire
Déplacer des fichiers dans ce répertoire
Ajouter des fichiers dans ce répertoire
Valider vos modifications et nous sommes prêts à fusionner ces fichiers dans le
nouveau référentiel
Scène Trois
Faire une copie du référentiel de B si vous n'en avez pas déjà
(en supposant que FOLDER_TO_KEEP est le nom du nouveau référentiel de copie)
cd en il
Créer une connexion à distance à Un référentiel comme une branche dans le dépôt
B
Tirer de cette branche (contenant uniquement le répertoire que vous voulez
déplacer) dans le référentiel B.
Le tirer des copies de fichiers et de l'histoire. Remarque: Vous pouvez utiliser une fusion au lieu d'un tirez, mais tirez fonctionne mieux.
Enfin, vous voulez probablement pour nettoyer un peu en retrait de la distance
connexion à Un référentiel
Pousser, et vous êtes tous ensemble.
J'ai trouvé cette très utile. C'est une approche très simple où vous créez des correctifs qui sont appliquées à la nouvelle repo. Voir la page du lien pour plus de détails.
Il contient seulement trois étapes (copié sur le blog):
Le seul problème que j'ai eu était que je ne pouvais pas appliquer tous les patchs à la fois à l'aide de
Sous Windows, j'ai eu une InvalidArgument erreur. J'ai donc dû appliquer tous les patchs, l'un après l'autre.
--src-prefix=./ --dst-prefix=./
pour éviter une erreur lors de la tentative d'appliquer les patchs.EN GARDANT LE NOM DU RÉPERTOIRE
Le sous-répertoire-filtre (ou le plus court de commande git subtree) fonctionne bien, mais ne fonctionne pas pour moi, car ils ont supprimer le nom du répertoire à partir de la validation de l'info. Dans mon scénario, je veux juste de fusionner les parties d'un référentiel à l'autre et conserver l'histoire AVEC un nom de chemin complet.
Ma solution a été d'utiliser l'arbre-filtre et simplement supprimer les fichiers indésirables et des répertoires à partir d'un clone temporaire de la source de dépôt, puis tirez à partir de ce clone dans mon référentiel cible en 5 étapes simples.
Cette réponse offrir intéressant de commandes basées sur les
git am
et présenté à l'aide d'exemples, étape par étape.Objectif
Procédure
git log --pretty=email -p --reverse --full-index --binary
git am
1. Extrait de l'histoire au format électronique
Exemple: Extrait de l'histoire de
file3
,file4
etfile5
Nettoyer le répertoire temporaire destination
Nettoyer votre le repo source
Extrait de l'histoire de chaque fichier au format électronique
Malheureusement option
--follow
ou--find-copies-harder
ne peut pas être combiné avec--reverse
. C'est pourquoi l'histoire est coupée lorsque le fichier est renommé (ou lorsqu'un parent directory est renommé).Après: Temporaire histoire au format électronique
2. Réorganiser l'arborescence de fichiers et de mettre à jour le nom de fichier du changement de l'histoire [facultatif]
Supposons que vous souhaitez déplacer ces trois fichiers dans ce repo (peut être le même repo).
Donc réorganiser vos fichiers:
Temporaire de l'histoire est maintenant:
Aussi changer les noms de fichiers dans l'histoire:
Remarque: Ce réécrit l'histoire afin de refléter le changement de chemin d'accès et nom de fichier.
(c'est à dire le changement de la nouvelle localisation/nom dans le nouveau repo)
3. Appliquer une nouvelle histoire
Vos autres pensions est:
Appliquer s'engage temporaires, l'historique des fichiers:
Vos autres pensions est maintenant:
Utilisation
git status
pour voir la quantité de commits prêt à pousser 🙂Remarque: Que l'histoire a été réécrite afin de refléter le chemin d'accès et nom de fichier modification:
(c'est à dire par rapport à l'emplacement/nom au sein de la précédente repo)
git mv
pour modifier l'emplacement/nom de fichier.git log --follow
pour avoir accès à la totalité de l'histoire.Particularité supplémentaire: Détecter renommé/fichiers qui ont été déplacés à l'intérieur de votre repo
Pour afficher la liste des fichiers ayant été renommés:
Plus de personnalisations: Vous pouvez compléter la commande
git log
à l'aide des options--find-copies-harder
ou--reverse
. Vous pouvez également supprimer les deux premières colonnes à l'aide decut -f3-
et grepping modèle complet '{.* => .*}'.Celui que j'ai toujours utiliser est ici http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Il est Simple et rapide.
De la conformité avec stackoverflow normes, voici la procédure:
Avoir eu ce genre de démangeaisons à zéro (bien que pour certains fichiers d'un référentiel) ce script s'est avéré être vraiment utile: git-import
La version courte, c'est qu'il crée des fichiers patch du fichier ou répertoire donné (
$object
) à partir du référentiel existant:qui ensuite appliquée à un nouveau référentiel:
Pour plus de détails, veuillez consulter:
Essayer cette
cd repo1
Cela va supprimer tous les répertoires à l'exception de celles mentionnées, la préservation de l'histoire seulement pour ces répertoires
Maintenant vous pouvez ajouter votre nouveau repo dans votre dépôt git distant et de le pousser à que
ajouter
-f
pour remplacerÀ l'aide de l'inspiration à partir de http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , j'ai créé cette fonction Powershell pour en faire de même, qui a beaucoup travaillé pour moi pour l'instant:
D'utilisation pour cet exemple:
Après vous avez fait cela, vous pouvez ré-organiser les fichiers sur le
migrate-from-project2
branche avant la fusion elle.Je voulais quelque chose de robuste et réutilisable (on-command-and-go + fonction undo) donc j'ai écrit le script bash suivant. A fonctionné pour moi à plusieurs reprises, donc je pensais que je voudrais partager ici.
Il est capable de se déplacer d'un arbitraire dossier
/path/to/foo
derepo1
en/some/other/folder/bar
àrepo2
(les chemins de dossier peuvent être identiques ou différents, la distance de la racine du dossier peut être différent).Car il ne va que sur les commits qui touche les fichiers dans le dossier d'entrée (pas sur tous les commits de la source repo), il devrait être assez rapide, même sur de gros source de repos, si vous venez d'extraire une profondément imbriqués sous-dossier qui n'a pas été touché dans chaque livraison.
Depuis ce qu'il fait est de créer un orphelin de la direction, avec toutes les anciennes pensions de l'histoire et ensuite de le fusionner à la TÊTE, il fonctionnera même en cas de nom de fichier affrontements (alors que vous auriez à résoudre une fusion à la fin du cours).
Si il n'y a pas de nom de fichier affrontements, vous avez juste besoin de
git commit
à la fin pour finaliser la fusion.L'inconvénient est qu'il ne sera probablement pas suivre renomme de fichier (en dehors de
REWRITE_FROM
dossier) dans la source repo - pull demandes bienvenue sur GitHub, pour accueillir pour que.GitHub lien: git-move-dossier-entre-repos-garder-histoire
Dans mon cas, je n'ai pas besoin de conserver le repo j'ai été la migration à partir de ou préserver toute l'histoire précédente. J'ai eu un patch de la même branche, d'un autre à distance
Dans ces deux étapes, j'ai été en mesure d'obtenir les autres pensions de la direction générale apparaître dans le même repo.
Enfin, j'ai mis cette branche (que j'ai importé de l'autre repo) pour suivre la cible des pensions de la canalisation principale (donc je pourrais diff avec précision)
Maintenant, il s'est comporté comme si c'était juste une autre branche que j'avais poussé à l'encontre même des pensions.
Si les chemins d'accès pour les fichiers en question sont les mêmes dans les deux repos et vous êtes désireux de les amener sur un seul fichier ou un petit ensemble de fichiers, un moyen facile de faire cela est d'utiliser
git cherry-pick
.La première étape consiste à réunir les commits de l'autre repo dans vos propres locaux des pensions de l'aide
git fetch <remote-url>
. Cela laisseraFETCH_HEAD
pointant vers la tête de commettre d'autres opérations de pension; si vous voulez conserver une référence pour qui s'engagent après que vous avez fait d'autres extractions vous pouvez marquer avecgit tag other-head FETCH_HEAD
.Vous devrez ensuite créer un premier de s'engager pour que le fichier s'il n'existe pas) ou s'engager à apporter le fichier à un état qui peut être corrigé avec le premier commit de l'autre dépôt que vous souhaitez apporter. Vous pourriez être en mesure de le faire avec un
git cherry-pick <commit-0>
sicommit-0
introduit les fichiers que vous souhaitez, ou vous pouvez avoir besoin pour construire le commettre "à la main". Ajouter-n
à la piocher dans les options si vous avez besoin de modifier la première s'engager à, par exemple, déposer des fichiers à partir de ce commit, vous ne voulez pas mettre en.Après cela, vous pouvez continuer à
git cherry-pick
ultérieure s'engage à nouveau à l'aide-n
le cas échéant. Dans le cas le plus simple (tous les commits sont exactement ce que vous voulez et de les appliquer correctement), vous pouvez donner la liste complète de commits sur le cherry-pick de la ligne de commande:git cherry-pick <commit-1> <commit-2> <commit-3> ...
.La méthode ci-dessous pour migrer mon GIT Stash à GitLab par le maintien de toutes les branches et la préservation de l'histoire.
Clone de l'ancien dépôt local.
Créer un dépôt vide dans GitLab.
Ci-dessus j'ai effectué lorsque nous avons migré notre code de cachette pour GitLab et ça a très bien fonctionné.