Il y a des instructions dans la page de manuel de git-filter-branch(1)
à ce sujet :
Pour définir un commit (qui est généralement à la pointe d'une autre historique) comme parent du commit initial actuel, afin de coller l'autre historique derrière l'historique actuel :
git filter-branch --parent-filter 'sed "s/^\$/-p /"' HEAD
(si la chaîne parent est vide - ce qui se produit lorsque nous traitons le commit initial - ajoutez graftcommit comme parent). Notez que cela suppose une historique avec une seule racine (c'est-à-dire, aucun merge sans ancêtres communs ne s'est produit).
En suivant une suggestion de Kent, vous voudrez peut-être ajouter --tag-name-filter cat
à cette commande pour réécrire les noms des étiquettes vers les nouveaux commits. Cela est également documenté dans la page de manuel, avec un avertissement de sauvegarder d'abord les anciennes étiquettes :
Les étiquettes originales ne sont pas supprimées, mais peuvent être écrasées ; utilisez "--tag-name-filter cat" pour simplement mettre à jour les étiquettes. Dans ce cas, soyez très prudent et assurez-vous d'avoir sauvegardé les anciennes étiquettes au cas où la conversion échoue.
La page de manuel explique également pourquoi les signatures doivent être supprimées lors de la réécriture des étiquettes :
La réécriture quasi correcte des objets étiquettes est prise en charge. Si l'étiquette a un message attaché, un nouvel objet étiquette sera créé avec le même message, auteur et horodatage. Si l'étiquette a une signature attachée, la signature sera supprimée. Il est par définition impossible de préserver les signatures. La raison pour laquelle cela est "quasi" correct, c'est parce qu'idéalement si l'étiquette n'a pas changé (pointe vers le même objet, a le même nom, etc.), elle devrait conserver toute signature. Ce n'est pas le cas, les signatures seront toujours supprimées, l'acheteur est prévenu. Il n'y a également aucun support pour changer l'auteur ou l'horodatage (ou le message de l'étiquette pour cette question). Les étiquettes qui pointent vers d'autres étiquettes seront réécrites pour pointer vers le commit sous-jacent.
Après l'exemple de la racine unique, la page de manuel continue avec des instructions pour les multiples racines :
Si ce n'est pas le cas, utilisez :
git filter-branch --parent-filter \
'test $GIT_COMMIT = && echo "-p " || cat' HEAD
ou même plus simplement :
git replace --graft $commit-id $graft-id
git filter-branch $graft-id..HEAD
Utiliser git filter-branch
est bien plus propre que d'utiliser git --rebase
, qui fonctionne comme une combinaison de diff
et patch
, appliquant les changements de chaque commit sur un autre. Au lieu de cela, git filter-branch
laisse les fichiers actuels de chaque commit intacts et modifie directement le pointeur du commit parent.
Évidemment, les nouveaux commits auront des SHAs différents des originaux - c'est inévitable puisque le commit parent est inclus dans le calcul du SHA.
Vous pouvez également essayer le script de Mark Lodato git-reparent
, qui semble utiliser des commandes de plomberie git pour obtenir un résultat similaire.