Lors de l’atelier de samedi je vous ai promis un prompt Bash qui déboîte quand on fait du Git.
Celui-ci repose sur une série de fonctions Bash dont la plus « high-level » est appelée à chaque prompt. Elle permet de voir immédiatement :
- Dans quelle branche on est
- Si on a des modifs en working tree
- Si on a des modifs en stage
- Si on a des fichiers untracked
- Si on a du stash
Toutes choses fort utiles au moment de faire son commit, histoire de ne rien oublier…
Voici le code source avec quelques commentaires. Vous pouvez aussi aller piocher le Gist directement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | # Scavenged from Git 1.6.5.x contrib/completion/git_completion.bash # __git_ps1 accepts 0 or 1 arguments (i.e., format string) # returns text to add to bash PS1 prompt (includes branch name) __gitdir () { if [ -z "${1-}" ]; then if [ -n "${__git_dir-}" ]; then echo "$__git_dir" elif [ -d .git ]; then echo .git else git rev-parse --git-dir 2>/dev/null fi elif [ -d "$1/.git" ]; then echo "$1/.git" else echo "$1" fi } __git_ps1 () { local g="$(__gitdir)" if [ -n "$g" ]; then local r local b if [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" else if [ -d "$g/rebase-apply" ]; then if [ -f "$g/rebase-apply/rebasing" ]; then r="|REBASE" elif [ -f "$g/rebase-apply/applying" ]; then r="|AM" else r="|AM/REBASE" fi elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" elif [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi b="$(git symbolic-ref HEAD 2>/dev/null)" || { b="$( case "${GIT_PS1_DESCRIBE_STYLE-}" in (contains) git describe --contains HEAD ;; (branch) git describe --contains --all HEAD ;; (describe) git describe HEAD ;; (* | default) git describe --exact-match HEAD ;; esac 2>/dev/null)" || b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || b="unknown" b="($b)" } fi local w local i local s local u local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then c="BARE:" else b="GIT_DIR!" fi elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then git diff --no-ext-diff --ignore-submodules \ --quiet --exit-code || w="*" if git rev-parse --quiet --verify HEAD >/dev/null; then git diff-index --cached --quiet \ --ignore-submodules HEAD -- || i="+" else i="#" fi fi fi if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" fi if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then if [ -n "$(git ls-files --others --exclude-standard)" ]; then u="%" fi fi fi if [ -n "${1-}" ]; then printf "$1" "$c${b##refs/heads/}$w$i$s$u$r" else printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r" fi fi } export GIT_PS1_SHOWDIRTYSTATE=1 GIT_PS1_SHOWUNTRACKEDFILES=1 GIT_PS1_SHOWSTASHSTATE=1 # Basique… # export PS1='\u@\h:\W$(__git_ps1) \$ ' # Avec plein de couleurs… export PS1='\[\033[0;37m\]\u@\h:\[\033[0;33m\]\W\[\033[0m\]\[\033[1;32m\]$(__git_ps1)\[\033[0m\] \$ ' |
Notez les variables qui configurent ce qui est affiché :
- GIT_PS1_SHOWDIRTYSTATE fait remonter les modifs en working tree (symbole *) et en stage (symbole +)
- GIT_PS1_SHOWUNTRACKED_FILES fait remonter les fichiers non présents dans l’index (non pris en compte jusqu’à ajout explicite) (symbole %)
- GIT_PS1_SHOWSTASHSTATE indique qu’on a des trucs dans le stash (symbole $)
Ça ralentit un tout petit peu le premier prompt de votre session dans un dépôt non trivial, le temps pour les commandes Git internes de construire les caches d’accès à l’index et à l’historique… Ensuite c’est instantané.
Je ne pourrais pas vivre sans !
Enjoy!














#1 by Sunny on 07/19/2010 - 14:49
Malheureusement sur de gros dépôts au bureau le prompt mets longtemps à se construire la première fois mais pas loin de 2 secondes à s’afficher les fois suivante… (Avec des volumes montés sur le réseau.)
Mais en désactivant GIT_PS1_SHOWDIRTYSTATE GIT_PS1_SHOWUNTRACKEDFILES ça va bien mieux.
[WORDPRESS HASHCASH] The poster sent us ’0 which is not a hashcash value.
#2 by Christophe on 07/23/2010 - 22:21
Sunny: j’ai des dépôts de Gros Bill™, et si le prompt toutes-options met en effet quelques secondes à se construire au départ, il est instantané ensuite. Je pense que le fautif ici est ton montage réseau, qui doit beaucoup ralentir le parsing interne de .git… :-/
#3 by Kaelig on 07/23/2010 - 10:09
Merci Christophe, précisons que c’est à coller dans .bash_profile (par ex).
[WORDPRESS HASHCASH] The poster sent us ’0 which is not a hashcash value.
#4 by Christophe on 07/23/2010 - 22:21
En effet. Our .profile, voire .bashrc suivant la config en vigueur.