1043 lines
31 KiB
Bash
1043 lines
31 KiB
Bash
##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
## Fonctions pour gérer les outils de vcs (subversion, git)
|
|
##@cooked nocomments
|
|
##@require base
|
|
uprovide vcs
|
|
urequire base
|
|
|
|
################################################################################
|
|
# Général
|
|
|
|
function __vcs_get_type() {
|
|
# Afficher le type de vcs du répertoire $1. L'arborescence n'est pas
|
|
# parcourue. Si l'information n'est pas immédiatement disponible, cette
|
|
# fonction échoue.
|
|
# retourner 1 si le type n'a pas été trouvé
|
|
[ -d "$1/.git" ] && { echo "git"; return; }
|
|
[ -d "$1/.svn" ] && { echo "svn"; return; }
|
|
[ -d "$1/CVS" ] && { echo "cvs"; return; }
|
|
return 1
|
|
}
|
|
|
|
function __vcs_find_type() {
|
|
# Chercher le type de vcs du répertoire $1. L'arborescence jusqu'au
|
|
# répertoire $HOME est parcourue si nécessaire. $1 doit être un répertoire
|
|
# absolu.
|
|
# Retourner 1 si le type n'a pas été trouvé
|
|
local root="$1"
|
|
# soit on a l'information immédiatement...
|
|
__vcs_get_type "$root" && return
|
|
# soit il faut la chercher en parcourant l'arborescence...
|
|
while [ -n "$root" -a "$root" != "/" -a "$root" != "." -a "$root" != "$HOME" ]; do
|
|
__vcs_get_type "$root" && return
|
|
root="$(dirname "$root")"
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function __vcs_upward_until() {
|
|
local root="$1"
|
|
local type="$2"
|
|
while [ -n "$root" -a "$root" != "/" -a "$root" != "." -a "$(__vcs_get_type "$root/..")" == "$type" ]; do
|
|
root="$(dirname "$root")"
|
|
done
|
|
echo "$root"
|
|
}
|
|
|
|
function __vcs_find_root() {
|
|
# A partir du répertoire $1, chercher le répertoire de base du checkout
|
|
# local. $1 doit être un répertoire absolu.
|
|
# retourner 1 si le type n'a pas été trouvé
|
|
local root="$1"
|
|
local type="$(__vcs_get_type "$root")"
|
|
if [ "$type" == "git" ]; then
|
|
# si le type est git, nous sommes forcément déjà à la racine
|
|
echo "$root"
|
|
return
|
|
elif [ "$type" == "svn" -o "$type" == "cvs" ]; then
|
|
# trouver le répertoire le plus haut
|
|
__vcs_upward_until "$root" "$type"
|
|
return
|
|
else
|
|
# sinon, il faut chercher dans les répertoire au dessus jusqu'à trouver
|
|
# un repository. On arrête la recherche à $HOME
|
|
local found
|
|
while [ -n "$root" -a "$root" != "/" -a "$root" != "." -a "$root" != "$HOME" ]; do
|
|
type="$(__vcs_get_type "$root")" && { found=1; break; }
|
|
root="$(dirname "$root")"
|
|
done
|
|
if [ -n "$found" ]; then
|
|
if [ "$type" == "git" ]; then
|
|
echo "$root"
|
|
return
|
|
elif [ "$type" == "svn" -o "$type" == "cvs" ]; then
|
|
__vcs_upward_until "$root" "$type"
|
|
return
|
|
fi
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
function _vcs_get_dir() {
|
|
abspath "${1:-.}"
|
|
}
|
|
function _vcs_get_type() {
|
|
__vcs_find_type "$1" || {
|
|
eerror "$(ppath "$1"): Ce répertoire n'est pas versionné"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
function _vcs_unsupported() {
|
|
eerror "${1:+$1: }Opération ou option non supportée"
|
|
return 1
|
|
}
|
|
function _vcs_invalid_copy() {
|
|
eerror "Vous ne pouvez copier/déplacer qu'un fichier sur un fichier"
|
|
return 1
|
|
}
|
|
function _vcs_check_nbargs() {
|
|
[ "$1" -ge "$2" ] || {
|
|
eerror "Cette commande nécessite au moins $2 argument(s)"
|
|
return 1
|
|
}
|
|
if [ -n "$3" ]; then
|
|
[ "$1" -le "$3" ] || {
|
|
eerror "Cette commande supporte au plus $3 argument(s)"
|
|
return 1
|
|
}
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function _vcs_showhelp() {
|
|
local action func
|
|
action="$1"; shift
|
|
func="vcs_${action}_help"
|
|
[ $# == 1 -a "$1" == "--help" ] && { "$func" "$action"; return; }
|
|
return 1
|
|
}
|
|
|
|
function _vcs_dispatch() {
|
|
local dir action type func
|
|
dir="$(_vcs_get_dir "$1")"; shift
|
|
action="$1"; shift
|
|
_vcs_showhelp "$action" "$@" && return
|
|
type="$(_vcs_get_type "$dir")" || return
|
|
func="${type}_${action}"
|
|
"$func" "$@"
|
|
}
|
|
|
|
########################################
|
|
function vcs_getvcs_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Afficher le type de vcs pour le répertoire spécifié
|
|
|
|
USAGE
|
|
uproject $1 [dir]"
|
|
}
|
|
function vcs_getvcs() {
|
|
_vcs_showhelp getvcs "$@" && return
|
|
__vcs_find_type "$(_vcs_get_dir "$1")"
|
|
}
|
|
########################################
|
|
function vcs_getroot_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Afficher le répertoire racine du projet versionné correspondant au répertoire spécifié
|
|
|
|
USAGE
|
|
uproject $1 [dir]"
|
|
}
|
|
function vcs_getroot() {
|
|
_vcs_showhelp getvcs "$@" && return
|
|
__vcs_find_root "$(_vcs_get_dir "$1")"
|
|
}
|
|
########################################
|
|
function vcs_getrepos_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Afficher l'url du repository du projet versionné correspondant au répertoire spécifié
|
|
|
|
USAGE
|
|
uproject $1 [dir]"
|
|
}
|
|
function vcs_getrepos() {
|
|
_vcs_dispatch "$1" getrepos "$@"
|
|
}
|
|
########################################
|
|
function vcs_geturl_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Afficher l'url dans le repository du répertoire versionné spécifié
|
|
|
|
USAGE
|
|
uproject $1 [dir]"
|
|
}
|
|
function vcs_geturl() {
|
|
_vcs_dispatch "$1" geturl "$@"
|
|
}
|
|
########################################
|
|
function vcs_vcs_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Appeler le gestionnaire de version approprié avec les arguments donnés
|
|
|
|
USAGE
|
|
uproject $1 [args]
|
|
|
|
note: les commandes suivantes permettent de lancer des opérations avancées:
|
|
$(array_to_lines __VCS_GIT_ADVANCED "" " ")"
|
|
}
|
|
function vcs_vcs() {
|
|
_vcs_showhelp vcs "$@" && return
|
|
|
|
local i f t
|
|
for i in "${__VCS_GIT_ADVANCED_MAP[@]}"; do
|
|
splitpair "$i" f t
|
|
if [ "$1" == "$f" ]; then
|
|
shift
|
|
set -- "$t" "$@"
|
|
break
|
|
fi
|
|
done
|
|
if array_contains __VCS_GIT_ADVANCED "$1"; then
|
|
"$@"
|
|
return $?
|
|
fi
|
|
|
|
local type
|
|
type="$(_vcs_get_type "$(_vcs_get_dir)")" || return
|
|
"$type" "$@"
|
|
}
|
|
########################################
|
|
function vcs_add_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Ajouter les fichiers spécifiés dans le gestionnaire de version
|
|
|
|
USAGE
|
|
uproject $1 <files...>"
|
|
}
|
|
function vcs_add() {
|
|
# le répertoire de référence est le répertoire du premier fichier ajouté
|
|
_vcs_dispatch "$(dirname "$1")" add "$@"
|
|
}
|
|
########################################
|
|
function vcs_remove_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Suppprimer les fichiers versionnés spécifiés du gestionaire de version
|
|
|
|
USAGE
|
|
uproject $1 <files...>"
|
|
}
|
|
function vcs_remove() {
|
|
# le répertoire de référence est le répertoire du premier fichier supprimé
|
|
_vcs_dispatch "$(dirname "$1")" remove "$@"
|
|
}
|
|
########################################
|
|
function vcs_copy_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Copier les fichiers versionnés spécifiés
|
|
|
|
USAGE
|
|
uproject $1 <sources...> <dest>"
|
|
}
|
|
function vcs_copy() {
|
|
# le répertoire de référence est le répertoire de destination
|
|
local last=$#
|
|
_vcs_dispatch "$(dirname "${!last}")" copy "$@"
|
|
}
|
|
########################################
|
|
function vcs_move_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Déplacer les fichiers versionnés spécifiés
|
|
|
|
USAGE
|
|
uproject $1 <sources...> <dest>"
|
|
}
|
|
function vcs_move() {
|
|
# le répertoire de référence est le répertoire de destination
|
|
local last=$#
|
|
_vcs_dispatch "$(dirname "${!last}")" move "$@"
|
|
}
|
|
########################################
|
|
function vcs_mkdir_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Créer un nouveau répertoire versionné
|
|
|
|
USAGE
|
|
uproject $1"
|
|
}
|
|
function vcs_mkdir() {
|
|
# le répertoire de référence est le répertoire du premier répertoire créé
|
|
_vcs_dispatch "$(dirname "$1")" mkdir "$@"
|
|
}
|
|
########################################
|
|
function vcs_commit_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Enregistrer les modifications sur les fichiers modifiés avec le message spécifiés
|
|
|
|
USAGE
|
|
uproject $1 <message> [files...]
|
|
|
|
Si files n'est pas spécifié, prendre tous les fichiers modifiés actuellement
|
|
|
|
OPTIONS
|
|
-a Enregistrer les modifications sur les fichiers modifiés uniquement.
|
|
-A Enregistrer les modifictions sur les nouveaux fichiers et les fichiers
|
|
modifiés.
|
|
-c Enregistrer uniquement les modifications de l'index. (si applicable)
|
|
-p Pousser les modifications sur le serveur après le commit (par défaut)
|
|
-l Garder le commit local, i.e. les modifications ne sont pas poussées sur
|
|
le serveur. (si applicable)"
|
|
}
|
|
function vcs_commit() {
|
|
_vcs_dispatch "" commit "$@"
|
|
}
|
|
########################################
|
|
function vcs_status_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Afficher l'état des fichiers versionnés et non versionnés
|
|
|
|
USAGE
|
|
uproject $1"
|
|
}
|
|
function vcs_status() {
|
|
_vcs_dispatch "" status "$@"
|
|
}
|
|
########################################
|
|
function vcs_update_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Mettre à jour le checkout local avec les modifications présentes sur le serveur
|
|
|
|
USAGE
|
|
uproject $1
|
|
|
|
OPTIONS
|
|
-x Ne pas mettre à jour les références externes"
|
|
}
|
|
function vcs_update() {
|
|
_vcs_dispatch "" update "$@"
|
|
}
|
|
########################################
|
|
function vcs_push_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Pousser les modifications locales sur le serveur
|
|
|
|
USAGE
|
|
uproject $1"
|
|
}
|
|
function vcs_push() {
|
|
_vcs_dispatch "" push "$@"
|
|
}
|
|
########################################
|
|
function vcs_diff_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: Afficher les différences
|
|
|
|
USAGE
|
|
uproject $1
|
|
|
|
OPTIONS
|
|
-l (par défaut) Afficher les différences non commitées
|
|
-c Si cela a du sens, afficher les différences en passe d'être commitées
|
|
-r REVISION
|
|
Afficher les différences depuis la révision spécifiée
|
|
-R Afficher les modifications effectuées depuis la dernière release
|
|
Pour le moment, l'option -R n'est pas implémentée"
|
|
}
|
|
function vcs_diff() {
|
|
_vcs_dispatch "" diff "$@"
|
|
}
|
|
########################################
|
|
function vcs_tag_help() {
|
|
local OENC="$UTF8"
|
|
uecho "uproject $1: donner un nom symbolique à la révision courante
|
|
|
|
USAGE
|
|
uproject $1 <name>"
|
|
}
|
|
function vcs_tag() {
|
|
_vcs_dispatch "" tag "$@"
|
|
}
|
|
########################################
|
|
# XXX Ajouter vcs_revert, vcs_resolve, uproject_grep
|
|
|
|
################################################################################
|
|
# Git
|
|
|
|
function git_getrepos() {
|
|
git config --get remote.origin.url
|
|
}
|
|
function git_geturl() {
|
|
git config --get remote.origin.url
|
|
}
|
|
function git_have_annex() {
|
|
[ -n "$(git config --get annex.uuid)" ]
|
|
}
|
|
function git_add() {
|
|
git add "$@"
|
|
}
|
|
function git_remove() {
|
|
git rm "$@"
|
|
}
|
|
function git_copy() {
|
|
git cp "$@"
|
|
}
|
|
function git_move() {
|
|
git mv "$@"
|
|
}
|
|
function git_mkdir() {
|
|
git mkdir "$@"
|
|
}
|
|
function git_commit() {
|
|
local all=auto allnew push=auto nopush args
|
|
setyesval nopush "$UTOOLS_VCS_OFFLINE"
|
|
[ -n "$nopush" ] && push=
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-a,--all all=1 \
|
|
-A,--all-new allnew=1 \
|
|
-c,--cached all= \
|
|
-p,--push push=1 \
|
|
-l,--local push= \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
if [ -n "$allnew" ]; then
|
|
git add -A
|
|
all=
|
|
fi
|
|
|
|
local message="$1"; shift
|
|
local -a cmd
|
|
cmd=(git commit)
|
|
[ -n "$message" ] && cmd=("${cmd[@]}" -m "$message")
|
|
if [ "$all" == "auto" ]; then
|
|
# Si des fichiers sont spécifiés, prendre ceux-là.
|
|
if [ -z "$*" ]; then
|
|
# Sinon, s'il y a des fichiers dans l'index, commiter uniquement ces
|
|
# fichiers
|
|
# Sinon, committer tous les fichiers modifiés
|
|
# le code suivant retourne vrai si l'index contient au moins fichier
|
|
git status --porcelain 2>/dev/null | awk '
|
|
BEGIN { ec = 1 }
|
|
substr($0, 1, 1) ~ /[^ ?]/ { ec = 0; exit }
|
|
END { exit ec }' ||
|
|
cmd=("${cmd[@]}" -a)
|
|
fi
|
|
else
|
|
[ -n "$all" ] && cmd=("${cmd[@]}" -a)
|
|
fi
|
|
|
|
if ! "${cmd[@]}" "$@"; then
|
|
[ "$push" == auto ] && return 1
|
|
fi
|
|
if [ "$push" == auto ]; then
|
|
git_push --auto || return
|
|
elif [ -n "$push" ]; then
|
|
git_push --force || return
|
|
fi
|
|
return 0
|
|
}
|
|
function git_status() {
|
|
git status "$@"
|
|
}
|
|
function git_update() {
|
|
local args autoff=1
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-x '$_vcs_unsupported -x' \
|
|
-n,--no-autoff autoff= \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
if [ -z "$autoff" ]; then
|
|
git pull "$@"
|
|
return $?
|
|
fi
|
|
|
|
local orig_branch restore_branch remote rbranch pbranch
|
|
local -a branches
|
|
|
|
git fetch -p "$@" || return
|
|
|
|
if ! git_check_cleancheckout; then
|
|
branch="$(git_get_branch)"
|
|
remote="$(git_get_branch_remote "$branch")"
|
|
rbranch="$(git_get_branch_rbranch "$branch" "$remote")"
|
|
pbranch="${rbranch#refs/remotes/}"
|
|
if git merge -q --ff-only "$rbranch"; then
|
|
echo "* There are uncommitted local changes: only CURRENT branch were updated"
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
orig_branch="$(git_get_branch)"
|
|
array_from_lines branches "$(git_list_branches)"
|
|
for branch in "${branches[@]}"; do
|
|
remote="$(git_get_branch_remote "$branch")"
|
|
rbranch="$(git_get_branch_rbranch "$branch" "$remote")"
|
|
pbranch="${rbranch#refs/remotes/}"
|
|
[ -n "$remote" -a -n "$rbranch" ] || continue
|
|
if git_is_ancestor "$branch" "$rbranch"; then
|
|
if git_should_ff "$branch" "$rbranch"; then
|
|
echo "* Fast-forwarding $branch -> $pbranch"
|
|
git checkout -q "$branch"
|
|
git merge -q --ff-only "$rbranch"
|
|
restore_branch=1
|
|
fi
|
|
else
|
|
if [ "$branch" == "$orig_branch" ]; then
|
|
echo "* Cannot fast-forward CURRENT branch $branch from $pbranch
|
|
Try to merge manually with: git merge $pbranch"
|
|
else
|
|
echo "* Cannot fast-forward local branch $branch from $pbranch
|
|
You can merge manually with: git checkout $branch; git merge $pbranch"
|
|
fi
|
|
fi
|
|
done
|
|
[ -n "$restore_branch" ] && git checkout -q "$orig_branch"
|
|
return 0
|
|
}
|
|
function git_push() {
|
|
local all all_branches all_tags auto force args no_annex
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-a,--all all=1 \
|
|
-b,--branches,--all-branches all_branches=1 \
|
|
-t,--tags,--all-tags all_tags=1 \
|
|
--auto auto=1 \
|
|
-f,--force force=1 \
|
|
-n,--no-annex no_annex=1 \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
if [ $# -gt 0 ]; then
|
|
# si des arguments sont spécifiés, les passer à git sans modification
|
|
git push "$@"
|
|
return $?
|
|
elif [ -n "$all" ]; then
|
|
# On a demandé à pusher toutes les branches et tous les tags
|
|
git push --all
|
|
git push --tags
|
|
return $?
|
|
elif [ -n "$all_branches" ]; then
|
|
# On a demandé à pusher toutes les branches
|
|
git push --all
|
|
return $?
|
|
elif [ -n "$all_tags" ]; then
|
|
# On a demandé à pusher tous les tags
|
|
git push --tags
|
|
return $?
|
|
elif git_have_annex; then
|
|
# Si une annexe existe dans le dépôt, demander à git-annex de faire la
|
|
# synchronisation, sauf si --no-annex est spécifié ou si on est en mode
|
|
# automatique
|
|
if [ -z "$no_annex" -a -z "$auto" ]; then
|
|
git annex sync
|
|
return $?
|
|
fi
|
|
fi
|
|
|
|
# sinon on push vers origin. vérifier la présence du remote
|
|
[ -n "$(git config --get remote.origin.url)" ] || {
|
|
if [ -n "$auto" ]; then
|
|
# en mode automatique, ignorer l'absence de remote
|
|
return 0
|
|
else
|
|
eerror "Aucun remote origin n'est défini"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# puis calculer la branche à pusher
|
|
local branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
|
|
local origin="$(git config --get "branch.$branch.remote")"
|
|
if [ -n "$branch" -a "$origin" == origin ]; then
|
|
if [ -n "$auto" ]; then
|
|
# en mode automatique, ne pousser que la branche courante
|
|
git push "$origin" "$branch" || return
|
|
else
|
|
# utiliser la configuration par défaut, qui est sous debian squeeze
|
|
# de pousser toutes les branches
|
|
git push || return
|
|
fi
|
|
elif [ -n "$force" ]; then
|
|
# utiliser la configuration par défaut, qui est sous debian squeeze de
|
|
# pousser toutes les branches
|
|
git push || return
|
|
fi
|
|
return 0
|
|
}
|
|
function git_diff() {
|
|
local dummy cached args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-l dummy \
|
|
-c cached \
|
|
-r: '$_vcs_unsupported -r' \
|
|
-R '$_vcs_unsupported -R' \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
git diff ${cached:+--cached} "$@"
|
|
}
|
|
function git_tag() {
|
|
_vcs_unsupported tag #XXX
|
|
}
|
|
|
|
# Fonctions avancées de git
|
|
__VCS_GIT_ADVANCED_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
|
|
)
|
|
__VCS_GIT_ADVANCED=(
|
|
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
|
|
)
|
|
function git_check_gitvcs() {
|
|
[ "$(_vcs_get_type "$(_vcs_get_dir)")" == git ]
|
|
}
|
|
function git_ensure_gitvcs() {
|
|
git_check_gitvcs || die "Ce n'est pas un dépôt git"
|
|
}
|
|
function git_list_branches() {
|
|
git for-each-ref refs/heads/ --format='%(refname:short)' | csort
|
|
}
|
|
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
|
|
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() {
|
|
git_list_branches | grep -qF "$1"
|
|
}
|
|
function git_have_rbranch() {
|
|
git_list_rbranches "${2:-origin}" | grep -qF "$1"
|
|
}
|
|
function git_get_branch() {
|
|
git rev-parse --abbrev-ref HEAD 2>/dev/null
|
|
}
|
|
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() {
|
|
local branch="$1"
|
|
[ -n "$branch" ] || branch="$(git_get_branch)"
|
|
[ -n "$branch" ] || return
|
|
git config --get "branch.$branch.merge"
|
|
}
|
|
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() {
|
|
[ "$(git_get_branch)" == "${1:-master}" ]
|
|
}
|
|
function git_have_remote() {
|
|
[ -n "$(git config --get remote.${1:-origin}.url)" ]
|
|
}
|
|
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
|
|
git push "$origin" "$branch" || return
|
|
fi
|
|
local version="$(LANG=C git version)"
|
|
version="${version#git version }"
|
|
eval "version=(${version//./ })"
|
|
if [ ${version[0]} -le 1 -a ${version[1]} -le 7 ]; then
|
|
git branch -t --set-upstream "$branch" "$origin/$branch"
|
|
else
|
|
git branch -u "$origin/$branch" "$branch"
|
|
fi
|
|
}
|
|
function git_ensure_branch() {
|
|
# retourner 0 si la branche a été créée, 1 si elle existait déjà, 2 en cas d'erreur
|
|
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 "$UTOOLS_VCS_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.
|
|
[ -z "$(git status --porcelain 2>/dev/null)" ]
|
|
}
|
|
function git_ensure_cleancheckout() {
|
|
git_check_cleancheckout || die "Vous avez des modifications locales. Enregistrez ces modifications avant de continuer"
|
|
}
|
|
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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")" ]
|
|
}
|
|
|
|
# fonctions pour git annex
|
|
function git_annex_initial() {
|
|
# sur le dépôt $1 fraichement cloné, vérifier s'il faut faire git annex
|
|
# init. Si oui, l'initialiser avec le nom d'hôte, et récupérer tous les
|
|
# fichiers annexés
|
|
# retourner 1 si une erreur s'est produite
|
|
local repodir="${1:-.}"
|
|
[ -d "$repodir" ] || return 1
|
|
repodir="$(abspath "$repodir")"
|
|
|
|
local GIT_DIR GIT_WORK_TREE
|
|
[ "$(cd "$repodir"; git rev-parse --is-bare-repository)" == false ] || return 0
|
|
[ -n "$(GIT_DIR="$repodir/.git" git config --get annex.uuid)" ] && return 0
|
|
|
|
# ici, on sait que git annex n'a pas encore été configuré
|
|
# vérifier s'il existe des fichiers annexés
|
|
local -a links
|
|
array_from_lines links "$(find "$repodir" -type l)"
|
|
local link hasannex=
|
|
for link in "${links[@]}"; do
|
|
link="$(readlink "$link")"
|
|
if [ "${link#.git/annex/}" != "$link" ]; then
|
|
hasannex=1
|
|
break
|
|
elif [[ "$link" == */.git/annex/* ]]; then
|
|
hasannex=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -n "$hasannex" ]; then
|
|
progexists git-annex || {
|
|
eerror "Vous devez installer git-annex"
|
|
return 1
|
|
}
|
|
(cd "$repodir"
|
|
git annex init "$MYHOSTNAME" &&
|
|
git annex get &&
|
|
git annex sync
|
|
) || return 1
|
|
fi
|
|
}
|
|
|
|
################################################################################
|
|
# Subversion
|
|
|
|
function svn_getrepos() {
|
|
LANG=C svn info "${@:-.}" | awk '/^Repository Root: / { print substr($0, 18) }'
|
|
}
|
|
function svn_geturl() {
|
|
LANG=C svn info "${@:-.}" | awk '/^URL: / { print substr($0, 6) }'
|
|
}
|
|
function svn_add() {
|
|
svn add "$@"
|
|
}
|
|
function svn_remove() {
|
|
svn delete "$@"
|
|
}
|
|
function svn_copy() {
|
|
svn copy --parents "$@"
|
|
}
|
|
function svn_move() {
|
|
svn move --parents "$@"
|
|
}
|
|
function svn_mkdir() {
|
|
svn mkdir --parents "$@"
|
|
}
|
|
function svn_commit() {
|
|
local dummy args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-a,--all dummy \
|
|
-A,--all-new '$_vcs_unsupported -A' \
|
|
-c,--cached '$_vcs_unsupported -c' \
|
|
-p,--push dummy \
|
|
-l,--local '$_vcs_unsupported -l' \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
local message="$1"; shift
|
|
local -a cmd
|
|
cmd=(svn commit)
|
|
[ -n "$message" ] && cmd=("${cmd[@]}" -m "$message")
|
|
cmd=("${cmd[@]}" "${@:-.}")
|
|
"${cmd[@]}"
|
|
}
|
|
function svn_status() {
|
|
svn status "$@"
|
|
}
|
|
function svn_update() {
|
|
local ignore_externals args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-x ignore_externals \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
svn update ${ignore_externals:+--ignore-externals} "$@"
|
|
}
|
|
function svn_push() {
|
|
local args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
}
|
|
function svn_diff() {
|
|
local dummy revision args tmpfile0 tmpfile
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-l dummy \
|
|
-r: revision \
|
|
-c '$_vcs_unsupported -c' \
|
|
-R '$_vcs_unsupported -R' \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
ac_set_tmpfile tmpfile0
|
|
ac_set_tmpfile tmpfile
|
|
LANG=C svn status 2>/dev/null | awk '/^$/ { next } /^(M|X|Performing status on external item at)/ { next } { print }' >"$tmpfile0"
|
|
if [ -s "$tmpfile0" ]; then
|
|
echo "Fichiers non modifiés dans un état spécial:" >>"$tmpfile"
|
|
cat "$tmpfile0" >>"$tmpfile"
|
|
echo "===================================================================" >>"$tmpfile"
|
|
fi
|
|
ac_clean "$tmpfile0"
|
|
|
|
svn diff -x -u ${revision:+-r "$revision"} "$@" >>"$tmpfile"
|
|
|
|
if [ -s "$tmpfile" ]; then
|
|
"${PAGER:-less}" "$tmpfile"
|
|
ac_clean "$tmpfile"
|
|
return 0
|
|
else
|
|
einfo "Aucune différence détectée"
|
|
ac_clean "$tmpfile"
|
|
return 1
|
|
fi
|
|
}
|
|
function svn_tag() {
|
|
_vcs_unsupported tag #XXX
|
|
}
|
|
|
|
################################################################################
|
|
# CVS
|
|
|
|
function cvs_getrepos() {
|
|
_vcs_unsupported getrepos
|
|
}
|
|
function cvs_geturl() {
|
|
_vcs_unsupported geturl
|
|
}
|
|
function cvs_add() {
|
|
cvs add "$@"
|
|
}
|
|
function cvs_remove() {
|
|
rm "$@"
|
|
cvs remove "$@"
|
|
}
|
|
|
|
function cvs_copy() {
|
|
_vcs_check_nbargs $# 2 2 || return
|
|
local src="$1" dest="$2"
|
|
if [ -d "$dest" ]; then
|
|
# copie d'un fichier vers un répertoire
|
|
cp "$src" "$dest" || return
|
|
cvs add "$dest/$(basename "$src")"
|
|
elif [ -f "$src" ]; then
|
|
# copie d'un fichier vers un fichier
|
|
cp "$src" "$dest" || return
|
|
cvs add "$dest"
|
|
else
|
|
_vcs_invalid_copy
|
|
return 1
|
|
fi
|
|
}
|
|
function cvs_move() {
|
|
_vcs_check_nbargs $# 2 2 || return
|
|
local src="$1" dest="$2"
|
|
if [ -d "$dest" ]; then
|
|
# copie d'un fichier vers un répertoire
|
|
mv "$src" "$dest" || return
|
|
cvs remove "$src"
|
|
cvs add "$dest/$(basename "$src")"
|
|
elif [ -f "$src" ]; then
|
|
# copie d'un fichier vers un fichier
|
|
mv "$src" "$dest" || return
|
|
cvs remove "$src"
|
|
cvs add "$dest"
|
|
else
|
|
_vcs_invalid_copy
|
|
return 1
|
|
fi
|
|
}
|
|
function cvs_mkdir() {
|
|
mkdir -p "$@"
|
|
cvs add "$@"
|
|
}
|
|
function cvs_commit() {
|
|
local dummy args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-a,--all dummy \
|
|
-A,--all-new '$_vcs_unsupported -A' \
|
|
-c,--cached '$_vcs_unsupported -c' \
|
|
-p,--push dummy \
|
|
-l,--local '$_vcs_unsupported -l' \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
local message="$1"; shift
|
|
local -a cmd
|
|
cmd=(cvs commit)
|
|
[ -n "$message" ] && cmd=("${cmd[@]}" -m "$message")
|
|
cmd=("${cmd[@]}" "${@:-.}")
|
|
"${cmd[@]}"
|
|
}
|
|
function cvs_status() {
|
|
cvs update "$@"
|
|
}
|
|
function cvs_update() {
|
|
local dummy args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-x dummy \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
|
|
cvs update "$@"
|
|
}
|
|
function cvs_push() {
|
|
local args
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
}
|
|
function cvs_diff() {
|
|
local dummy revision args tmpfile
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
-l dummy \
|
|
-r: revision \
|
|
-c '$_vcs_unsupported -c' \
|
|
-R '$_vcs_unsupported -R' \
|
|
@ args -- "$@" && set -- "${args[@]}" || {
|
|
eerror "$args"
|
|
return 1
|
|
}
|
|
ac_set_tmpfile tmpfile
|
|
cvs diff -u ${revision:+-r "$revision"} "$@" >"$tmpfile"
|
|
|
|
if [ -s "$tmpfile" ]; then
|
|
"${PAGER:-less}" "$tmpfile"
|
|
ac_clean "$tmpfile"
|
|
return 0
|
|
else
|
|
ac_clean "$tmpfile"
|
|
einfo "Aucune différence détectée"
|
|
return 1
|
|
fi
|
|
}
|
|
function cvs_tag() {
|
|
local cwd rootdir
|
|
cwd="$(pwd)"
|
|
rootdir="$(__vcs_find_root "$cwd")"
|
|
cvs tag "$1" "$(relpath "$rootdir" "$cwd")"
|
|
}
|