Remplacement et greffe
git replace dit à git : partout où tu allais lire l'objet A, lis B à la
place. Rien n'est réécrit. Aucun sha ne change. Chaque commit reste exactement
là où il était — git regarde simplement ailleurs au passage.
Cela ressemble à une curiosité jusqu'au jour où vous voulez un clone plus petit. C'est alors l'alternative honnête à une réécriture d'historique : greffez un commit sur aucun parent et tout ce qui précède disparaît du journal, du graphe et de tout clone effectué à partir de là — tout en restant stocké, toujours récupérable, et à une seule référence supprimée de son retour.
⌘K → Remplacement et greffe.

Greffer
| Donnez-lui | Et vous obtenez |
|---|---|
| Un commit, aucun parent | Ce commit devient le début de l'histoire |
| Un commit, un ou plusieurs parents | Il s'accroche là plutôt qu'à sa vraie place |
La seconde forme est la plus intéressante. Gardez l'historique complet dans un dépôt d'archive, tronquez celui de travail, et une greffe pointant vers la pointe de l'archive rattache les deux — la même astuce que GitHub emploie pour servir un clone superficiel qui peut malgré tout être approfondi.
Greffer sur aucun parent demande confirmation d'abord, parce que
« l'historique est parti » et « l'historique est caché » se ressemblent
parfaitement vus du journal, et ne sont pas du tout la même chose. Les objets
survivent jusqu'à ce qu'un gc les élague ; voir la
maintenance.
Vivre avec
Les remplacements sont des références, sous refs/replace/. Cela a trois
conséquences qu'il vaut la peine de connaître :
- Ils sont locaux tant qu'ils ne sont pas poussés :
git push origin "refs/replace/*"les partage, et quiconque clone sans eux voit l'historique intact. - L'annulation fonctionne — supprimer la référence restaure l'ascendance réelle immédiatement, et Gitcito enregistre la greffe comme une action annulable, comme n'importe quelle autre.
core.useReplaceRefs=falsefait ignorer tous les remplacements d'un coup par git. La bascule d'ici écrit exactement cela, et la boîte de dialogue le signale quand elle est désactivée, parce qu'un dépôt qui ignore silencieusement ses propres remplacements est un endroit déroutant.
En ligne de commande, git --no-replace-objects log montre l'histoire réelle
sans changer le moindre réglage.
Quand recourir à ceci plutôt qu'à une réécriture
| Objectif | Outil |
|---|---|
| Le clone est trop gros, l'historique va bien | Greffe — rien de réécrit, réversible |
| Un secret ou un blob énorme doit disparaître | Retirer un fichier de l'historique — une vraie réécriture |
| Vous voulez juste moins à télécharger, une fois | git clone --depth — superficiel, aucune référence à gérer |
Une greffe ne retire rien. Si la raison pour laquelle vous voulez sortir les vieux commits est qu'ils contiennent quelque chose qui n'aurait jamais dû être validé, ce n'est pas la bonne page : les objets sont toujours là, toujours récupérables par sha, et toujours dans chaque clone existant.
Limites qu'il vaut mieux connaître
- Ce que vous voyez cesse de correspondre à ce qui est stocké. C'est la fonctionnalité, et le danger. Quiconque débogue un clone avec des remplacements doit savoir qu'ils existent.
- Les remplacements ne voyagent pas par défaut : le
git logd'un collègue et le vôtre peuvent donc légitimement diverger. - Un remplacement peut cacher un commit aux outils, pas à git.
git cat-fileet l'explorateur d'objets ouvrent toujours l'original par son sha. - Gitcito ne propose pas
git replace --edit(réécrire à la main le contenu d'un objet). C'est le travail d'un éditeur de texte sur un objet brut, et un tire-au-pied avec une interface autour.
Voir aussi : Explorateur d'objets · Retirer un fichier de l'historique · Maintenance du dépôt