~rom1v/blog { un blog libre }

Prompt Bash pour GIT

J’utilise git depuis quelques mois, et je trouve ça vraiment génial. Si vous ne connaissez pas, ou peu, vous ne pouvez pas ne pas lire le livre Pro Git (sous licence cc-by-nc-sa). Les explications très claires permettent en quelques heures de maîtriser toutes les fonctions de base, et d’être à l’aise avec la gestion des branches (et bien plus encore).

Branches visibles

Le but de ce billet est de répondre à un problème particulier : par manque d’attention, il m’est arrivé plusieurs fois de commiter des changements sur une mauvaise branche (j’étais persuadé d’être sur une branche, en fait j’étais sur une autre). Ce n’est pas très grave (on peut s’en sortir), mais c’est pénible.

Je souhaiterais donc avoir le nom de la branche dans le prompt bash.

Des solutions existent déjà : le paquet git embarque même un script qui répond au besoin. Certains utilisent aussi des scripts personnalisés. Mais aucun de ceux que j’ai trouvés ne me convenait. J’ai donc écrit mon propre script.

Mes prompts

Version simple

J’ai commencé par une version simple, qui ajoute en couleur @nomdelabranche à la fin du prompt. Un exemple vaut mieux qu’un long discours :

rom@rom-laptop:~/dev$ cd myproject/
rom@rom-laptop:~/dev/myproject@master$ git checkout testing
Switched to branch 'testing'
rom@rom-laptop:~/dev/myproject@testing$ cd img
rom@rom-laptop:~/dev/myproject/img@testing$ 

Dans une arborescence ayant plusieurs projets GIT imbriqués (dans le cas de l’utilisation de sous-modules), la branche des projets parents n’est pas affichée :

rom@rom-laptop:~/dev$ cd mybigproject/
rom@rom-laptop:~/dev/mybigproject@master$ cd submodule/
rom@rom-laptop:~/dev/mybigproject/submodule@master$ git checkout exp
Switched to branch 'exp'
rom@rom-laptop:~/dev/mybigproject/submodule@exp$ cd ..
rom@rom-laptop:~/dev/mybigproject@master$ 

Version améliorée

Dans cette version simple, le nom de la branche est toujours affiché à la fin. Cela ne me convient pas, je le voudrais toujours à la racine du projet en question. C’est ce que permet la version améliorée.

Voici le résultat avec les mêmes commandes :

rom@rom-laptop:~/dev$ cd myproject/
rom@rom-laptop:~/dev/myproject@master$ git checkout testing
Switched to branch 'testing'
rom@rom-laptop:~/dev/myproject@testing$ cd img
rom@rom-laptop:~/dev/myproject@testing/img$ 

Et avec des sous-modules, la branche des projets parents est affichée :

rom@rom-laptop:~/dev$ cd mybigproject/
rom@rom-laptop:~/dev/mybigproject@master$ cd submodule/
rom@rom-laptop:~/dev/mybigproject@master/submodule@master$ git checkout exp
Switched to branch 'exp'
rom@rom-laptop:~/dev/mybigproject@master/submodule@exp$ cd ..
rom@rom-laptop:~/dev/mybigproject@master$ 

En image :

gitbashprompt

Script

Le script, sous licence WTFPL, est disponible sur ce dépôt git : [gitbashprompt].

Une fois cloné, éditez le fichier ~/.bashrc pour remplacer l’initialisation de la variable PS1 :

        PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '

par :

        . full/path/to/your/gitbashprompt

Pour tester, ouvrir un nouveau terminal.

Conclusion

Tout d’abord, je suis content d’avoir exactement le comportement que je souhaitais pour mon git.

Ensuite, j’ai découvert le fonctionnement du prompt, avec notamment les subtilités d’échappement de caractères de la variable PS1 et la prise en compte des caractères de contrôle \[ et \].

Enfin, je me suis enfin décidé à étudier la gestion des couleurs de Bash (qui, à première vue, est assez repoussante, il faut bien l’avouer). Mes scripts seront donc plus jolis à l’avenir ;-)

Commentaires

huit_six

Merci pour l’astuce,

Très utile

C’est pas ce que propose nativement ZSH ? Ou alors c’est via Oh My ZSH ?

®om

@tominardi

Je viens de tester, nativement, non.

Je ne connaissais pas oh-my-zsh, je testerai quand j’aurai un moment. Si tu l’utilises, tu peux indiquer ce qu’il fait exactement pour GIT ?

cosmocat

je me demande, si ça ressembla pas à çà…

https://yeevgen.wordpress.com/2011/05/23/mercurial-zsh-prompt/

pums974

Merci pour ce petit script très pratique.

Cependant j’ai eu un problème chez moi, il a fallut que j’enlève des guillemets pour qu’il marche:

-        SFI="$IFS"
+        SFI=$IFS
-        IFS="$SFI"
+        IFS=$SFI
®om

Je viens de mettre à jour le script (v0.11), car la première version n’affichait plus du tout le chemin lorsqu’on était dans un répertoire hors du home (quand le chemin affiché commençait par /).

Améliorer son prompt pour supporter git est vraiment quelque chose d’obligatoire une fois que l’on utilise fréquement git.

Personnellement, j’utilise zsh avec un script custom pour afficher les informations de Git, pour l’instant je n’affiche que la branche courant et l’état du répo (modified ou up-to-date).

Et pour info, oh-my-zsh utilise un principe de plugin pour afficher un prompt “git” complet, il est même possible d’afficher dans le prompt si on est en avance ou en retard sur l’origin.

®om

@pums974 Ah, bizarre, je ne comprends pas pourquoi.

Cependant, pour moi il faut des guillemets (si on ancien IFS contenait par exemple un ~, il ne faut pas que bash l’interprète).

Peux-tu nous montrer ton IFS :

printf "$IFS" | hd

?

C’est vrai que se passer de ce genre d’infos une fois qu’on y a goûté, c’est difficile.

C’est un minimum d’afficher la branche mais je trouve qu’afficher l’état du dépôt (nouveau fichier non ajouté au dépôt, fichier modifié) est indispensable également.

Pour ma part, j’utilise le module vcs_info de zsh. C’est vraiment super efficace et cela se configure selon ses envies. De plus, pratiquement tous les VCS sont supportés (git, hg, svn, etc.).

J’en avais fait un article en janvier dernier, si cela intéresse quelqu’un…

J’aime bien ton “par manque d’inattention” :)

®om

@bartounet Merci, corrigé. ;-)

En plus, ce matin, je me suis dit qu’il fallait que je vérifie, car il me semblait bien avoir écrit “par manque d’inattention”. Mais j’ai finalement oublié de vérifier, sans doute par manque de concentration ;-)

le packet git de mageia (rpm chez moi: git-core-1.7.4.4-2.mga1), a modifié la variable PS1 par défaut et installé un fichier pour bash_completion (/etc/bash_completion.d/git) qui fait déjà cela (et même bcp plus, cf. completion).

voila mon PS1 :

"$?[\u@\h \W$(type __git_ps1 &>/dev/null && __git_ps1 " (%s)")]\$"

(j’ai rajouté \$? …)

Asenar

Merci beaucoup !

Une suggestion : pourquoi ne pas mettre ton script dans github ?

®om

@Asenar C’est vrai que j’aurais pu mettre le script sur un dépôt git. D’ailleurs, je le fais :

git clone http://git.rom1v.com/gitbashprompt.git

Pour github, j’essaie d’éviter (autant que faire se peut) les services centralisés, d’autant qu’il n’est pas libre…

Après, techniquement, je ne dis pas, c’est sympa les pull requests et tout ça… Une solution pragmatique serait de le mettre sur github en plus d’un dépôt sur lequel on a la main.

Mais pour un simple script comme celui-là, je vais me contenter de mon dépôt http ;-)

J’utilise ça :

https://github.com/olivierverdier/zsh-git-prompt

ça permet d’avoir le git status dans le prompt également :)

Bonsoir,

Il est vrai que le livre Pro Git est assez formidable. Perso, j’utilise Git depuis un moment maintenant pour mes projets Java et je trouve cet outil super puissant, facile à utiliser et rapide. Un utile vraiment utile !

