# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ##@cooked nocomments ##@require nulib.sh ##@require base module: git "Fonctions pour faciliter l'utilisation de git" require: nulib base function: git_geturl "" function git_geturl() { git config --get remote.origin.url } function: git_have_annex "" function git_have_annex() { [ -n "$(git config --get annex.uuid)" ] } NULIB_GIT_FUNCTIONS=( git_check_gitvcs git_ensure_gitvcs git_list_branches git_list_rbranches git_have_branch git_have_rbranch git_get_branch git_is_branch git_have_remote git_track_branch git_check_cleancheckout git_ensure_cleancheckout git_is_ancestor git_should_ff git_should_push git_is_merged ) NULIB_GIT_FUNCTIONS_MAP=( cg:git_check_gitvcs eg:git_ensure_gitvcs lbs:git_list_branches rbs:git_list_rbranches hlb:git_have_branch hrb:git_have_rbranch gb:git_get_branch ib:git_is_branch hr:git_have_remote tb:git_track_branch cc:git_check_cleancheckout ec:git_ensure_cleancheckout ia:git_is_ancestor sff:git_should_ff spu:git_should_push im:git_is_merged ) function: git_get_toplevel "" function git_get_toplevel() { git rev-parse --show-toplevel 2>/dev/null } function: git_check_gitvcs "" function git_check_gitvcs() { git rev-parse --show-toplevel >&/dev/null } function: git_ensure_gitvcs "" function git_ensure_gitvcs() { git_check_gitvcs || die "$(ppath "$(pwd)" ~): ce répertoire n'est pas un dépôt git" || return } function: git_list_branches "" function git_list_branches() { git for-each-ref refs/heads/ --format='%(refname:short)' | csort } function: git_list_rbranches "" function git_list_rbranches() { git for-each-ref "refs/remotes/${1:-origin}/" --format='%(refname:short)' | csort } function: git_list_pbranches "lister les branches locales et celles qui existent dans l'origine \$1(=origin) et qui pourraient devenir une branche locale avec la commande git checkout -b" function git_list_pbranches() { local prefix="${1:-origin}/" { git for-each-ref refs/heads/ --format='%(refname:short)' git for-each-ref "refs/remotes/$prefix" --format='%(refname:short)' | grep -F "$prefix" | cut -c $((${#prefix} + 1))- } | grep -vF HEAD | csort -u } function: git_have_branch "" function git_have_branch() { git_list_branches | grep -qF "$1" } function: git_have_rbranch "" function git_have_rbranch() { git_list_rbranches "${2:-origin}" | grep -qF "$1" } function: git_get_branch "" function git_get_branch() { git rev-parse --abbrev-ref HEAD 2>/dev/null } function: git_get_branch_remote "" function git_get_branch_remote() { local branch="$1" [ -n "$branch" ] || branch="$(git_get_branch)" [ -n "$branch" ] || return git config --get "branch.$branch.remote" } function: git_get_branch_merge "" function git_get_branch_merge() { local branch="$1" [ -n "$branch" ] || branch="$(git_get_branch)" [ -n "$branch" ] || return git config --get "branch.$branch.merge" } function: git_get_branch_rbranch "" function git_get_branch_rbranch() { local branch="$1" remote="$2" merge [ -n "$branch" ] || branch="$(git_get_branch)" [ -n "$branch" ] || return [ -n "$remote" ] || remote="$(git_get_branch_remote "$branch")" [ -n "$remote" ] || return merge="$(git_get_branch_merge "$branch")" [ -n "$merge" ] || return echo "refs/remotes/$remote/${merge#refs/heads/}" } function: git_is_branch "" function git_is_branch() { [ "$(git_get_branch)" == "${1:-master}" ] } function: git_have_remote "" function git_have_remote() { [ -n "$(git config --get remote.${1:-origin}.url)" ] } function: git_track_branch "" function git_track_branch() { local branch="$1" origin="${2:-origin}" [ -n "$branch" ] || return git_have_remote "$origin" || return [ "$(git config --get branch.$branch.remote)" == "$origin" ] && return if git_have_rbranch "$branch" "$origin"; then if git_have_branch "$branch"; then git branch -u "$origin/$branch" "$branch" else git branch -t "$branch" "$origin/$branch" fi elif git_have_branch "$branch"; then git push -u "$origin" "$branch" || return fi } function: git_ensure_branch " @return 0 si la branche a été créée, 1 si elle existait déjà, 2 en cas d'erreur" function git_ensure_branch() { local branch="$1" source="${2:-master}" origin="${3:-origin}" [ -n "$branch" ] || return 2 git_have_branch "$branch" && return 1 if git_have_rbranch "$branch" "$origin"; then # une branche du même nom existe dans l'origine. faire une copie de cette branche git branch -t "$branch" "$origin/$branch" || return 2 else # créer une nouvelle branche du nom spécifié git_have_branch "$source" || return 2 git branch "$branch" "$source" || return 2 if [ -z "$NULIB_GIT_OFFLINE" ]; then git_have_remote "$origin" && git_track_branch "$branch" "$origin" fi fi return 0 } function: git_check_cleancheckout "vérifier qu'il n'y a pas de modification locales dans le dépôt correspondant au répertoire courant." function git_check_cleancheckout() { [ -z "$(git status --porcelain 2>/dev/null)" ] } git_cleancheckout_VERBOSE=1 git_cleancheckout_DIRTY="Vous avez des modifications locales. Enregistrez ces modifications avant de continuer" function: git_ensure_cleancheckout "" function git_ensure_cleancheckout() { git_check_cleancheckout && return [ -n "$git_cleancheckout_VERBOSE" ] && git status --porcelain 2>/dev/null die "$git_cleancheckout_DIRTY" || return } function git__init_ff() { o="${3:-origin}" b="$1" s="${2:-refs/remotes/$o/$1}" b="$(git rev-parse --verify --quiet "$b")" || return 1 s="$(git rev-parse --verify --quiet "$s")" || return 1 return 0 } function git__can_ff() { [ "$1" == "$(git merge-base "$1" "$2")" ] } function: git_is_ancestor "vérifier que la branche \$1 est un ancêtre direct de la branche \$2, qui vaut par défaut refs/remotes/\${3:-origin}/\$1 note: cette fonction retourne vrai si \$1 et \$2 identifient le même commit" function git_is_ancestor() { local o b s; git__init_ff "$@" || return git__can_ff "$b" "$s" } function: git_should_ff "vérifier si la branche \$1 devrait être fast-forwardée à partir de la branche d'origine \$2, qui vaut par défaut refs/remotes/\${3:-origin}/\$1 note: cette fonction est similaire à git_is_ancestor(), mais retourne false si \$1 et \$2 identifient le même commit" function git_should_ff() { local o b s; git__init_ff "$@" || return [ "$b" != "$s" ] || return 1 git__can_ff "$b" "$s" } function: git_should_push "vérifier si la branche \$1 devrait être poussée vers la branche de même nom dans l'origine \$2(=origin), parce que l'origin peut-être fast-forwardée à partir de cette branche." function git_should_push() { git_should_ff "refs/remotes/${2:-origin}/$1" "$1" } function: git_fast_forward "vérifier que la branche courante est bien \$1, puis tester s'il faut la fast-forwarder à partir de la branche d'origine \$2, puis le faire si c'est nécessaire. la branche d'origine \$2 vaut par défaut refs/remotes/origin/\$1" function git_fast_forward() { local o b s; git__init_ff "$@" || return [ "$b" != "$s" ] || return 1 local head="$(git rev-parse HEAD)" [ "$head" == "$b" ] || return 1 git__can_ff "$b" "$s" || return 1 git merge --ff-only "$s" } function: git_is_merged "vérifier que les branches \$1 et \$2 ont un ancêtre commun, et que la branche \$1 a été complètement fusionnée dans la branche destination \$2" function git_is_merged() { local b="$1" d="$2" b="$(git rev-parse --verify --quiet "$b")" || return 1 d="$(git rev-parse --verify --quiet "$d")" || return 1 [ -n "$(git merge-base "$b" "$d")" ] || return 1 [ -z "$(git rev-list "$d..$b")" ] }