Il arrive des fois où la mise en place de micro-services ou tout refactoring sur du code legacy entraine le déplacement de fichiers d’un repository vers un autre. Et quelle tristesse si ce déplacement entraine la perte de l’historique des fichiers ! Imaginez-vous si vous avez à comprendre pourquoi tel ou tel choix a été fait dans le fichier mais vous n’avez pas son histoire Git. Pour ma part je me sentirai obligé de passer en mode archéologue : prendre du temps à décrypter, comprendre, imaginer ce que d’autres développeurs ont voulu faire.
Pour éviter ça, nous avons essayé pas mal de choses sans grand succès jusqu’à trouver une solution que nous vous présentons en dessous !
Préparer un espace de travail
cd /where/you/want/to/create/the/workspace
mkdir workspace
cd workspaceRéécrire l’histoire du repository source
Le but est d’avoir seulement l’historique Git des fichiers que nous voulons migrer.
Pour ça, nous commençons avec un clone du repository source qui contient les fichiers à migrer.
git clone --branch master --origin origin --progress -v https://github.com/Reacteev/IllustrateMoveFileSourceRepo.git
cd IllustrateMoveFileSourceRepoEnsuite, nous enlevons la référence à origin car il ne faudrait pas accidentellement pousser l’histoire réécrite !
git remote rm originEt nous enlevons tous les tags, qui n’ont pas besoin d’être migrer.
git tag -l | xargs git tag -dEnfin, nous réécrivons l’histoire.
git filter-branch --index-filter '
git read-tree --empty
git reset $GIT_COMMIT -- $path_to_the_files_you_want_to_migrate
' \
-- --all -- $path_to_the_files_you_want_to_migrateQuelques notes sur la commande :
filter-branchfixe$GIT_COMMITavec le SHA1 de chaque commit qui sera réécrit,- Le
--index-filter <command>permet de filtrer les fichiers au niveau de l’index (donc pas de checkout de chaque commit et est ainsi plus rapide), git read-tree --emptyvide l’index pour permettre le remplacement du contenu de l’index avec ce qui est référencé parHEAD,- Le
git resetre-initialise les fichiers spécifiés dans l’état du commit spécifié par$GIT_COMMIT, - Le
--all -- $path_to_the_files_you_want_to_migrateà la dernière ligne est transmis àgit rev-listen tant qu’options, quefilter-branchexécute.filter-branchréécrit donc seulement les commits qui sont spécifiés par les options.
Voici un exemple :
$ git filter-branch --index-filter '
> git read-tree --empty
> git reset $GIT_COMMIT -- FileToMove.md folder/FileToMove.md
> ' \
> -- --all -- FileToMove.md folder/FileToMove.md
WARNING: git-filter-branch has a glut of gotchas generating mangled history
rewrites. Hit Ctrl-C before proceeding to abort, then use an
alternative filtering tool such as 'git filter-repo'
(https://github.com/newren/git-filter-repo/) instead. See the
filter-branch manual page for more details; to squelch this warning,
set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...
Rewrite 802c4eb0346d755c32ab3744dfdb6e5ed1d756cc (1/3) (0 seconds passed, remaining 0 predicted) Unstaged changes after reset:
D FileToMove.md
D folder/FileToMove.md
Rewrite 8d53e470684bfecf4bbcb534db936add5d9ef6bb (2/3) (1 seconds passed, remaining 0 predicted) Unstaged changes after reset:
D FileToMove.md
D folder/FileToMove.md
Rewrite 610f3dedddec705c1f44570e328626b1d6cb8918 (2/3) (1 seconds passed, remaining 0 predicted) Unstaged changes after reset:
D FileToMove.md
D folder/FileToMove.md
Ref 'refs/heads/master' was rewritten
Ref 'refs/remotes/origin/master' was rewrittenA ce point, une nouvelle histoire a été réécrite.
Et juste avant la suite, retour à la racine de l’espace de travail
cd ..Récupérer l’histoire du repository source dans le repository cible
Nous commençons par récupérer le repository cible.
git clone https://github.com/Reacteev/IllustrateMoveFileTargetRepo.git
cd IllustrateMoveFileTargetRepoOptionnellement, vous pouvez choisir de changer de branche de départ et d’en créer une avant la migration des fichiers.
git checkout your-base-branch
git checkout -b gitMigrateFilesEnsuite, nous ramenons l’historique recréé dans le repository cible.
git remote add repo-source ../repo-source
git fetch repo-source masterEnfin, il suffit de merger le tout avec le repository cible pour créer un commit qui contiendra alors deux arbres sources.
git merge repo-source/master --allow-unrelated-historiesEt voici les deux arbres mergés 🙂
Puis nous finalisons et publions.
git remote rm repo-source
git push -u origin gitMigrateFiles
cd ..Nettoyer l’espace de travail
cd ..
rm -rf workspaceVoilà !