Je pense donc que c’est une bonne chance d’en parler à travers un blog, cela promeut l’outil ;-).

Bonne soirée,

®om

Je viens de publier une version 0.12, qui corrige un petit bug dans certains cas lorsqu’on utilise les sous-modules.

En effet, le répertoire .git d’un sous-module peut se trouver ailleurs qu’à la racine du projet, et être référencé par un fichier .git contenant par exemple :

gitdir: ../.git/modules/monprojet

Avec la version 0.11, la branche ne s’affichait pas dans ce cas, c’est maintenant corrigé.

huit_six

Bonjour,

Encore merci pour ton script, je l’utilise quotidiennement.

Petite remarque, sous bash, il y a un (bug ? comportement bizarre ?) qui fait qu’on peut mettre deux / en début de chemin et dans ce cas, ton script ne fonctionne pas (aucun chemin n’est affiché).

Je sais c’est un peu du pinaillage mais j’ai eu le cas ce matin et je ne comprenais pas pourquoi je ne retrouvait plus mon repo ^^

®om

@huit_six :

Je ne savais pas qu’on pouvait mettre // en début de chemin. Que cela signifie-t-il ?

Tu peux préciser le chemin que tu mets stp pour reproduire le problème ?

J’ai essayé avec un projet git dans //tmp, je n’ai pas eu de problème…

L’implémentation est peu efficace : tout le code shell est dans la variable PS1, donc le parsing des 40 lignes de code shell (la grosse boucle while) et la vérification de la syntaxe sont fait à chaque affichage du prompt. Une meilleure solution serait de faire comme fait le plugin Git de bash (voir __git_ps1): mettre le code dans une fonction et ne mettre dans PS1 que l’appel à la fonction.

De plus les variables col et nocol pourraient être développées et supprimées de l’environnement.

®om

@Olivier Mengué (dolmen) :

Bonnes remarques.

Quand j’aurai un moment, j’en ferai une fonction à part.

®om

@Olivier Mengué (dolmen) :

J’ai donc mis le contenu de la fonction dans une fonction à part, et je l’ai appelée dans PS1.

J’ai créé une nouvelle branche temporaire nommée func pour cette modif :

git clone http://git.rom1v.com/gitbashprompt.git -b func

Mais je n’arrive pas à faire fonctionner les couleurs comme avant.

En effet, si j’utilise le script que je t’envoie tel quel, ça affiche les \[ :

~/MonProjet\[\]@master\[\]$

Si je retire ces \[\] “en trop”, ça s’affiche correctement, mais le terminal, lui, se trouve buggé : il retourne à la ligne avant la fin de la ligne (et écrit par-dessus le texte déjà présent) car il “croit” que les codes couleurs (\e[34m) comptent pour 6 caractères, alors qu’en réalité aucun de ces caractères n’est affiché. C’est la raison pour laquelle j’avais encadré le code couleur de \[ et \] dans ma première version, mais ça ne semble pas fonctionner s’ils se trouvent dans une fonction à part.

As-tu une idée ?

®om

J’ai trouvé !

\[ et \] ne sont interprétés que par PS1, dans une fonction il faut utiliser \001 et \002.

Un bout du commit qui résout le problème (1d062d9) :

--- gitbashprompt
+++ gitbashprompt
@@ -9,8 +9,8 @@
 
 __git_bash_prompt() {
     # Colors
-    local nocol='\[\e[0m\]'
-    local col='\[\e[34m\]' # blue
+    local nocol='\001\e[0m\002'
+    local col='\001\e[34m\002' # blue
 
     SFI="$IFS"
     IFS=/

Cette nouvelle version du script (0.2) a maintenant été mergée sur la branche master et supprimé la branche temporaire func.

Romlinch

Connaissez-vous LiquidPrompt ?

https://github.com/nojhan/liquidprompt

Il affiche:

  • Le niveau de batterie à partir d’un seuil
  • Le proxy
  • La charge à partir d’un seuil
  • Dans un dossier git, la branche, le nombre de commit par rapport au remote
  • etc

Les commentaires sont fermés.