Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Le modèle Git : snapshots, SHA et 3 états

Par mis à jour le

Améliorer cette page

Ce guide vous explique comment Git pense. Avant d'enchaîner les commandes, comprendre son modèle interne rend la plupart de ses messages lisibles et ses erreurs prévisibles. Vous verrez comment Git stocke vos données (des snapshots), comment il identifie chaque version (le SHA), et par quelles trois zones passent vos fichiers avant d'entrer dans l'historique.

Prérequis : aucun pour lire. Pour rejouer les commandes, il vous faut Git installé et un terminal. Ce guide est le point de départ recommandé pour tout débutant.

  • Le modèle snapshot : pourquoi Git enregistre l'état complet du projet à chaque commit, et non une liste de différences
  • Les identifiants SHA : comment Git donne un nom unique, calculé à partir du contenu, à chaque version
  • Les 3 états : le circuit que suivent vos fichiers avant d'être enregistrés dans l'historique
  • L'anatomie d'un commit : ce qu'il contient réellement, lu avec git cat-file

Git enregistre à chaque commit l'état complet de votre projet, là où les anciens systèmes de version centralisés, comme CVS ou Subversion (SVN), raisonnent en deltas : la liste des différences d'une version d'un fichier à la suivante.

À chaque commit, Git prend donc une photo complète (un snapshot) de tous les fichiers suivis. Une photo complète ne veut pas dire une copie complète : un fichier inchangé n'est pas dupliqué, Git réutilise la version déjà enregistrée et se contente d'y faire référence.

Stockage par deltas (SVN, CVS) : chaque version ne garde que les différences des fichiers A, B, C. Stockage par snapshots (Git) : chaque commit photographie tous les fichiers et un fichier inchangé n'est qu'un lien vers sa version précédente.Stockage par deltas (SVN, CVS) : seules les différences sont stockéesStockage par snapshots (Git) : → est un lien vers le fichier inchangéVersion 1Version 2Version 3Commit 1Commit 2Commit 3ABCΔA–ΔC–ΔB–A₁B₁C₁A₂→ B₁C₂→ A₂B₂→ C₂
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Le modèle et le stockage sur disque sont deux choses distinctes. Pour gagner de la place, Git range régulièrement ses objets dans des packfiles, où il compresse les versions proches les unes par rapport aux autres, avec des deltas. C'est une optimisation invisible à l'usage : vous manipulez toujours des snapshots.

Le modèle par snapshots rend la plupart des opérations locales et directes. Chaque commit désigne une arborescence complète : pour afficher une version, Git la lit telle quelle, sans rejouer une chaîne de différences. Le tableau compare les deux familles sur ce que vous ferez au quotidien.

Ce que vous faitesDeltas centralisés (SVN, CVS)Snapshots distribués (Git)
Ouvrir une version passéeRequête au serveur, qui reconstruit la versionLecture locale du snapshot
Consulter l'historiqueRequête au serveurLecture locale de .git/
Committer sans réseauImpossible : le commit part au serveurPossible, le commit est local
Vérifier l'intégritéSommes de contrôle par fichierChaque objet est nommé par son empreinte

Pour comparer deux versions, git diff calcule bien des différences, mais seulement à la demande et entre deux snapshots. Il compare les deux arborescences et saute d'un coup tout sous-dossier dont l'empreinte est identique des deux côtés : seuls les fichiers réellement modifiés sont lus. C'est ce qui garde git diff rapide sur un dépôt de milliers de commits.

Les identifiants SHA : l'empreinte digitale de chaque version

Section intitulée « Les identifiants SHA : l'empreinte digitale de chaque version »

Chaque objet de Git porte un nom calculé à partir de son contenu : son SHA. Commit, fichier ou dossier, tout objet est identifié par une empreinte (un hash) de 40 caractères hexadécimaux dans le format par défaut, SHA-1, et de 64 caractères dans le format SHA-256, présenté plus bas. Voici l'empreinte complète d'un commit, en SHA-1 :

1e28c5e26a9e43fcd96ed0d8827ef57060d045c2

Ce nom n'est pas tiré au hasard : Git le calcule à partir du contenu. La commande git hash-object le montre sur un simple texte : il suffit d'ajouter un seul caractère pour obtenir une empreinte entièrement différente.

Fenêtre de terminal
echo "Bonjour" | git hash-object --stdin
echo "Bonjour." | git hash-object --stdin
632e4fe73c3da8c9018008dbda117fc6b00e3e83
e03e829c1d374b5d17b6d194e4cee13b49a88788

