Nouveautés de Git 2.1

Tim Petterson
Tim Petterson
Retour à la liste

Après la livraison de Git 2.0.0 il y a deux mois et demi, nous avons eu le plaisir de voir arriver une nouvelle version mineure de Git, 2.1.0, avec une foule de nouvelles fonctionnalités intéressantes !

The full release notes are available here, but they can be a bit terse if you're not deeply involved in the git community. This blog is my own commentary on some aspects of the release that got us excited at Atlassian.

Amélioration du pager par défaut

Les citations de cet article sont directement extraites des notes de livraison, avec mes propres commentaires en dessous.

Depuis les tout débuts de Git, nous avons attribué une valeur par défaut « FRSX » à l'environnement LESS, en utilisant « less » pour le pager. « S » (coupe les lignes longues au lieu de renvoyer à la ligne suivante) a été supprimé de cet ensemble d'options par défaut, car il s'agit plutôt d'une préférence personnelle, contrairement à d'autres qui se justifient amplement (p. ex., « R » se justifie, comme de nombreuses sorties que nous produisons sont colorées, tout comme « FX », car la sortie que nous produisons fait moins d'une page). Si vous n'avez pas encore modifié votre pager Git par défaut, sachez qu'en opérant ce changement, la sortie paginée issue des commandes Git sera renvoyée à la ligne plutôt que d'être tronquée en fonction de la largeur de votre terminal. Voici un exemple avec Git 2.1.0 (renvoi à la ligne), à gauche, et Git 2.0.3 (sortie tronquée), à droite :

Styles des pagers dans Git 2.1.0 et 2.0.3

Ce changement affectera uniquement la sortie de log si vous utilisez un terminal restreint ou si vos messages de commit se composent de longues lignes ininterrompues. Dans Git, le bon sens veut que les messages de commit ne dépassent pas 72 caractères, mais si le renvoi à la ligne vous embête, vous pouvez toujours le désactiver en restaurant le comportement d'origine. Pour ce faire, utilisez :

$ git config core.pager "less -S"

Bien sûr, le pager sert également à afficher d'autres sorties, notamment git blame, qui peut contenir des lignes très longues suivant la taille du nom de l'auteur et le style de programmation. Les notes de la livraison 2.1.0 indiquent également que vous pouvez activer le flag -S uniquement pour le pager blame en utilisant :

$ git config pager.blame "less -S"

Si vous souhaitez en savoir plus sur les options less que Git utilise toujours par défaut :

  • -F force la fin de less si la sortie fait moins d'une page,
  • -R ensures only ANSI color escape sequences are output in raw form (so your git console colors work), and
  • -X prevents the screen from being cleared when less launches (again useful for logs of less than a page in length).

Meilleure complétion automatique de bash

Le script de complétion pour bash (dans contrib/) a été mis à jour pour une meilleure gestion des alias qui définissent une séquence de commandes complexe. C'est super cool ! Je suis un grand fan des alias Git personnalisés. Le fait de pouvoir intégrer la complétion bash de Git à mes alias complexes rend ceux-ci bien plus efficaces et faciles à utiliser à partir de la ligne de commande. Par exemple, j'ai défini un alias qui récupère les clés de ticket à la manière de Jira (p. ex., STASH-123) dans le log :

issues = !sh -c 'git log --oneline $@ | egrep -o [A-Z]+-[0-9]+ | sort | uniq' -

Tous les arguments sont ajoutés à la commande git log, donc vous pouvez limiter la plage de commits pour lesquels récupérer les clés de ticket. Par exemple, git issues -n 1 m'indiquera la clé de ticket associée au commit qui se trouve sur la pointe de ma branche. Comme dans la livraison 2.1.0, la complétion bash a été améliorée pour me permettre d'utiliser l'alias des tickets Git comme s'il s'agissait de la commande git log.

Dans Git 2.0.3, saisir git issues m<onglet> aurait automatiquement induit la complétion bash par défaut, et les fichiers commençant par m auraient été listés dans le répertoire courant. Dans Git 2.1.0, l'opération s'effectue dans master, puisque c'est ainsi que la commande git log réagirait. Il vous est également possible d'amorcer la complétion bash en ajoutant une commande nulle à votre alias, commençant par « : ». Cette solution est pratique si la commande Git que vous voulez exécuter n'est pas la première de vos alias. Par exemple, l'alias :

