Le prompt Bash qui change la vie avec Git
Par Christophe Porteneuve • Publié le 14 juillet 2010
• 2 min
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…
Cet article est obsolète ; vous trouverez une version plus à jour et plus utile dans l’article plus récent « Un prompt Git qui déchire ».
Voici le code source avec quelques commentaires. Vous pouvez aussi aller piocher le Gist directement.
# 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!