Le SHA n'est pas seulement une étiquette. Il apporte deux garanties qui expliquent la robustesse de Git :

  • Intégrité : si un objet est corrompu (disque défaillant, transfert réseau interrompu), son contenu ne correspond plus à son empreinte, et git fsck le signale. Comme chaque commit contient l'empreinte de son parent, modifier un ancien commit change l'empreinte de tous ceux qui suivent.
  • Unicité en pratique : deux contenus différents ne produisent pas la même empreinte dans un dépôt réel. Depuis l'attaque SHAttered de 2017, Git utilise une version durcie de SHA-1, qui détecte les collisions fabriquées.

SHA-1 est le format par défaut, pas une propriété du modèle. Le projet Git a déjà prévu d'en changer, et tout ce que vous apprenez ici reste vrai avec un autre algorithme : seule la longueur des empreintes change. Votre dépôt vous dit lequel il utilise :

Fenêtre de terminal
git rev-parse --show-object-format
sha1

SHA-256 existe déjà : git init --object-format=sha256 crée un tel dépôt depuis Git 2.29. Ses empreintes font 64 caractères au lieu de 40. Les forges courantes ne savent pas toutes héberger ce format : réservez-le à l'expérimentation pour l'instant.

FormatLongueur de l'empreinteTaille de .git/refs/heads/<branche>
SHA-1 (défaut aujourd'hui)40 caractères41 octets
SHA-25664 caractères65 octets

Une branche est un petit fichier qui contient l'empreinte d'un commit, plus un saut de ligne : d'où les 41 octets en SHA-1. Ce chiffre est un détail d'implémentation, pas une propriété des branches. La propriété, elle, ne bouge pas : une branche est une référence vers un commit, et c'est pour cela qu'elle ne coûte presque rien.

En pratique, vous verrez surtout des SHA abrégés, comme dans la sortie de git log --oneline :

b1d8e9c Ajout de la page d'accueil
1e28c5e Création du fichier README

Le commit 1e28c5e est celui dont l'empreinte complète figure plus haut : Git accepte une empreinte abrégée tant qu'elle reste non ambiguë dans le dépôt. Il affiche 7 caractères par défaut et en ajoute automatiquement quand le dépôt grossit.

Avant d'entrer dans l'historique, un changement passe par trois zones : le répertoire de travail, la zone de staging et le dépôt. C'est le concept le plus important de ce guide, parce que chaque commande de base fait passer un changement d'une zone à l'autre.

Les trois états de Git : git add fait passer les fichiers modifiés du répertoire de travail à la zone de staging, git commit les enregistre dans le dépôt .git ; git restore --staged et git restore font le chemin inverse.git addgit commitgit restore --stagedgit restore fichierRépertoire de travailfichiers modifiésZone de stagingprochaine photoDépôt .githistorique complet
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Le Working Directory est le dossier de votre projet tel que vous le voyez sur votre disque. Quand vous ouvrez un fichier dans votre éditeur et que vous le modifiez, vous travaillez dans cette zone : Git voit la modification, mais ne l'a encore enregistrée nulle part.

La Staging Area, aussi appelée Index, est la zone où vous préparez votre prochain commit. git add y copie l'état actuel d'un fichier : le fichier reste dans votre répertoire de travail, et l'index garde la version que vous avez ajoutée.

Ce point explique un piège classique : si vous modifiez de nouveau le fichier après git add, le prochain commit contiendra la version ajoutée, pas la dernière. git status --short l'affiche avec deux lettres, MM : une modification indexée, puis une autre non indexée.

MM index.html
M style.css

L'intérêt de cette zone est de choisir ce qui entre dans un commit. Vous avez modifié dix fichiers, mais trois seulement concernent la même fonctionnalité : vous n'indexez que ces trois-là, et le commit qui suit ne portera qu'eux.

Le Repository est la base de données de Git, stockée dans le dossier .git/ à la racine du projet. Git y conserve tous les commits, les branches, les tags et la configuration locale du dépôt.

git commit y enregistre un nouveau snapshot, construit à partir de l'index. Ce commit est identifié par son SHA et ne se modifie plus : « corriger » un commit en crée en réalité un nouveau, avec une autre empreinte.

Les trois zones se traversent toujours dans le même ordre, et chaque étape a sa commande :

  1. Vous modifiez un fichier dans votre éditeur. Il est modifié dans le Working Directory.

  2. Vous exécutez git add pour copier son état dans la Staging Area. Il est indexé (staged).

  3. Vous exécutez git commit pour enregistrer le snapshot dans le Repository. Il est committé.

Pour savoir où en sont vos fichiers, git status décrit chaque zone. Ici, index.html a été modifié puis ajouté, et style.css modifié sans être ajouté :

Fenêtre de terminal
git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: style.css

La rubrique Changes to be committed liste la Staging Area : c'est le contenu du prochain commit. La rubrique Changes not staged for commit liste ce qui n'existe encore que dans le répertoire de travail. Git y rappelle même la commande de chaque passage.

Un commit est un petit objet texte que vous pouvez lire. La commande git cat-file -p affiche son contenu brut : un lien vers son arborescence, un lien vers son parent, son auteur, son committer et son message.

Fenêtre de terminal
git cat-file -p HEAD
tree 359fa95ef2a6931d2543bf7840a980e7b3ca22f9
parent 1e28c5e26a9e43fcd96ed0d8827ef57060d045c2
author Alice Martin <alice@exemple.fr> 1791966600 +0200
committer Alice Martin <alice@exemple.fr> 1791966600 +0200
Ajout de la page d'accueil

Chaque ligne a un rôle précis. L'empreinte du commit n'y figure pas : elle est calculée à partir de ce texte, ce qui la rend impossible à modifier sans changer le commit lui-même.

ChampContenuDans l'exemple
treeEmpreinte de l'arborescence complète, le snapshot359fa95…
parentEmpreinte du ou des commits précédents1e28c5e…
authorQui a écrit les modifications, et quandAlice Martin, puis la date en secondes
committerQui a créé le commit, et quandsouvent la même personne
messageLa description du changementAjout de la page d'accueil

Le champ tree mène au snapshot. git cat-file -p 'HEAD^{tree}' le montre : chaque fichier y est un blob désigné par son empreinte, et un fichier inchangé d'un commit à l'autre garde la même empreinte, donc le même objet.

100644 blob 7b538c86f2b6dc9965561dc49624197e69b8e944 README.md
100644 blob bf5de4d05d7ad70e40b09c8fd9314e68d49461da index.html
100644 blob bc406f05614660340d90008ead85d26f50ea87db style.css

Le champ parent crée la chaîne de l'historique : chaque commit désigne son prédécesseur, ce qui forme un graphe orienté. Le premier commit du projet n'a pas de parent ; un commit de merge en a deux.

Avec Git, presque toutes les opérations se font sur votre machine. Votre copie contient l'historique complet du projet, là où Subversion interroge le serveur pour afficher l'historique ou créer une branche. Les conséquences au quotidien :

  • Consulter l'historique (git log) : lecture locale, aucune requête réseau
  • Comparer deux versions (git diff) : tout se calcule sur votre disque
  • Créer une branche (git branch) : Git écrit une référence vers un commit, rien de plus
  • Committer dans le train sans Wi-Fi : aucun problème, vous synchroniserez au retour du réseau

Seules les opérations qui parlent à un serveur distant demandent le réseau : push, pull, fetch et clone.

Un contenu committé est très difficile à perdre, un contenu jamais committé ne l'est pas. Git garde la trace des positions successives de HEAD dans le reflog : même après la suppression d'une branche, ses commits restent récupérables tant qu'ils y figurent, soit 30 jours par défaut pour un commit qui n'est plus rattaché à aucune branche.

La leçon pratique pour un débutant : committez souvent. Un commit, même imparfait, se retrouve et se corrige ; un travail resté dans le répertoire de travail n'a aucune copie dans Git.

Les questions qui reviennent le plus chez les débutants tiennent presque toutes à la zone où se trouve un changement. Le tableau y répond en partant du symptôme.

QuestionRéponse
« Quelle différence entre git add et git commit ? »git add copie l'état d'un fichier dans la Staging Area. git commit enregistre le contenu de la Staging Area dans le Repository.
« Pourquoi ma modification n'apparaît pas dans git log ? »Elle n'a pas été committée. git status montre dans quelle zone elle se trouve.
« Mon commit ne contient pas ma dernière modification »Vous avez modifié le fichier après git add. Relancez git add, puis committez (MM dans git status --short).
« C'est quoi ce numéro après chaque commit ? »Le SHA, l'empreinte unique du commit, calculée à partir de son contenu.
« Je peux annuler un git add ? »Oui : git restore --staged <fichier> retire le fichier de la Staging Area sans toucher à vos modifications.
« Si je supprime un fichier, Git le perd ? »Non, s'il a été committé au moins une fois : il reste dans l'historique et se récupère.
  • Git enregistre des snapshots : chaque commit désigne l'état complet du projet, et un fichier inchangé n'est pas dupliqué.
  • Le SHA est l'empreinte de chaque objet, calculée à partir de son contenu : changer un octet change l'empreinte.
  • Les 3 états : Working Directory (vos fichiers) → Staging Area (le prochain commit) → Repository (l'historique).
  • git add copie l'état actuel d'un fichier dans la Staging Area ; git commit enregistre la Staging Area dans le Repository.
  • Un commit se lit avec git cat-file -p : tree, parent, author, committer, message.
  • Presque tout est local : seuls push, pull, fetch et clone demandent le réseau.
  • Committez souvent : le committé se récupère, le non committé peut se perdre.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn