On vient de le voir avec l'index, nos manipulations Git aboutissent à des fichiers aux noms abscons et difficilement mémorisables. 

Oui, j’ai dit « abscons », c’est pour me donner un air intelligent. Ça fait son effet, non ?

Bref, il existe des mécanismes pour référencer avec des noms plus simples ces fichiers aux noms étranges.

On trouve plusieurs mécanismes pour gérer ces références et qui sont complémentaires :

Comme on l'a vu l'index s'occupe de lister les références d'objets en préparation d'un (+) commit qui à son tour reprend ces références et pointe vers un ou plusieurs commit parents. Un historique est donc la représentation des relations entre les commits. Le « HEAD » désigne le commit courant, celui servant de base à notre travail local, ou en clair, celui duquel partirait le prochain commit. Cette désignation est souvent indirecte, c-à-d qu'elle désigne une branche qui elle-même pointe sur un commit. Grosso modo toute manipulation ou déplacement dans notre historique représente un déplacement de HEAD ou de l’étiquette de branche qu’il pointe, donc un changement de référence.

On avait parlé plus tôt des branches. Et il se trouve que je ne t’avais pas tout dit. Une branche est en réalité une simple étiquette nommée qui pointe vers le dernier commit en date de l'historique qu'elle désigne. On parle parfois aussi de tête de branche pour faciliter notre compréhension. Du coup la branche en elle-même est la lecture qu’on fait des références de commits successifs jusqu’à l’étiquette.

Un tag est comme une branche, à savoir une simple étiquette nommée, à la différence près qu'un tag a pour vocation de toujours désigner le même emplacement ou commit, contrairement à une branche qui évoluera. Il existe aussi des tags annotés, qui contiennent quelques méta-données supplémentaires.

Allez, je te montre tout ça avec un joli schéma animé.

Assemblage des références

Si on repart de nos objets, voici comment s'enchaînent les références dans la construction d'un historique Git.

On a donc les blobs qui représentent des fichiers. Viennent ensuite les trees, qui représentent nos répertoires et référencent d'autres trees et blobs. Chaque commit référencera un objet tree représentant la racine du projet. 

On s'attaque ensuite à la création de l'historique des commits. Celui-ci débute par la mise en place d'une première branche nommée par défaut « master ». Celle-ci est automatiquement créée avec notre premier commit. Il s'agit d'une simple étiquette qui référence le commit. On trouve également une autre référence essentielle à Git, « HEAD », qui a pour rôle de nous indiquer notre emplacement actuel dans l'historique. Dans l'immédiat elle pointe sur la branche « master ». On enchaîne ensuite les commits qui vont créer notre historique, chaque commit référençant son parent.

Si on vient créer une branche « dev », on voit qu'on crée en fait un autre pointeur vers l'emplacement référencé par HEAD. Note qu'on a créé l'étiquette mais pas fait pointer HEAD dessus : elle n'est donc pas la branche active. Allez, faisons ça : demandons à HEAD de référencer la branche « dev » de manière à pouvoir la faire évoluer.

Ajoutons ensuite des commits. On fait ensuite repointer HEAD sur « master » pour qu'elle puisse à son tour évoluer en recevant le travail effectué sur la branche « dev ». Lorsqu'on effectue la fusion on se retrouve avec un commit ayant deux références parentes. Nous pouvons créer un tag pour marquer la version de notre projet suite à cette fusion de travail. 

Ensuite si on créé un nouveau commit sur « master », on voit que le tag conserve bien sa référence intacte alors que « master » pointe vers la référence du nouveau commit.

Enfin, notre branche « dev » étant fusionnée, nous pouvons décider de la supprimer. Ça nécessite juste la suppression de la référence dans le dépôt, et ne vient en aucun cas supprimer les commits qu'elle contenait.