Extract a subfolder as a new repository

Dans certains cas d’architectures logicielles modulaires il peut être intéressant d’extraire l’un des modules afin de le rendre indépendant du projet parent.

Plutôt que copier bêtement le code source dans un nouveau repository et perdre un historique généralement précieux, nous pouvons utiliser git pour conserver cet historique et le remettre au propre.

Nous allons ici extraire le répertoire subfolder1 du projet dans un nouveau repository.

Partons tout d’abord du projet source et copions le à part, dans un workspace pour le nouveau projet.

cp -R /home/dev/myproject /home/dev/newmodule

Rendons nous ensure dans le répertoire du module et purgeons la remote, celle-ci pointant sur le répertoire local source.

cd /home/dev/newmodule

On purge les tags qui n’ont pas subfolder1 à leur racine.

# Purge non related tags
for T in $(git tag -l); do
  git show ${T}:subfolder1>/dev/null;
  if [ $? -ne 0 ]; then
    echo "No subfolder1 in ${T}" && git tag -d ${T};
  fi;
done

On réécrit maintenant l’historique git afin de ne garder que le sous répertoire subfolder1 et le remonter à la racine, pour toutes les branches.

git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter subfolder1 -- --all

Ensuite on nettoie l’historique git en réalignant les références.

git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

Pour finir on nettoie le workspace

git reflog expire --expire=now --all
git gc --aggressive --prune=now

Et on pousse les informations sur le repository distant.

git remote add newrepo git@gitlab.com:myself/newmodule.git
git push newrepo master
git push newrepo --tags
for B in $(git branch -r | grep -v newrepo | grep -v tags); do
  git push newrepo +${B}:refs/heads/${B}
done

La dernière boucle est intéressante car elle permet de pousser les branches mises à jour provenant de la remote origin vers la remote newrepo sans effectuer de checkout.

See Also