Leçons 1/4 · 25 min
L'historique Git : commits, branches et réécriture maîtrisée
Une question de départ
Deux étudiants travaillent sur le même fichier. L'un l'envoie à l'autre par messagerie ; l'autre répond avec « version finale 2 ». Trois jours plus tard, personne ne sait quelle version est la bonne. Git résout ce problème : il garde l'historique de chaque modification, qui l'a faite, quand, et pourquoi.
Ce qu'est un commit
Un commit est un instantané de tous les fichiers du projet à un moment donné, avec : un auteur, une date, un message, et un lien vers le ou les commits parents. Git identifie chaque commit par une empreinte calculée à partir de tout ce contenu, y compris l'empreinte du parent. Historiquement, cette empreinte est un SHA-1 ; Git sait aussi fonctionner avec SHA-256 (voir la note plus bas).
Comme pour une blockchain, changer un commit ancien change son empreinte, donc celle de tous ses descendants. Dans l'outil de hachage, modifiez une lettre d'un texte et observez : l'empreinte est totalement différente. Dans l'explorateur de chaîne, modifiez le bloc 2 : tous les suivants « cassent ». C'est la même idée.
Deux différences honnêtes avec une blockchain :
- Il n'y a pas de consensus. Chacun possède une copie complète et peut réécrire son historique local. La « vérité » du projet est ce que l'équipe a convenu de considérer comme dépôt de référence (en général la branche
maindu dépôt principal). - Une empreinte Git identifie un contenu ; elle ne prouve pas qui l'a écrit (sauf signature de commit, voir leçon 4).
Les trois zones
dossier de travail --git add--> zone d'index (staging) --git commit--> dépôt (historique)git statusmontre ce qui a changé et ce qui est prêt à être validé.git add fichierchoisit ce qui entrera dans le prochain commit (un commit = une idée, pas tout ce que vous avez touché).git commitcrée l'instantané.
Branches : des étiquettes qui bougent
Une branche est un simple pointeur vers un commit. Créer une branche ne copie rien :
git switch -c feature/fees # crée et se place sur la branche
git log --oneline --graph --decorate --allExemple de sortie :
* 9f3c2ab (HEAD -> feature/fees) Add fee estimate helper
* 41d7e90 Fix typo in README
| * c0a11fe (origin/main, main) Update CI node version
|/
* 7b2e5d1 Initial importLecture : à partir du commit initial 7b2e5d1, main a avancé d'un commit (c0a11fe) et feature/fees de deux. HEAD est la branche sur laquelle vous êtes. origin/main est ce que vous savez de main sur le serveur au dernier git fetch.
De bons messages de commit
Un message est lu des mois plus tard, par quelqu'un qui cherche pourquoi une ligne existe. Convention répandue (pas une loi) :
Reject negative amounts in transfer()
A negative amount credited the sender. Validate amount > 0
and add tests for -1 and 0.Un sujet court à l'impératif (environ 50 caractères), une ligne vide, puis le pourquoi. Un message « fix » ou « WIP » n'apprend rien. Un commit qui mélange trois sujets ne peut pas être annulé proprement.
Réécriture maîtrisée
Git permet de réécrire l'historique : git commit --amend (le dernier commit), git rebase -i HEAD~3 (regrouper, renommer, réordonner trois commits). Chaque réécriture crée de nouveaux commits avec de nouvelles empreintes. Règle d'or :
- Réécrire est sûr tant que les commits sont à vous seul (pas encore poussés, ou sur votre branche de pull request que personne d'autre n'utilise). Pour pousser après réécriture :
git push --force-with-lease(plus prudent que--force: il refuse si la branche distante a avancé depuis votre dernière récupération, par exemple parce que quelqu'un d'autre y a poussé ; ungit fetchautomatique de votre éditeur peut toutefois mettre à jour votre vue et neutraliser cette protection). - Réécrire est interdit sur une branche partagée (
main) : vos collègues ont bâti dessus. - Pour défaire un commit déjà partagé, utilisez
git revert <empreinte>: il crée un nouveau commit qui annule l'ancien, sans rien réécrire.
Filet de sécurité : git reflog liste les endroits où HEAD est passé, même après une réécriture, ce qui permet généralement de retrouver un commit « perdu » pendant un certain temps. git reset --hard, en revanche, jette définitivement les modifications non validées.
Ce que cela ne garantit PAS
- Un historique propre n'est pas un code correct : il dit ce qui a changé, pas que cela marche.
- Git ne prouve pas l'identité de l'auteur : le nom et l'e-mail d'un commit sont des champs que chacun peut écrire.
git revertn'efface pas la trace d'un secret ou d'une erreur : l'ancien commit reste dans l'historique.
À retenir
Un commit = un instantané lié à son parent par une empreinte ; une branche = un pointeur. Réécrivez seulement ce qui est à vous seul, et utilisez revert pour ce qui est partagé.
Outil interactif
Empreinte SHA-256
Tapez un texte : l'empreinte se met à jour à chaque caractère. Comparez avec un second texte pour mesurer l'effet d'avalanche.
Empreinte (64 caractères hexadécimaux)
…Empreinte du second texte
…Les caractères différents sont soulignés et en gras. Le texte est encodé en UTF-8 sans ajout d'espace ni de retour à la ligne.
Outil interactif
Explorateur de chaîne de blocs (jouet)
Cinq blocs liés par leur empreinte. Modifiez les données d'un bloc : son empreinte change, le lien du suivant se rompt, et tous les blocs suivants deviennent invalides.
Construction de la chaîne (minage des 5 blocs)…
Ceci est un jouet. L'empreinte d'un bloc est SHA-256 de « index|empreinte précédente|données|nonce » ; la difficulté est de seulement deux zéros hexadécimaux au début (environ 256 essais). Une vraie chaîne exige un travail astronomiquement plus grand et un réseau de nœuds qui vérifient chacun la chaîne.