issues = "!f() { echo 'Printing issue keys'; git log --oneline $@ | egrep -o [A-Z]+-[0-9]+ | sort | uniq; }; f"

doesn't work properly with completion because git fails to recognize the echo command as a completion target. But if you prefix it with ': git log ;' completion works correctly again:

issues = "!f() { : git log ; echo 'Printing issue keys'; git log --oneline $@ | egrep -o [A-Z]+-[0-9]+ | sort | uniq; }; f"

C'est tout bénéfique si vous aimez créer des alias Git complexes ! Rappelez-vous que tout ceci se trouve dans contrib/, et non dans la structure principale de Git. Par conséquent, n'oubliez pas de mettre à jour vos paramètres bash pour indiquer la nouvelle version de contrib/completion/git-completion.bash si nécessaire.

approxidate comes to git commit

L'option « git commit ‐‐date=<date> » intègre davantage de formats d'horodotage, comme « ‐‐date=now ». Le flag --date de git commit réintègre l'analyseur approxidate, fonctionnalité ô combien géniale (bien que légèrement farfelue), alors que son cousin plus sérieux parse_date() ne peut analyser une chaîne de dates particulière. approxidate peut analyser des éléments clairs, comme --date=now, mais aussi des formats légèrement plus ésotériques, comme --date="midnight the 12th of october, anno domini 1979" ou --date=teatime. Si vous souhaitez en savoir plus, je vous recommande le billet d'Alex Peattie sur l'excellente gestion des dates de Git.

Chemins améliorés avec grep.fullname

La commande « git grep » a intégré la variable de configuration grep.fullname afin que l'option « ‐‐full-name » soit sélectionnée par défaut. Cela peut entraîner des régressions dans les scripts qui utilisent des commandes Git et ne s'attendent pas à ce nouveau comportement. Je vous fais grâce de l'appel de man git-grep : --full-name

Lorsqu'elle est exécutée à partir d'un sous-répertoire, la commande génère généralement les chemins associés au répertoire courant. Cette option force la sortie de chemins associés au répertoire supérieur du projet. C'est génial ! Cette valeur par défaut est parfaite pour mon workflow, qui nécessite souvent d'exécuter git grep pour retrouver un chemin et le copier-coller dans un fichier XML quelque part (comme vous l'aurez peut-être compris, je suis développeur Java). Si elle est pratique pour vous aussi, exécutez simplement :

$ git config --global grep.fullname true

pour l'activer dans votre configuration.

Le flag --global applique la configuration à mon fichier $HOME/.gitconfig pour en faire le comportement par défaut pour l'ensemble des dépôts Git de mon système. Si nécessaire, il est toujours possible de modifier ce comportement à l'échelle des dépôts.

git replace a évolué

Une minute ! N'oubliez pas le backup. Quel est le rôle premier de git replace ?

In a nutshell, git replace lets you rewrite certain objects in your git repository without changing their corresponding tree or commit SHAs. If this is the first time you've heard of git replace and you're familiar with the git data model this may sound like sacrilege! It certainly did to me. I have another blog post in the works for when and why you might use such a feature. If you want to learn more in the meantime this article is far better than the man page, which is a little scant on use cases.

Bref, comment git replace a-t-elle évolué ?

La commande « git replace » a intégré la sous-commande « ‐‐edit » pour effectuer un remplacement lorsqu'un objet existant est modifié. L'option --edit permet de copier puis de remplacer facilement un objet spécifique en transférant son contenu vers un fichier temporaire et en lançant l'éditeur souhaité. Pour remplacer le commit situé sur la pointe de master, exécutez simplement :

$ git replace --edit master

Ou, pour modifier le blob que le commit situé sur la pointe de master considère comme jira-components/pom.xml, vous pouvez exécuter :

$ git replace --edit master:jira-components/pom.xml

Est-ce une bonne idée ? Pas sûr :) Dans la plupart des cas, vous utiliserez git rebase pour réécrire des objets. Ainsi, les empreintes SHA de vos commits seront correctement réécrites, et votre historique restera propre.

La commande « git replace » a intégré une option « ‐‐graft », qui vous permet de réécrire les parents d'un commit. L'option --graft est un raccourci qui sert à remplacer un commit par un commit identique, mais dont les parents diffèrent. C'est une manière plus judicieuse d'utiliser « git replace », tout en raccourcissant votre historique Git. Pour remplacer le parent de la pointe de ma branche master, je pourrais simplement exécuter :

$ git replace master --graft [new parent]..

Pour couper mon historique à un certain niveau, je peux rendre un commit orphelin en omettant complètement les nouveaux parents :

$ git replace master --graft

Encore une fois, il vous faut une bonne raison pour choisir cette solution. En règle générale, une utilisation judicieuse de git rebase est la méthode privilégiée pour réécrire un historique.

Classement des tags sensibles avec tag.sort

« git tag » est désormais capable de tenir compte de la configuration « tag.sort » destinée à être utilisée comme l'ordre de tri par défaut si aucune option ‐‐sort= n'est indiquée. C'est une excellente nouvelle si vous utilisez des numéros de version dans vos noms de tag, ce qui est le cas pour 99,9 % d'entre vous j'imagine. Une fois que vous avez livré une version contenant un segment de plus d'un chiffre (p. ex. v10 ou v1.10), le classement lexicographique par défaut de Git ne la coupe plus. Prenez, par exemple, le classement par défaut des tags du dépôt Git d'Atlassian Stash (désormais Bitbucket Server) .

src/stash $ git tag -l *.*.0
..
stash-parent-2.0.0
stash-parent-2.1.0
stash-parent-2.10.0
stash-parent-2.11.0
stash-parent-2.12.0
stash-parent-2.2.0
stash-parent-2.3.0
stash-parent-2.4.0
stash-parent-2.5.0
stash-parent-2.6.0
stash-parent-2.7.0
stash-parent-2.8.0
stash-parent-2.9.0
stash-parent-3.0.0
..

Pas bien ! Chronologiquement, « 2.10.0 » suit « 2.3.0 », donc cet ordre de tri par défaut des tags n'est pas correct. Depuis Git 2.0.0, il est possible d'utiliser le flag --sort pour trier les versions numériques correctement :

src/stash $ git tag --sort="version:refname" -l *.*.0
..
stash-parent-2.0.0
stash-parent-2.1.0
stash-parent-2.2.0
stash-parent-2.3.0
stash-parent-2.4.0
stash-parent-2.5.0
stash-parent-2.6.0
stash-parent-2.7.0
stash-parent-2.8.0
stash-parent-2.9.0
stash-parent-2.10.0
stash-parent-2.11.0
stash-parent-2.12.0
stash-parent-3.0.0
..

Beaucoup mieux. Avec Git 2.1.0, vous pouvez définir cet ordre comme réglage par défaut en exécutant :

$ git config --global tag.sort version:refname

D'ailleurs, le flag très pratique -l utilisé dans les exemples de git tag ci-dessus limite les noms de tag affichés à un modèle donné. -l *.*.0 est utilisé pour montrer uniquement les livraisons mineure et majeure de Stash (à présent connu sous le nom de Bitbucket Server).

Vérification simplifiée des commits signés

La commande « git verify-commit » a été ajoutée. Elle permet de vérifier les signatures GPG dans les commits signés à l'instar de « git verify-tag », et peut également servir à vérifier les tags signés. Si vous signez les commits pour en authentifier les auteurs, la commande verify-commit facilite grandement la vérification des signatures. Plutôt que d'écrire votre propre script pour analyser la sortie de git log --show-signature, ajoutez simplement une plage de commits à verify-commit pour vérifier les signatures. Cela dit, il y a de fortes chances que vous n'utilisiez pas de commits signés pour le moment (nous ne le faisons pas chez Atlassian), puisqu'ils sont source de désagréments pour les gestionnaires et les développeurs. Pour la plupart des projets, signer les tags est généralement considéré comme une meilleure alternative pour trouver le juste équilibre entre le côté pratique et la sécurité. Si vous vous demandez pourquoi un projet se prête mieux à l'utilisation des commits signés, Mike Gerwitz imagine un véritable cauchemar Git. Dans son scénario hypothétique, ces commits seraient très utiles. Si vous travaillez pour un secteur particulièrement sensible, vous pourriez envisager de les intégrer à votre workflow.

Gains de performances

Git 2.1.0 a également fait l'objet de quelques améliorations de performance bien utiles.

Git 2.1.0 intègre un format expérimental qui utilise deux fichiers (le fichier de base et les changements incrémentiels qui s'y rapportent) pour représenter l'index. Ce système pourrait réduire les coûts d'E/S liés à la réécriture d'un grand index lorsque seules de petites parties de l'arborescence de travail sont modifiées. Traduction : si avez généralement des commits volumineux qui modifient beaucoup de fichiers, exécuter git add pourrait s'avérer plus rapide. git add opère déjà à la vitesse de l'éclair pour toutes les incrémentations que j'ai testées en local ; je n'ai donc pas constaté de grande différence de performances entre les versions. Fait intéressant : le premier ajout semble avoir légèrement gagné en vitesse pour les ensembles de fichiers volumineux. Pendant mes tests rapides, j'ai essayé de stager tous les changements de la base de code Jira apportés entre Jira 5 et Jira 6.

$ git checkout jira-v6.0
$ git reset jira-v5.0
$ time git add --all

Dans Git 2.0.3, j'avais noté une moyenne de 2,44 secondes, contre 2,18 secondes pour Git 2.1.0 – soit un gain de temps supérieur à 10 % ! Remarque : le chronométrage n'a pas vraiment été effectué dans des conditions de laboratoire et nous gagnons environ un quart de seconde si nous ajoutons plus de 14 500 fichiers à la fois à l'index, donc vous ne devriez pas constater de gros changements si vous utilisez Git normalement. Pour en savoir plus sur le nouveau format d'index partagé, consultez la documentation relative aux formats d'index.

La variable de configuration « core.preloadindex » est activée par défaut. Elle permet aux plateformes modernes de tirer parti de leurs nombreux cœurs. Sympa, non ? Je n'avais pas activé cette fonctionnalité auparavant, mais après le passage à la version 2.1.0, l'augmentation des performances est très nette. Lors d'un autre test rapide, j'ai essayé de chronométrer l'exécution de git status par rapport aux changements stagés entre Jira 5 et Jira 6 que j'ai mentionnés plus haut. Dans Git 2.0.3, j'ai noté une moyenne de 4,94 secondes pour plus de 14 500 fichiers stagés, contre 3,99 secondes pour Git 2.1.0 – un gain de temps impressionnant d'environ 19 %. C'est particulièrement utile si vous utilisez une invite Shell personnalisée, qui vérifie si votre copie de travail présente des changements non commités à chaque fois qu'elle est renvoyée. L'exécution de bash m'a paru un peu plus rapide pour les index très volumineux. La commande « git blame » a été considérablement améliorée ; la structure de données utilisée a fait l'objet d'une réorganisation, ce qui permet de garder un œil sur le travail restant. git blame détermine désormais plus rapidement qui <del>a commis une bourde</del> a ajouté une ligne de code particulière :). Je suis très satisfait de cette amélioration en particulier, puisque cela signifie que git-guilt (un petit outil que j'ai développé pour savoir dans quelle mesure blame change entre les commits) pourrait devenir plus performant, son fonctionnement dépendant fortement de la sortie de blame.

Lors d'un autre test rapide, j'ai vérifié le temps nécessaire pour calculer le transfert de guilt vers le dépôt source Git 2.0.0 et 2.1.0. Par conséquent, git-guilt fait un fork des 886 commandes git blame sur des fichiers de taille variable modifiés entre Git 2.0.0 et Git 2.1.0.

$ git guilt v2.0.0 v2.1.0

J'ai noté une moyenne de 72,1 secondes pour Git 2.0.3 et une moyenne de 66,7 secondes pour Git 2.1.0, soit une amélioration de 7,5 % ! Si vous êtes curieux, vous pouvez voir le transfert de git-guilt (Karsten Blees vient tout juste de devancer Junio C. Hamano de 66 LOC).

Tous ces tests de performance sont réalisés de façon ad hoc, mais nous procédons actuellement à la mise à niveau de Bitbucket Cloud pour Git 2.1.0. Nous allons contrôler les fonctionnalités avant et après la mise à niveau pour déterminer dans quelle mesure la nouvelle version influence les performances de certaines opérations Git, en particulier blame et diff. Je vous tiendrai au courant de notre progression d'ici quelques semaines.

Et ce n'est pas fini !

Git 2.1.0 comporte plein d'autres trucs géniaux dont je n'ai pas parlé dans ce billet de blog, mais vous pouvez toujours consulter les notes de livraison complètes si ça vous intéresse. Mention spéciale à l'équipe Git, qui nous propose une fois encore une livraison de qualité et ultra fonctionnelle. Si vous voulez lire d'autres trucs et astuces liés à l'univers Git, n'hésitez pas me suivre (@kannonboy) et l'équipe Atlassian Dev Tools (@atldevtools) sur Twitter.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant