Commit 964c0ae6 authored by Pierre-Elliott Bécue's avatar Pierre-Elliott Bécue

[.bashrc] Modular and expressive VCS functions

parent 8a79d465
......@@ -5,7 +5,7 @@
# Licence : WTFPL
# Les sections commentées par #~# sont des features qui ne sont pas activées
# par défaut. Sentez-vous libre de les décommenter pour les utiliser.
# par défaut. Sentez-vous libre de les décommenter pour les utiliser.
#------------------------------------------------------------------------------
......@@ -36,7 +36,6 @@ fi
# ils seront chargés par la ligne suivante
[ -d ~/.bash_completion.d/ ] && for f in ~/.bash_completion.d/*; do source $f; done
# +-----+
# | VCS |
# +-----+
......@@ -44,57 +43,422 @@ fi
# Définition de fonction pour pouvoir afficher dans le prompt
# des infos quand on est dans un dépôt versionné
find_up () {
local path normalized_path normalized_ret
path="$1"
shift 1
normalized_path=`readlink -f -- "$path"`
normalized_ret=$?
while [[ "$normalized_path" != "/" ]] && [ $normalized_ret -eq 0 ];
do
find "$path" -maxdepth 1 -mindepth 1 "$@"
path=${path}/..
normalized_path=`readlink -f -- "$path"`
normalized_ret=$?
done
# Checks if the command provided is in the commands list and is
# executable
check_command(){
[[ -n ${commands[$1]} ]] && [ -x ${commands[$1]} ] && return 0
return 1
}
get_vcs_info () {
# Donne les infos sur le dépôt VCS courant.
local LBRANCH LTYPE BRANCH TYPE DIR
declare -a DIR
declare -A TYPE
declare -A BRANCH
DIR[0]=".git"
DIR[1]=".hg"
DIR[2]="_darcs"
DIR[3]=".svn"
TYPE[.git]="git"
TYPE[.hg]="mercurial"
TYPE[_darcs]="darcs"
TYPE[.svn]="svn"
BRANCH[.git]='git branch 2>/dev/null | sed -r "s/^[^*].*$//" | paste -s -d "" | sed -r "s/^[*] //"'
BRANCH[.hg]='hg branch 2>/dev/null'
BRANCH[_darcs]="darcs show repo 2>/dev/null| egrep '^ *Cache' | sed 's@.*/\([^/]*\),.*@\1@'"
BRANCH[.svn]="svn info 2>/dev/null | head -n 6 | tail -n 1"
DIR=$(eval "find_up \"$PWD\" -name \"\"$(printf -- ' -o -name "%s"' "${DIR[@]}") | head -n 1")
if [ -n "$DIR" ]; then
DIR=$(basename "$DIR")
LBRANCH=$(eval "${BRANCH[$DIR]}")
LTYPE="${TYPE[$DIR]}"
if [ "$color_prompt" = yes ]; then
VCS_info="${nocolor_prompt}${vcs_symbols_color}(${vcs_type_color}$LTYPE${vcs_symbols_color})-${vcs_symbols_color}[${vcs_branch_color}$LBRANCH${vcs_symbols_color}]${nocolor_prompt}"
# If name should be overwritten (eg for git-svn), do it.
vcs_adjust(){
[[ -n ${vcs_comm[overwrite_name]} ]] && vcs=${vcs_comm[overwrite_name]}
return 0
}
# Formats two VCS_info messages, one with colors, and one without
vcs_formats(){
local action=$1 branch=$2 base=$3 rev=$4
local msg
local -i i
# printf is for readability (it's easier to find %s)
msg="(%s)-[%s/%s"
msg=$(printf $msg $vcs ${base/*\/} $branch)
# If there is a revnumber, print it
if [ ! -z ${rev} ]; then
msg="${msg}:%s"
msg=$(printf $msg $rev)
fi
# Print the current cvs action state
if [ ! -z ${action} ] ; then
msg="${msg}|%s"
msg=$(printf $msg $action)
fi
msg="${msg}]-"
msgs[1]=$msg
# Same shit with colors
msg="${nocolor_prompt}${vcs_symbols_color}(${vcs_type_color}%s${vcs_symbols_color})${vcs_sep_color}-${vcs_symbols_color}[${vcs_repo_color}%s${vcs_sep_color}/${vcs_branch_color}%s"
msg=$(printf $msg $vcs ${base/*\/} $branch)
if [ ! -z ${rev} ]; then
msg="${msg}${vcs_colon_color}:${vcs_rev_color}%s"
msg=$(printf $msg $rev)
fi
if [[ ! -z ${action} ]] ; then
msg="${msg}${nocolor_prompt}|${vcs_action_color}%s"
msg=$(printf $msg $action)
fi
msg="${msg}${vcs_symbols_color}]${nocolor_prompt}-"
msgs[0]=$msg
return 0
}
# Uses -P option for cd in order to resolve symlinks
vcs_realpath(){
(
cd -P $1 2>/dev/null && pwd
)
}
# Feature to detect a special dir, at the top of
# the current repo
detect_by_dir(){
local dirname=$1
local basedir="." realbasedir
realbasedir="$(vcs_realpath ${basedir})"
while [[ ${realbasedir} != '/' ]]; do
[[ -r ${realbasedir} ]] || return 1
# Tries to find detect_need_file (eg formats) in the dir
if [[ -n ${vcs_comm[detect_need_file]} ]] ; then
[[ -d ${basedir}/${dirname} ]] && \
[[ -e ${basedir}/${dirname}/${vcs_comm[detect_need_file]} ]] && \
break
else
VCS_info="($LTYPE)-[$LBRANCH]"
[[ -d ${basedir}/${dirname} ]] && break
fi
basedir=${basedir}/..
realbasedir="$(vcs_realpath ${basedir})"
done
[[ ${realbasedir} == "/" ]] && return 1
vcs_comm[basedir]=${realbasedir}
return 0
}
# Git is powerfull
git_detect(){
if check_command git && git rev-parse --is-inside-work-tree &> /dev/null; then
vcs_comm[gitdir]="$(git rev-parse --git-dir 2> /dev/null)" || return 1
if [[ -d ${vcs_comm[gitdir]}/svn ]] ; then vcs_comm[overwrite_name]='git-svn'
elif [[ -d ${vcs_comm[gitdir]}/refs/remotes/p4 ]] ; then vcs_comm[overwrite_name]='git-p4' ; fi
return 0
fi
return 1
}
# Mercurial isn't
hg_detect(){
check_command hg || return 1
vcs_comm[detect_need_file]=store
detect_by_dir '.hg'
return $?
}
# Neither is svk
# TODO - Not working : imported from zsh but not post treated
svk_detect(){
local -a info
local -i fhash
fhash=0
check_command svk || return 1
[[ -f ~/.svk/config ]] || return 1
# This detection function is a bit different from the others.
# We need to read svk's config file to detect a svk repository
# in the first place. Therefore, we'll just proceed and read
# the other information, too. This is more then any of the
# other detections do but this takes only one file open for
# svk at most. VCS_INFO_svk_get_data() get simpler, too. :-)
while IFS= read -r line ; do
if [[ -n ${vcs_comm[basedir]} ]] ; then
line=${line## ##}
[[ ${line} == depotpath:* ]] && vcs_comm[branch]=${line##*/}
[[ ${line} == revision:* ]] && vcs_comm[revision]=${line##*[[:space:]]##}
[[ -n ${vcs_comm[branch]} ]] && [[ -n ${vcs_comm[revision]} ]] && break
continue
fi
(( fhash > 0 )) && [[ ${line} == ' '[^[:space:]]*:* ]] && break
[[ ${line} == ' hash:'* ]] && fhash=1 && continue
(( fhash == 0 )) && continue
[[ ${PWD}/ == ${${line## ##}%:*}/* ]] && vcs_comm[basedir]=${${line## ##}%:*}
done < ~/.svk/config
[[ -n ${vcs_comm[basedir]} ]] && \
[[ -n ${vcs_comm[branch]} ]] && \
[[ -n ${vcs_comm[revision]} ]] && return 0
return 1
}
# .svn in each directories
svn_detect() {
check_command svn || return 1
[[ -d ".svn" ]] && return 0
return 1
}
bzr_detect(){
check_command bzr || return 1
vcs_comm[detect_need_file]=branch/format
detect_by_dir '.bzr'
return $?
}
cdv_detect(){
check_command cdv || return 1
vcs_comm[detect_need_file]=format
detect_by_dir '.cdv'
return $?
}
cvs_detect(){
check_command svn || return 1
[[ -d "./CVS" ]] && [[ -r "./CVS/Repository" ]] && return 0
return 1
}
darcs_detect(){
check_command darcs || return 1
vcs_comm[detect_need_file]=format
detect_by_dir '_darcs'
return $?
}
# Find git's branch
git_getbranch (){
local gitbranch gitdir=$1 tmp actiondir
local gitsymref='git symbolic-ref HEAD'
# In certain circumstances, we have to take into account
# actions
actiondir=''
for tmp in "${gitdir}/rebase-apply" \
"${gitdir}/rebase" \
"${gitdir}/../.dotest"; do
if [[ -d ${tmp} ]]; then
actiondir=${tmp}
break
fi
done
if [[ -n ${actiondir} ]]; then
gitbranch="$(${gitsymref} 2> /dev/null)"
[[ -z ${gitbranch} ]] && [[ -r ${actiondir}/head-name ]] \
&& gitbranch="$(< ${actiondir}/head-name)"
# MERGE_HEAD state
elif [[ -f "${gitdir}/MERGE_HEAD" ]] ; then
gitbranch="$(eval $gitsymref 2> /dev/null)"
[[ -z ${gitbranch} ]] && gitbranch="$(< ${gitdir}/MERGE_HEAD)"
# rebase
elif [[ -d "${gitdir}/rebase-merge" ]] ; then
gitbranch="$(< ${gitdir}/rebase-merge/head-name)"
# dotest
elif [[ -d "${gitdir}/.dotest-merge" ]] ; then
gitbranch="$(< ${gitdir}/.dotest-merge/head-name)"
# Normal case
else
gitbranch="$(eval $gitsymref 2> /dev/null)"
# shit happens
if [[ $? -ne 0 ]] ; then
gitbranch="refs/tags/$(git describe --exact-match HEAD 2>/dev/null)"
# big shit happens
if [[ $? -ne 0 ]] ; then
gitbranch=$(< $gitdir/HEAD)
gitbranch="${gitbranch:0:7}..."
fi
fi
fi
# keep only the last part of gitbranch
printf '%s' "${gitbranch#refs/[^/]*/}"
return 0
}
git_getaction(){
local gitaction='' gitdir=$1
local tmp
for tmp in "${gitdir}/rebase-apply" \
"${gitdir}/rebase" \
"${gitdir}/../.dotest" ; do
if [[ -d ${tmp} ]] ; then
if [[ -f "${tmp}/rebasing" ]] ; then
gitaction="rebase"
elif [[ -f "${tmp}/applying" ]] ; then
gitaction="am"
else
gitaction="am/rebase"
fi
printf '%s' ${gitaction}
return 0
fi
VCS_size=$((${#LTYPE}+${#LBRANCH}+5))
done
for tmp in "${gitdir}/rebase-merge/interactive" \
"${gitdir}/.dotest-merge/interactive" ; do
if [[ -f "${tmp}" ]] ; then
printf '%s' "rebase-i"
return 0
fi
done
for tmp in "${gitdir}/rebase-merge" \
"${gitdir}/.dotest-merge" ; do
if [[ -d "${tmp}" ]] ; then
printf '%s' "rebase-m"
return 0
fi
done
if [[ -f "${gitdir}/MERGE_HEAD" ]] ; then
printf '%s' "merge"
return 0
fi
if [[ -f "${gitdir}/BISECT_LOG" ]] ; then
printf '%s' "bisect"
return 0
fi
return 1
}
git_get_data(){
local gitdir gitbase gitbranch gitaction
gitdir=${vcs_comm[gitdir]}
gitbranch="$(git_getbranch ${gitdir})"
if [[ -z ${gitdir} ]] || [[ -z ${gitbranch} ]] ; then
return 1
fi
vcs_adjust
gitaction="$(git_getaction ${gitdir})"
gitprefix=$(git rev-parse --show-prefix)
gitbase=${PWD%/${gitprefix%/}}
vcs_formats "${gitaction}" "${gitbranch}" "${gitbase}" ''
return 0
}
hg_get_data(){
local hgbranch hgbase file
hgbase=${vcs_comm[basedir]}
file="${hgbase}/.hg/branch"
if [[ -r ${file} ]] ; then
hgbranch=$(< ${file})
else
VCS_info=""
VCS_size=0
hgbranch='default'
fi
vcs_formats '' "${hgbranch}" "${hgbase}" ''
return 0
}
svk_get_data(){
local svkbranch svkbase
svkbase=${vcs_comm[basedir]}
svkbranch=${vcs_comm[branch]}
svkrevision=${vcs_comm[revision]}
vcs_formats '' "${svkbranch}" "${svkbase}" "${svkrevision}"
return 0
}
svn_get_data(){
local svnbase svnbranch
local -a svninfo
svnbase="."
while [[ -d "${svnbase}/../.svn" ]]; do
svnbase="${svnbase}/.."
done
svnbase="$(vcs_realpath ${svnbase})"
svnrev=$(svn info | awk '{if($1 == "Révision :") print $2}')
svnbranch=$(svn info | awk '{if($1 == "URL :") print $2}'|awk -F "/" '{ print $NF }')
vcs_formats '' "${svnbranch}" "${svnbase}" "${svnrev}"
return 0
}
bzr_get_data(){
local bzrbase bzrbr
local -a bzrinfo
bzrbase=$(bzr info|awk '{if ($1 == "branch" && $2 == "root:") print $3}')
bzrbranch=$(bzr version-info|awk '{if ($1 == "branch-nick:") print $2}')
bzrrev=$(bzr version-info|awk '{if ($1 == "revno:") print $2}')
bzrbase="$(vcs_realpath ${bzrbase})"
bzrbr="${bzrbranch}"
vcs_formats '' "${bzrbr}" "${bzrbase}" "${bzrrev}"
return 0
}
cdv_get_data(){
local cdvbase
cdvbase=${vcs_comm[basedir]}
vcs_formats '' "${cdvbase/*\/}" "${cdvbase}" ''
return 0
}
cvs_get_data(){
local cvsbranch cvsbase basename
cvsbase="."
while [[ -d "${cvsbase}/../CVS" ]]; do
cvsbase="${cvsbase}/.."
done
cvsbase="$(vcs_realpath ${cvsbase})"
cvsbranch=$(< ./CVS/Repository)
basename=${cvsbase/*\/}
cvsbranch=${cvsbranch#${basename}/}
[[ -z ${cvsbranch} ]] && cvsbranch=${basename}
vcs_formats '' "${cvsbranch}" "${cvsbase}" ''
return 0
}
darcs_get_data(){
local darcsbase
darcsbase=${vcs_comm[basedir]}
vcs_formats '' "${darcsbase/*\/}" "${darcsbase}" ''
return 0
}
vcs_info(){
local -i found
local -ax msgs
local -Ax vcs_comm commands
local -x vcs
local -a vcss
vcs="init"
vcss=(git hg bzr darcs svk svn cvs cdv)
for i in $(seq 0 $(( ${#vcss[*]} - 1 ))); do
commands[${vcss[$i]}]=$(which ${vcss[$i]});
done;
found=0
for vcs in ${vcss[*]}; do
${vcs}_detect && found=1 && break
done
(( found == 1 )) && ${vcs}_get_data
if [ ${color_prompt} = "yes" ]; then
VCS_info=${msgs[0]}
else
VCS_info=${msgs[1]}
fi
VCS_size=${#msgs[1]}
}
# Pour avoir le bon umask en fonction du dossier où on se trouve
# L'umask définit avec quel droits un fichier est créé.
......@@ -151,30 +515,6 @@ blanc_prompt="\[${blanc}\]"
blanc_thin_prompt="\[${blanc_thin}\]"
nocolor_prompt="\[${nocolor}\]"
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# support de la couleur
color_prompt=yes
# Couleurs dans "user@host $"
username_color=${rouge_prompt}
host_color=${bleu_prompt}
symbols_color=${vert_prompt}
# Couleur de la ligne séparatrice de prompt
line_color=${cyan}
line_color_prompt=${cyan_prompt}
# Couleur du path actuel
pwd_color=${jaune_prompt}
# Couleur de la date (à chaque affichage du prompt)
date_color=${violet_prompt}
# Couleur de la date au premier affichage (à l'ouverture du terminal)
announce_date_color=${blanc}
# Couleur d'affichage de vcs_info
vcs_symbols_color=${violet_thin_prompt}
vcs_type_color=${jaune_thin_prompt}
vcs_branch_color=${vert_thin_prompt}
else
# pas de support de la couleur
color_prompt=no
fi
# Est-ce qu'on veut que le prompt affiche les information sur l'éventuel dépôt
# versionné dans lequel on se trouve
......@@ -215,7 +555,7 @@ function prompt_command
# À décommenter si on veut afficher des infos
# quand on se trouve dans un dépôt versionné
if [ "$display_vcs_info" = yes ]; then
get_vcs_info
vcs_info
fi
# Chemin courant, en faisant attention à la largeur de la fenêtre
......@@ -257,6 +597,36 @@ function prompt_command
fi
}
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# support de la couleur
color_prompt=yes
# Couleurs dans "user@host $"
username_color=${rouge_prompt}
host_color=${bleu_prompt}
symbols_color=${vert_prompt}
# Couleur de la ligne séparatrice de prompt
line_color=${cyan}
line_color_prompt=${cyan_prompt}
# Couleur du path actuel
pwd_color=${jaune_prompt}
# Couleur de la date (à chaque affichage du prompt)
date_color=${violet_prompt}
# Couleur de la date au premier affichage (à l'ouverture du terminal)
announce_date_color=${blanc}
# Couleur d'affichage de vcs_info
vcs_symbols_color=${violet_thin_prompt}
vcs_type_color=${jaune_thin_prompt}
vcs_branch_color=${vert_thin_prompt}
vcs_repo_color=${vert_thin_prompt}
vcs_action_color=${rouge_thin_prompt}
vcs_sep_color=${jaune_thin_prompt}
vcs_rev_color=${jaune_thin_prompt}
vcs_colon_color=${rouge_thin_prompt}
else
# pas de support de la couleur
color_prompt=no
fi
# On change le titre de la fenêtre dynamiquement si on est sous X
if [[ $TERM = "xterm" ]]; then
TITLE='\[\e];\u@\h:\w\a\]'
......@@ -264,7 +634,7 @@ else
TITLE=''
fi
# On régénère le prompt après chaque commande
# On regénére le prompt après chaque commande
PROMPT_COMMAND=prompt_command
# +-------------------+
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment