Git 🌙
Chapters â–Ÿ 2nd Edition

7.1 Utilitaires Git - SĂ©lection des versions

À prĂ©sent, vous avez appris les commandes et modes de fonctionnement usuels requis pour gĂ©rer et maintenir un dĂ©pĂŽt Git pour la gestion de votre code source. Vous avez dĂ©roulĂ© les routines de suivi et de validation de fichiers, vous avez exploitĂ© la puissance de l’index, de la crĂ©ation et de la fusion de branches locales de travail.

Maintenant, vous allez explorer un certain nombre de fonctionnalitĂ©s particuliĂšrement efficaces, fonctionnalitĂ©s que vous utiliserez moins souvent mais dont vous pourriez avoir l’usage Ă  un moment ou Ă  un autre.

SĂ©lection des versions

Git vous permet de faire référence à certains commits ou un ensemble de commits de différentes façons. Si elles ne sont pas toutes évidentes, il est bon de les connaßtre.

RĂ©visions ponctuelles

Naturellement, vous pouvez référencer un commit par sa signature SHA-1, mais il existe des méthodes plus confortables pour les humains. Cette section présente les méthodes pour référencer un commit simple.

Empreinte SHA courte

Git est capable de deviner de quel commit vous parlez si vous ne fournissez que quelques caractÚres du début de la signature, tant que votre SHA-1 partiel comporte au moins 4 caractÚres et ne correspond pas à plusieurs commits. Dans ces conditions, un seul objet correspondra à ce SHA-1 partiel.

Par exemple, pour afficher un commit prĂ©cis, supposons que vous exĂ©cutiez git log et que vous identifiiez le commit oĂč vous avez introduit une fonctionnalitĂ© prĂ©cise.

$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'

commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 14:58:32 2008 -0800

    added some blame and merge stuff

Pour cet exemple, choisissons 1c002dd
​. Si vous affichez le contenu de ce commit via git show, les commandes suivantes sont Ă©quivalentes (en partant du principe que les SHA-1 courts ne sont pas ambigus).

$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d

Git peut dĂ©terminer une rĂ©fĂ©rence SHA-1 tout Ă  la fois la plus courte possible et non ambigĂŒe. Ajoutez l’option --abbrev-commit Ă  la commande git log et le rĂ©sultat affichĂ© utilisera des valeurs plus courtes mais uniques ; par dĂ©faut Git retiendra 7 caractĂšres et augmentera au besoin :

$ git log --abbrev-commit --pretty=oneline
ca82a6d changed the version number
085bb3b removed unnecessary test code
a11bef0 first commit

En rĂšgle gĂ©nĂ©rale, entre 8 et 10 caractĂšres sont largement suffisants pour assurer l’unicitĂ© dans un projet. Par exemple, en fĂ©vrier 2019, le noyau Linux (qui est un projet plutĂŽt imposant) avait de plus de 875 000 commits et presque sept millions d’objets dont les empreintes SHA sont uniques Ă  partir des 12 premiers caractĂšres.

Note
Quelques mots sur SHA-1

Beaucoup de gens s’inquiĂštent qu’à un moment donnĂ© ils auront, par des circonstances hasardeuses, deux objets dans leur rĂ©fĂ©rentiel de hachage de mĂȘme empreinte SHA-1. Qu’en est-il rĂ©ellement ?

S’il vous arrivait de valider un objet qui se hache Ă  la mĂȘme empreinte SHA-1 qu’un objet existant dans votre rĂ©fĂ©rentiel, Git verrait l’objet existant dĂ©jĂ  dans votre base de donnĂ©es et prĂ©sumerait qu’il Ă©tait dĂ©jĂ  enregistrĂ©. Si vous essayez de rĂ©cupĂ©rer l’objet de nouveau Ă  un moment donnĂ©, vous auriez toujours les donnĂ©es du premier objet.

Quoi qu’il en soit, vous devriez ĂȘtre conscient Ă  quel point ce scĂ©nario est ridiculement improbable. Une empreinte SHA-1 porte sur 20 octets soit 160 bits. Le nombre d’objets alĂ©atoires Ă  hacher requis pour assurer une probabilitĂ© de collision de 50 % vaut environ 280 (la formule pour calculer la probabilitĂ© de collision est p = (n(n-1)/2) * (1/2^160)). 280 vaut 1,2 × 1024 soit 1 million de milliards de milliards. Cela reprĂ©sente 1 200 fois le nombre de grains de sable sur Terre.

Voici un exemple pour vous donner une idĂ©e de ce qui pourrait provoquer une collision du SHA-1. Si tous les 6,5 milliards d’humains sur Terre programmaient et que chaque seconde, chacun produisait du code Ă©quivalent Ă  l’historique entier du noyau Linux (6,5 millions d’objets Git) et le poussait sur un Ă©norme dĂ©pĂŽt Git, cela prendrait 2 ans pour que ce dĂ©pĂŽt contienne assez d’objets pour avoir une probabilitĂ© de 50 % qu’une seule collision SHA-1 existe. Ainsi, une collision organique de SHA-1 est moins probable que tous les membres de votre Ă©quipe de programmeurs soient attaquĂ©s et tuĂ©s par des loups dans des incidents sans relation la mĂȘme nuit.

Si vous y dĂ©diiez plusieurs milliers de dollars de puissance de calcul, il searit possible de synthĂ©tiser deux fichiers avec la mĂȘme empreinte, comme prouvĂ© par https://shattered.io/ en fĂ©vrier 2017. Git Ă©volue vers l’utilisation de SHA256 comme algorithme par dĂ©faut d’empreinte, qui est beaucoup plus rĂ©silient aux attaques par collision, et a dĂ©jĂ  du code en place pour amenuiser cette attaque (bien qu’il ne puisse pas totalement Ă©liminer cette faiblesse)

Références de branches

La mĂ©thode la plus commune pour dĂ©signer un commit est une branche y pointant. DĂšs lors, vous pouvez utiliser le nom de la branche dans toute commande utilisant un objet de type commit ou un SHA-1. Par exemple, si vous souhaitez afficher le dernier commit d’une branche, les commandes suivantes sont Ă©quivalentes, en supposant que la branche sujet1 pointe sur ca82a6d :

$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show sujet1

Pour connaĂźtre l’empreinte SHA sur laquelle pointe une branche ou pour savoir parmi tous les exemples prĂ©cĂ©dents ce que cela donne en terme de SHA, vous pouvez utiliser la commande de plomberie nommĂ©e rev-parse. RĂ©fĂ©rez-vous Ă  Les tripes de Git pour plus d’informations sur les commandes de plomberie ; rev-parse sert aux opĂ©rations de bas niveau et n’est pas conçue pour ĂȘtre utilisĂ©e quotidiennement. Quoi qu’il en soit, elle se rĂ©vĂšle utile pour comprendre ce qui se passe. Je vous invite Ă  tester rev-parse sur votre propre branche.

$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949

Raccourcis RefLog

Git maintient en arriĂšre-plan un historique des rĂ©fĂ©rences oĂč sont passĂ©s HEAD et vos branches sur les derniers mois — ceci s’appelle le reflog.

Vous pouvez le consulter avec la commande git reflog :

$ git reflog
734713b... HEAD@{0}: commit: fixed refs handling, added gc auto, updated
d921970... HEAD@{1}: merge phedders/rdocs: Merge made by recursive.
1c002dd... HEAD@{2}: commit: added some blame and merge stuff
1c36188... HEAD@{3}: rebase -i (squash): updating HEAD
95df984... HEAD@{4}: commit: # This is a combination of two commits.
1c36188... HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5... HEAD@{6}: rebase -i (pick): updating HEAD

À chaque fois que l’extrĂ©mitĂ© de votre branche est modifiĂ©e, Git enregistre cette information pour vous dans son historique temporaire. Vous pouvez rĂ©fĂ©rencer d’anciens commits avec cette donnĂ©e. Si vous souhaitez consulter le n-iĂšme antĂ©cĂ©dent de votre HEAD, vous pouvez utiliser la rĂ©fĂ©rence @{n} du reflog, 5 dans cet exemple :

$ git show HEAD@{5}

Vous pouvez Ă©galement remonter le temps et savoir oĂč en Ă©tait une branche Ă  une date donnĂ©e. Par exemple, pour savoir oĂč en Ă©tait la branche master hier (yesterday en anglais), tapez :

$ git show master@{yesterday}

Cette technique fonctionne uniquement si l’information est encore prĂ©sente dans le reflog et vous ne pourrez donc pas le consulter sur des commits plus vieux que quelques mois.

Pour consulter le reflog au format git log, exécutez: git log -g :

$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'

Veuillez noter que le reflog ne stocke que des informations locales, c’est un historique de ce que vous avez fait dans votre dĂ©pĂŽt. Les rĂ©fĂ©rences sont diffĂ©rentes pour un autre dĂ©pĂŽt et juste aprĂšs le clone d’un dĂ©pĂŽt, votre reflog sera vide puisque qu’aucune activitĂ© n’aura Ă©tĂ© produite. ExĂ©cuter git show HEAD@{2.months.ago} ne fonctionnera que si vous avez dupliquĂ© ce projet depuis au moins 2 mois — si vous l’avez dupliquĂ© il y a 5 minutes, vous n’obtiendrez aucun rĂ©sultat.

Astuce
Pensez le reflog comme la version Git d’un historique shell

Si vous avez une culture UNIX ou Linux, vous pouvez penser le reflog comme la version Git d’un historique shell, ce qui implique que ce qui y est n’est clairement pertinent que pour vous et votre « session », et n’a rien Ă  voir avec n’importe qui d’autre qui pourrait travailler sur la mĂȘme machine.

Note
Échapper les accolades dans PowerShell

Avec PowerShell, les accolades telles que { et } sont des caractĂšres spĂ©ciaux et doivent ĂȘtre Ă©chappĂ©s. Vous pouvez les Ă©chapper avec des apostrophes inversĂ©es ` ou placer la rĂ©fĂ©rence du commit entre guillemets :

$ git show HEAD@{0}     # will NOT work
$ git show HEAD@`{0`}   # OK
$ git show "HEAD@{0}"   # OK

RĂ©fĂ©rences ancĂȘtres

Une solution frĂ©quente pour rĂ©fĂ©rencer un commit est d’utiliser son ascendance. Si vous suffixez une rĂ©fĂ©rence par ^, Git la rĂ©soudra comme Ă©tant le parent de cette rĂ©fĂ©rence. Supposons que vous consultiez votre historique :

$ git log --pretty=format:'%h %s' --graph
* 734713b fixed refs handling, added gc auto, updated tests
*   d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd added some blame and merge stuff
|/
* 1c36188 ignore *.gem
* 9b29157 add open3_detach to gemspec file list

Alors, vous pouvez consulter le commit précédent en spécifiant HEAD^, ce qui signifie « le parent de HEAD » :

$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 15:08:43 2008 -0800

    Merge commit 'phedders/rdocs'
Note
Échapper l''accent circonflexe sous Windows

Sous Windows dans cmd.exe, ^ est un caractĂšre spĂ©cial qui doit ĂȘtre traitĂ© diffĂ©remment. Vous pouvez soit le doubler soit placer la rĂ©fĂ©rence du commit entre guillemets :

$ git show HEAD^     # will NOT work on Windows
$ git show HEAD^^    # OK
$ git show "HEAD^"   # OK

Vous pouvez Ă©galement spĂ©cifier un nombre aprĂšs ^ — par exemple, d921970^2 signifie « le second parent de d921970 ». Cette syntaxe ne sert que pour les commits de fusion, qui ont plus d’un parent. Le premier parent est la branche depuis laquelle vous avez fusionnĂ©, et le second est le commit de la branche que vous avez fusionnĂ©e :

$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Dec 11 14:58:32 2008 -0800

    added some blame and merge stuff

$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date:   Wed Dec 10 22:22:03 2008 +0000

    Some rdoc changes

Une autre solution courante pour spĂ©cifier une rĂ©fĂ©rence ancĂȘtre est le ~. Il fait Ă©galement rĂ©fĂ©rence au premier parent, donc HEAD~ et HEAD^ sont Ă©quivalents. La diffĂ©rence apparaĂźt si vous spĂ©cifiez un nombre. HEAD~2 signifie « le premier parent du premier parent », ou bien « le grand-parent » ; on remonte les premiers parents autant de fois que demandĂ©. Par exemple, dans l’historique prĂ©cĂ©demment prĂ©sentĂ©, HEAD~3 serait :

$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date:   Fri Nov 7 13:47:59 2008 -0500

    ignore *.gem

Cela peut aussi s’écrire HEAD^, qui lĂ  encore est le premier parent du premier parent du premier parent :

$ git show HEAD^^^
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner <tom@mojombo.com>
Date:   Fri Nov 7 13:47:59 2008 -0500

    ignore *.gem

Vous pouvez Ă©galement combiner ces syntaxes — vous pouvez obtenir le second parent de la rĂ©fĂ©rence prĂ©cĂ©dente (en supposant que c’était un commit de fusion) en utilisant HEAD~3^2, et ainsi de suite.

Plages de commits

À prĂ©sent que vous pouvez spĂ©cifier des commits individuels, voyons comment spĂ©cifier des plages de commits. Ceci est particuliĂšrement pratique pour la gestion des branches — si vous avez beaucoup de branches, vous pouvez utiliser les plages pour rĂ©pondre Ă  des questions telles que « Quel travail sur cette branche n’ai-je pas encore fusionnĂ© sur ma branche principale ? ».

Double point

La spĂ©cification de plage de commits la plus frĂ©quente est la syntaxe double-point. En gros, cela demande Ă  Git de rĂ©soudre la plage des commits qui sont accessibles depuis un commit mais ne le sont pas depuis un autre. Par exemple, disons que votre historique ressemble Ă  Exemple d’historique pour la sĂ©lection de plage..

Exemple d’historique pour la sĂ©lection de plage.
Figure 136. Exemple d’historique pour la sĂ©lection de plage.

Si vous voulez savoir ce qui n’a pas encore Ă©tĂ© fusionnĂ© sur votre branche master depuis votre branche experiment, vous pouvez demander Ă  Git de vous montrer un journal des commits avec master..experiment — ce qui signifie « tous les commits accessibles par experiment qui ne le sont pas par master ». Dans un souci de briĂšvetĂ© et de clartĂ© de ces exemples, je vais utiliser les lettres des commits issus du diagramme Ă  la place de la vraie liste dans l’ordre oĂč ils auraient dĂ» ĂȘtre affichĂ©s :

$ git log master..experiment
D
C

Si, par contre, vous souhaitez voir l’opposĂ© — tous les commits dans master mais pas encore dans experiment — vous pouvez inverser les noms de branches, experiment..master vous montre tout ce que master accĂšde mais qu'`experiment` ne voit pas :

$ git log experiment..master
F
E

C’est pratique si vous souhaitez maintenir experiment Ă  jour et anticiper les fusions. Un autre cas d’utilisation frĂ©quent consiste Ă  voir ce que vous vous apprĂȘtez Ă  pousser sur une branche distante :

$ git log origin/master..HEAD

Cette commande vous affiche tous les commits de votre branche courante qui ne sont pas sur la branche master du dĂ©pĂŽt distant origin. Si vous exĂ©cutez git push et que votre branche courante suit origin/master, les commits listĂ©s par git log origin/master..HEAD sont les commits qui seront transfĂ©rĂ©s sur le serveur. Vous pouvez Ă©galement laisser tomber une borne de la syntaxe pour faire comprendre Ă  Git que vous parlez de HEAD. Par exemple, vous pouvez obtenir les mĂȘmes rĂ©sultats que prĂ©cĂ©demment en tapant git log origin/master.. — Git utilise HEAD si une des bornes est manquante.

Emplacements multiples

La syntaxe double-point est pratique comme raccourci ; mais peut-ĂȘtre souhaitez-vous utiliser plus d’une branche pour spĂ©cifier une rĂ©vision, comme pour voir quels commits sont dans plusieurs branches mais sont absents de la branche courante. Git vous permet cela avec ^ ou --not en prĂ©fixe de toute rĂ©fĂ©rence de laquelle vous ne souhaitez pas voir les commits. Les 3 commandes ci-aprĂšs sont Ă©quivalentes :

$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA

C’est utile car cela vous permet de spĂ©cifier plus de 2 rĂ©fĂ©rences dans votre requĂȘte, ce que vous ne pouvez accomplir avec la syntaxe double-point. Par exemple, si vous souhaitez voir les commits qui sont accessibles depuis refA et refB mais pas depuis refC, vous pouvez taper ces 2 commandes :

$ git log refA refB ^refC
$ git log refA refB --not refC

Ceci vous fournit un systĂšme de requĂȘtage des rĂ©visions trĂšs puissant, pour vous aider Ă  saisir ce qui se trouve sur vos branches.

Triple point

La derniĂšre syntaxe majeure de sĂ©lection de plage de commits est la syntaxe triple-point qui spĂ©cifie tous les commits accessibles par l’une des deux rĂ©fĂ©rences, exclusivement. Toujours avec l’exemple d’historique de Exemple d’historique pour la sĂ©lection de plage., si vous voulez voir ce qui se trouve sur master ou experiment mais pas sur les deux, exĂ©cutez :

$ git log master...experiment
F
E
D
C

Encore une fois, cela vous donne un log normal mais ne vous montre les informations que pour ces quatre commits, dans l’ordre naturel des dates de validation.

Une option courante à utiliser avec la commande log dans ce cas est --left-right qui vous montre la borne de la plage à laquelle ce commit appartient. Cela rend les données plus utiles :

$ git log --left-right master...experiment
< F
< E
> D
> C

Avec ces outils, vous pourrez spécifier à Git les commits que vous souhaitez inspecter.

scroll-to-top