GitVoici le dix-huitième volet de la formation Git. Dans mon précédent article, nous avons vu en détail le renommage des branches. Aujourd’hui nous allons nous intéresser de plus près au rôle de ce mystérieux HEAD que vous avez pu voir apparaître dans les logs.

Comprendre HEAD par la pratique

Lorsqu’on souhaite afficher la différence entre deux commits, on peut très bien utiliser la procédure suivante :

  • Afficher les logs avec git log et les options qui vont bien.
  • Repérer les identifiants respectifs des commits que l’on souhaite comparer.
  • Invoquer git diff en effectuant deux opérations successives de copier/coller sur ces identifiants.

Une autre manière de faire – et qui est bien plus commode pour les commits relativement récents – utilise la référence HEAD.

Prenons un dépôt Git simple avec un historique linéaire sans la moindre branche :

$ cd ~/formation-git/atelier-23/
$ git log --oneline
b71f6b2 (HEAD -> master) Renommage du fichier.
cbeca14 Ajout du fichier Cartes.
7def037 Ajout d'une destination.
9159976 Ajout du fichier Entretien.
4220c4c Ajout du fichier Roadtrip.

Vous êtes iciVous connaissez tous le fameux petit symbole VOUS ÊTES ICI sur les plans affichés dans le centre historique des villes et des grands centres commerciaux ? Ou alors l’icône qui symbolise votre emplacement actuel dans les applications de géolocalisation de votre smartphone ? Le rôle de HEAD dans Git, c’est exactement cela : vous montrer votre emplacement actuel dans l’historique.

Admettons que dans mon dépôt Git, je souhaite afficher la différence entre l’avant-dernier et le dernier commit. Dans ce cas, j’effectue un copier/coller des identifiants respectifs, et j’invoque git diff comme ceci :

$ git diff cbeca14 b71f6b2
diff --git a/Entretien.md b/Maintenance.md
similarity index 100%
rename from Entretien.md
rename to Maintenance.md

AstuceNotez bien que j’écris d’abord l’identifiant du commit parent.

Alternativement, je pourrais utiliser la syntaxe suivante :

$ git diff HEAD~1 HEAD
diff --git a/Entretien.md b/Maintenance.md
similarity index 100%
rename from Entretien.md
rename to Maintenance.md
  • HEAD est la référence qui désigne le commit actuel.
  • HEAD~1 représente le premier parent du commit actuel.

Admettons maintenant que je veuille afficher la différence entre l’avant-dernier commit et son parent. En utilisant les identifiants respectifs, voilà ce que j’obtiendrais :

$ git diff 7def037 cbeca14
diff --git a/Cartes.md b/Cartes.md
new file mode 100644
index 0000000..a6e35b6
--- /dev/null
+++ b/Cartes.md
@@ -0,0 +1,9 @@
+# Cartes routières
+
+- [ ] France
+
+- [ ] Italie
+
+- [ ] Suisse
+
+- [ ] Autriche

Et en utilisant HEAD :

$ git diff HEAD~2 HEAD~1
diff --git a/Cartes.md b/Cartes.md
new file mode 100644
index 0000000..a6e35b6
--- /dev/null
+++ b/Cartes.md
@@ -0,0 +1,9 @@
+# Cartes routières
+
+- [ ] France
+
+- [ ] Italie
+
+- [ ] Suisse
+
+- [ ] Autriche

Vous l’aurez compris : lorsqu’on écrit HEAD~n, le nombre n après le symbole tilde ~ symbolise le n-ième ancêtre du commit actuel.

Mais ce n’est pas tout. Rappelez-vous que lorsque nous fusionnons deux branches avec un merge commit, ce dernier se distingue par le fait qu’il a deux commits parents. Comment faire alors ?

Pour ne pas nous perdre dans les méandres soporifiques de la théorie, cherchons tout de suite un exemple pratique :

$ cd ~/formation-git/atelier-16
$ git log --oneline --graph --all
*   403fb57 (HEAD -> master) Merge branch 'hello-figlet'.
|\  
| * d0c718c (hello-figlet) Message en lettres ASCII.
* | 36bfb46 (hello-cow) Message avec une vache qui parle.
|/  
* 797fe52 Commit initial.
  • Concrètement, le commit actuel 403fb57 est le produit de la fusion des deux commits parents 36bfb46 et d0c718c.
  • Avec la référence HEAD, nous pourrons désigner le premier parent (36bfb46) par HEAD^1 et le deuxième parent (d0c718c) par HEAD^2.

Si j’essaie de mettre en pratique cette manière de faire, je devrais obtenir le même résultat pour les deux commandes suivantes :

$ git diff d0c718c 403fb57
...
$ git diff HEAD^2 HEAD
...

Est-ce que je pourrais aller plus loin ? Oui, en combinant les deux écritures :

  • HEAD^1~1 est le premier ancêtre du premier parent.
  • HEAD^1~2 est le deuxième ancêtre du premier parent.
  • HEAD^2~1 est le premier ancêtre du deuxième parent.
  • HEAD^2~2 est le deuxième ancêtre du deuxième parent.
  • Etc.

Là aussi, je vais tester ce principe dans la pratique en vérifiant si j’obtiens un résultat identique pour les commandes suivantes :

$ git diff 797fe52 36bfb46
...
$ git diff HEAD^1~1 HEAD^1
...

Ou encore :

$ git diff 797fe52 d0c718c
...
$ git diff HEAD^2~1 HEAD^2
...

AstuceVous l’aurez remarqué : dans notre exemple, le commit 797fe52 peut être référence par HEAD^1~1 aussi bien que par HEAD^2~2. Comme quoi différents chemins mènent à Saint-Bauzille-de-Putois.

Exercice

Rendez-vous dans le répertoire ~/formation-git/atelier-15/hello et affichez l’historique complet du dépôt :

$ cd ~/formation-git/atelier-14/hello/
$ git log --oneline --graph --all
*   b2139e1 (HEAD -> master) Merge branch 'hello-figlet'.
|\  
| * 8db1b22 Premier jet avec des majuscules ASCII.
* |   5a7dd1d Merge branch 'hello-cow'.
|\ \  
| * | 477d1a0 Implémentation d'une vache fatiguée.
| * | d268724 Premier jet de la vache qui dit bonjour.
| |/  
* / 89211a3 Peaufinage du script initial.
|/  
* bebe769 Commit initial.

La commande git rev-parse --short permet d’afficher l’identifiant correspondant à la référence avec HEAD :

$ git rev-parse --short HEAD
b2139e1
$ git rev-parse --short HEAD^1
5a7dd1d
...
  • Essayez de référencer tous les commits du dépôt en utilisant l’écriture avec HEAD.
  • Trouvez les trois écritures possibles pour le commit initial bebe769.
  • Prenez un cachet d’aspirine. :o)

Lire la suite : Retour vers le commit


La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.

 

Catégories : Formation

0 commentaire

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *