Le point sur les évolutions depuis Git 1.7
Par Christophe Porteneuve • Publié le 28 août 2014
• 8 min
Vous êtes nombreux à utiliser Git sans bien suivre ce qui change d’une version à l’autre. Parfois, vous vous contentez aussi de la version disponible sur votre distribution Linux, même quand celle-ci date un peu.
Faisons le point sur les évolutions depuis Git 1.7, histoire de voir ce que vous loupez :-)
Mise à jour : la version anglaise de cet article est ici
Déjà, où en suis-je et comment mettre à jour ?
À l’heure où j’écris ceci, la version courante est la 2.1.0, qui date du 15/08/2014.
Le site officiel maintient des builds officiels pour Mac et Windows (ce dernier étant souvent un poil en retard, par exemple là tout de suite c’est la 1.9.4), et bien sûr les sources pour ceux qui veulent compiler ça eux-mêmes.
Notez que sur Mac, j’ai tendance à installer et mettre à jour via Homebrew, qui est généralement à jour sous 48h.
Vous pouvez tester votre version en cours :
$ git --version
git version 2.1.0
Pour mettre à jour sur Windows, installez simplement le dernier download fourni par le site officiel.
Sur Mac, utilisez soit l’installeur officiel si vous passiez par là, soit une mise à jour Homebrew si vous êtes comme moi :
$ brew update
…
$ brew upgrade git
==> Upgrading 1 outdated package, with result:
git 2.1.0
…
$ git --version
git version 2.1.0
Sur Linux, si vous passez par les paquets officiels (ou des PPA enregistrés), utilisez la commande d’installation ou de mise à jour adaptée (généralement apt-get
sur Debian/Ubuntu, yum
sur Fedora, etc.).
Un mot sur Debian et Ubuntu
Pour Debian et Ubuntu, les versions officielles accusent souvent un bon retard, surtout les LTS, en restant scotchées à leur version de sortie initiale :
- 10.04 LTS « Lucid Lynx » a la 1.7.0 (septembre 2010)
- 12.04 LTS « Precise Pangolin » a la 1.7.9 (janvier 2012)
- 14.04 LTS « Trusty Tahr » a la 1.9.1 (mars 2014)
Je ne saurais trop vous recommander d’enregistrer tout de suite le PPA officiel :
$ sudo add-apt-repository ppa:git-core/ppa
Si vous n’avez pas la commande add-apt-repository
, commencez par installer le paquet nécessaire :
$ sudo apt-get install python-software-properties
# Et si vous avez 14.04 ou au-delà…
$ sudo apt-get install software-properties-common
Vous aurez ainsi la dernière version (le PPA est mis à jour sous 3-4 jours en moyenne après la sortie centrale).
Liste choisie des évolutions à connaître
On ne va pas juste paraphraser les release notes : je me concentre sur les nouveautés fonctionnelles (pas les correctifs et améliorations de performances), en choisissant celles qui me semblent présenter un intérêt particulier.
Spécifiquement, je laisse de côté tout ce qui est foreign interface (interfaçage entre Git et les autres systèmes de gestion de version) et outils graphiques (gitk, git-gui, git-web), ainsi que les commandes de plomberie, pour me concentrer sur celles de porcelaine (celles que l’utilisateur normal est susceptible d’utiliser fréquemment).
Plutôt que de dérouler un titre par version, je vous fais trois gros intervalles, volontairement calés sur les versions de base des Ubuntu LTS récentes.
D’une manière générale, plus on avance dans les versions, plus les complétions Bash et ZSH et les prompts avancés s’améliorent. Par exemple, à bien des égards, le prompt Bash officiel fourni par __git_ps1
enterre fonctionnelement celui de Oh My ZSH.
Entre 1.7.0 et 1.7.9
Il y a eu un paquet de 1.7.x (12 !), mais le gros des modifs utiles est apparu dans la 1.7.2, qui a longtemps constitué mon « minimum vital ».
Pas mal de protections contres les pièges classiques des submodules ont fait leur apparition à cette période :
git diff
etgit status
sont plus détaillés et explicites sur les modifs locales apportées aux submodules, ce qui contribue à réduire les risques d’oubli de commit/push sur ceux-ci. Diverses options et variables de configuration permettent de personnaliser ces nouveaux comportements.git fetch --recurse-submodules
fait son apparition en 1.7.4, histoire de gagner un peu de temps.- Joli garde-fou depuis 1.7.7 dans
git push
en cas de commits locaux non pushés sur les submodules :git push --recurse-submodules=check
. - Depuis la 1.7.8, les dossiers Git des submodules ne sont plus directement dans leur checkout, mais dans le
.git/modules/
du dépôt conteneur, et déréférencés par gitfiles (leur.git
est un fichier texte contenant une lignegitdir: chemin-du-dossier-git-déporté
). Ça permet de recaler le commit utilisé d’un submodule au niveau parent sans avoir besoin d’en avoir fait le checkout ; dans la pratique, je trouve ça assez edge case, mais connaître ce déport de stockage évite les surprises en inspectant les dossiers de submodules…
Les logs ont aussi connu des améliorations importantes :
- La syntaxe de révision
:/pattern
, qui prenait jusqu’en 1.7.1 un texte simple, est interprétée comme regex depuis 1.7.2, et n’est plus limitée au seul début du message de commit (nettement plus pratique). - Apparition des colorations de
git log --decorate
en 1.7.2. - Apparition du pendant regex de
git log -S
:git log -G
, en 1.7.4. Indispensable ! git log
accepte des globs (ex.*.rb
) comme chemins depuis la 1.7.5.- Apparition de
log.abbrevCommit
en 1.7.6, qui évite les SHA complets dans les logs mais en fait un peu partout…
Côté fusions, rebases, push et pull, quelques trucs importants, voire centraux, ont débarqué :
- Apparition du sympathique mode
--keep
pourgit reset
(variation degit reset --merge
, qu’on appelle plus récemmentgit merge --abort
). git cherry-pick
(et incidemmentgit revert
) sont depuis 1.7.2 capables d’exploiter les stratégies de fusion (--strategy
), ce qui est super pratique dans le cadre des subtrees notamment.- Apparition de l’importante configuration
merge.ff
, qui pilote l’approche par défaut aux fast-forwards degit merge
, en 1.7.6. - Depuis 1.7.6,
git rebase
sans paramètre est depuis 1.7.6 équivalent àgit rebase @{u}
, donc rebase sur l’upstream. À mon sens pas très utile (je voudrais fetcher avant, donc faire en fait ungit pull --rebase
ou équivalent), mais potentiellement dangereux car ce n’est plus une no-op. On trouvera la même évolution plus récemment pourgit merge
, pfff… - Apparition du synonyme
upstream
, qui remplacera plus tardtracking
comme valeur de configuration pourpush.default
, en 1.7.5 (je recommande d’ailleursupstream
plutôt que le nouveau défaut,simple
). - La configuration critique
pull.rebase
n’est apparue qu’en 1.7.9 (uniquement avectrue
etfalse
à ce stade). core.autocrlf
évite désormais de pourrir des fichiers aux fins de ligne mixtes : seuls les fichiers 100% LF seront manipulés.
Certaines améliorations de confort me semblent aussi dignes d’être mentionnées :
- Le script officiel de complétion, qui est très pointu, est devenu compatible Zsh (et plus seulement Bash) en 1.7.4.
- Dans le même esprit que
git checkout -
, la 1.7.6 apportegit merge -
, pour fusionner la branche qu’on vient de quitter (très fréquent, bien pratique). D’autres commandes suivront plus récemment. - La 1.7.7 nous a enfin donné l’option
--include-untracked
/-u
surgit stash
! - Depuis 1.7.8, il est possible de fournir un glob à
git branch
, à condition de le préfixer par--list
:git branch --list pr-*
. - Depuis 1.7.9,
git commit --amend --no-edit
est une autre manière de faire le traditionnelgit commit --amend -C HEAD
. - Grosses améliorations fonctionnelles sur
git notes
, notamment pour les persister au travers de réécritures d’historique (rebase, amend…).
Entre 1.7.9 et 1.9.1
C’est depuis la 1.7.10 qu’un merge qui réussit en terminal se sent obligé d’ouvrir l’éditeur sur le message de commit… Perso ça me saoûle profond et je le désactive en assurant un GIT_MERGE_AUTOEDIT=no
dans mon environnement.
Notamment dans la période 1.8.x, il y a eu énormément de nouveautés et d’améliorations. Voici ma sélection…
Côté log et consorts (diff, notamment), pas mal de petites nouveautés et améliorations sympathiques :
git log -S
/-G
honorent enfin-i
(insensible à la casse) depuis 1.7.10- Depuis la 1.8.2, on peut auto-désactiver les codes couleurs de nos formats
git log
en les préfixant, par exemple%C(auto,blue)
au lieu de juste%C(blue)
. Pas révolutionnaire, mais pratique. - Depuis 1.8.3, le code couleur spécial
%C(auto)
est utilisé par le format de log%d
(décorations) pour restituer les couleurs nativement employées (ex. vert pour les branches locales, rouge pour les distantes, jaune pour les tags, bleu pour les symboliques…). Petit bonus sympa. - La 1.8.4 introduit, à titre expérimental,
git log -L
, pour suivre l’histo d’une portion plus ou moins avancée d’un fichier. - On peut s’intéresser depuis 1.9.0 à « tout sauf un chemin », par exemple dans
log
, avec une spécification négative :git log -- . ':!dir'
(attention aux quotes autour de la syntaxe, car!
est interpolé par la plupart des shells). - Depuis 1.9.0, on peut exclure des branches de
git log
et ses copains à l’aide de--exclude=<glob>
, par exemplegit log --exclude='*/*' --branches
. - Il est possible de définir globalement le nombre de lignes de contexte des diffs avec
diff.context
depuis la 1.8.1.
Côté push et pull, quelques nouveautés dont une absolument critique :
- Apparition du mode de push
simple
dans la 1.7.11, qui deviendra le défaut en 2.0. Je comprends l’intérêt pour les débutants (et encore), mais perso je préfèreupstream
, vu que je me réserve le droit de pusher sur une branche non éponyme. - Enfin un mode zéro-défaut de pull grâce à
pull.rebase=preserve
, merci 1.8.5 !
La tendance à rajouter des garde-fous contre les pièges des submodules continue :
- Après son introduction en 1.7.7 avec la seule valeur
check
, le pratiquegit push --recurse-submodules=…
apprend la valeuron-demand
, qui fait les pushes manquants. - L’ensemble des sous-commandes de
git submodule
ont cessé en 1.8.4 d’exiger qu’on les lance depuis la racine du dépôt. Parce que merde, hein.
Et parce que les subtrees sont une alternative intéressante aux submodules mais peuvent être difficiles à manipuler, la 1.7.11 intègre le script de contrib git subtree
dans la distro officielle. Je ne suis toutefois fan que de sa capacité à reporter le log partiel des modifs locales vers l’upstream ; pour le reste, je trouve qu’il pourrit joyeusement le graphe.
Côté configuration, attributs et fichiers ignorés, on voit aussi quelques trucs sympa débarquer :
- La configuration globale (par utilisateur) de Git n’est plus nécessairement dans
~/.gitconfig
depuis 1.7.12 : en conformité avec XDG, elle peut être dans~/.config/git/config
, accompagnée d’autres fichiers auto-détectés par leurs configurations respectives, commeattributes
(pourcore.attributesfile
) etignore
(pourcore.excludesfiles
) par exemple. - On peut enfin utiliser
**/
dans.gitignore
(et.gitattributes
) depuis la 1.8.2, pour indiquer une profondeur quelconque. Ça peut bien sûr être préfixé par un chemin parent, sans quoi c’est superflu.
Comme toujours, la complétion et le prompt évoluent vers davantage de puissance, avec notamment les points suivants :
- C’est depuis la 1.7.12 qui la complétion et les prompts ont été découpés en scripts indépendants.
- Des scripts de complétion dédiés à Zsh et Tcsh sont apparus en 1.8.1.
- Une amélioration très utile du prompt mérite d’être signalée à partir de 1.8.3 : lors d’un rebase interactif, il indique précisément où on en est (ex.
REBASE-i 3/8
), un vrai bonheur. Dans le même esprit,git status
est plus détaillé lors d’un rebase (ou d’un bisect, d’ailleurs).
Quelques autres évolutions orientées confort d’utilisation :
- Depuis la 1.7.12,
git help
bascule sur l’affichage HTML en navigateur quandman
n’est pas disponible (ex. Windows). On peut le forcer avecgit help -w
. - Pour réécrire tout l’historique d’une branche, c’était galère faute de point de base. Depuis 1.7.12,
git rebase --root
le permet facilement. - Il est possible d’abréger
HEAD
en@
(pratique sur Mac !) depuis la 1.8.5. - Tout comme
merge
etcheckout
,cherry-pick
autorise l’argument-
depuis la 1.8.5, pour chopper la tête de la branche précédente.
Enfin, vous avez peur de virer trop de trucs avec git clean
? Depuis 1.8.4, on a le bien pratique git clean -i
, qui est interactif.
Entre 1.9.1 et 2.1.0
À partir de la 1.9.0, Git a adopté le semver, donc les versions de 3ème niveau (ex. 1.9.1, 1.9.2) n’apportent que des correctifs. Pour trouver des nouveautés et améliorations, il faut aller au 1er (ex. 2.0.0) ou 2ème (ex. 2.1.0) niveau.
Notez qu’à partir de ce niveau, sur Debian/Ubuntu par exemple vous serez obligés de recourir à une installation alternative, idéalement le PPA (voir début d’article).
On parle ici de 2–3 gros changements et un poil de confort.
git rebase
rejoint dans la 2.0merge
,checkout
etcherry-pick
dans le club des commandes acceptant-
pour dire « la branche précédente » (pour rappel, la syntaxe universelle serait@{-1}
).- À partir de la 2.0,
git tag --list
sait trier les tags en tant que numéros de versions, de sorte quev2.10.3
serait considéré ultérieur àv2.9.4
, par exemple. Il suffit de préciser--sort=version:refname
. Pour rappel, l’option--sort
peut préfixer sa valeur par-
pour inverser l’ordre. Si vous taguez principalement des versions et voulez ce comportement par défaut, depuis la 2.1 vous avez une variable de configurationtag.sort
pour ça. - La 2.0 change la valeur originale de
push.default
àsimple
. Encore une fois, je recommande plutôtupstream
pour les non-débutants (voire pour tous). - Autre changement important de la 2.0 :
git add -A
/-u
sans chemin fourni traite maintenant par défaut tout le dépôt, au lieu du dossier courant et ses sous-dossiers comme auparavant. Gaffe à vos scripts, aliases et habitudes ! - Attention, depuis la 2.1,
git merge
seul n’est plus une no-op (enfin, un refus plus exactement) : si un upstream est défini (la branche tracke une distante), c’est lui qu’on merge. Il rejoint ici l’approche degit rebase
.
Mettez-vous à jour !
Avec Git comme avec la plupart de vos logiciels importants, avoir une version récente est très utile. Pensez à vous mettre à jour régulièrement (le début de l’article vous rappelle comment faire techniquement).
J’espère qu’à la lecture de cet article, vous avez trouvé plein de raisons de mettre à jour votre Git si ce n’est déjà fait, et peut-être découvert quelques possibilités cool que vous ignoriez.
Bon Git !