Extraire un dossier d’un dépôt git avec suivi du renommage

git contient une commande permettant d’extraire un dossier d’un dépôt existant. C’est pratique lorsque le dépôt contenait initialement un seul projet, qui a évolué au cours du temps, grossit, et est devenu un truc trop gros pour rester dans un dépôt unique. Cela se fait avec la commande git –subdirectory , mais cette commande ne suivait pas le renommage du dossier à extraire et l’on se retrouvait donc avec un historique tronqué.

Le script ci-dessous fonctionne (c’est déjà ça), en travaillant sur le dépôt lui-même plutôt que l’espace de travail, lequel ne reflète qu’une version spécifique. Il :

  • ne garde du dépôt que tout ce qui se trouve ou bien dans Kveer.MyAwesomeProject, ou bien dans MyAwesomeProject, qui sont un même dossier que j’ai renommé à un moment donné
  • il fait remonter à la racine le contenu des dossiers susmentionnés
  • quitte à casser tout l’historique, j’en profite pour unifier le nom et l’email utilisé dans les commits (on pourra ajouter une condition pour ne réécrire que ses propres commits)
  • je supprime le dossier package pour réduire la taille du dépôt
  • plus qu’à nettoyer
  • le nouveau dépôt est prêt à être chargé.
# extracts the subdirectory
# i take this opportunity to rewrite author name and email of my commits
git filter-branch --prune-empty --env-filter '
GIT_AUTHOR_EMAIL=my-awesome-email@kveer.fr
GIT_AUTHOR_NAME=Veovis' --index-filter '
    # Delete files which are NOT needed
    git ls-files -z | egrep -zv  "^(Kveer.MyAwesomeProject|MyAwesomeProject)" |
        xargs -0 -r git rm --cached -q
    # Move files to root directory
    git ls-files -s | sed -e "s-\t\(Kveer.MyAwesomeProject\|MyAwesomeProject\)/-\t-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
        ( test ! -f "$GIT_INDEX_FILE.new" \
            || mv -f "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" )
'

# deletes the backup ref
git update-ref -d refs/original/refs/heads/master

# more cleaning by removing a directory that should not be tracked
git filter-branch --index-filter \
  'git rm --ignore-unmatch --cached packages'

# deletes the backup ref
git update-ref -d refs/original/refs/heads/master

# cleans the repo
git gc
git repack -Ad


La commande subdirectory ne doit pas servir à modifier un dépôt existant. En effet il réécrit tous les commits, tous les hash sont différents. Le nouveau dépô n’aura donc plus aucun lien avec l’ancien dépôt et il sera impossible de faire un push autrement que sur un dépôt vierge ou bien en forçant le passage.

Sources