539 lines
18 KiB
Bash
Executable File
539 lines
18 KiB
Bash
Executable File
#!/bin/bash
|
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
function display_help() {
|
|
uecho "$scriptname: Outil pour gérer des projets
|
|
|
|
USAGE
|
|
$scriptname cmd [args]
|
|
|
|
COMMANDS
|
|
getvcs [dir]
|
|
Afficher le type de VCS pour dir.
|
|
getroot [dir]
|
|
Si dir est un répertoire versionné, retourner le répertoire racine du
|
|
projet versionné.
|
|
getrepos [dir]
|
|
Si dir est un répertoire versionné, retourner l'url du repository du
|
|
projet versionné.
|
|
geturl [dir]
|
|
Si dir est un répertoire versionné, retourner son url dans le
|
|
repository.
|
|
fold [dir]
|
|
unfold [dir]
|
|
Utiliser uinc pour défaire (resp. refaire) toutes les inclusions des
|
|
fichiers de dir. Cela nécessite qu'un fichier .udir soit configuré à la
|
|
racine du projet avec uinc=true
|
|
vcs [args]
|
|
Appeler le gestionnaire de gestion approprié avec les arguments donnés.
|
|
add files...
|
|
Ajouter les fichiers files dans le gestionnaire de version.
|
|
remove files...
|
|
Supprimer les fichiers versionnés files.
|
|
copy from to
|
|
Copier le fichier versionné from vers le fichier to.
|
|
move from to
|
|
Renommer le fichier versionné from vers le fichier to.
|
|
mkdir dir
|
|
Créer un nouveau répertoire versionné.
|
|
commit message [files...]
|
|
Enregistrer les modifications (par défaut sur tous les fichiers
|
|
modifiés) avec le commentaire message.
|
|
status
|
|
Afficher l'état des fichiers versionnés et non versionnés.
|
|
update [-x]
|
|
Mettre à jour la copie locale avec la copie sur le serveur.
|
|
-x Ne pas mettre à jour les références externes (uniquement pour svn)
|
|
-c, --clean-before
|
|
Avant de faire git pull, supprimer toutes les branches locales qui
|
|
sont à jour par rapport à l'origine. La copie de travail doit être
|
|
propre. Basculer sur la branche develop avant de supprimer les
|
|
branches. S'il n'y a pas de branche develop, c'est un NOP.
|
|
-n, --no-autoff
|
|
Ne pas faire de fast-forward automatique pour toutes les branches
|
|
traquées. Par défaut, s'il n'y a pas de modifications locales,
|
|
essayer de fast-fowarder toutes les branches locales traquées.
|
|
diff [options]
|
|
Afficher les différences.
|
|
-l Afficher les différences non commitées (par défaut)
|
|
-c Afficher les différences en passe d'être commitées (si appliquable)
|
|
-r REV
|
|
Afficher les différences depuis la révision REV.
|
|
-R Afficher les modifications effectuées depuis la dernière release.
|
|
|
|
clone git@host:path/to/repo [destdir]
|
|
clone http://host/gituser/path/to/repo [destdir]
|
|
Cloner un dépôt distant. Basculer sur la branche develop si elle existe.
|
|
Initialiser git annex si le dépôt contient des fichiers annexés.
|
|
Récupérer aussi ces fichiers avec 'git annex get'
|
|
-n, --no-clone
|
|
Ne pas cloner, afficher simplement ce qui serait fait
|
|
-u, --update
|
|
Si le dépôt a déjà été cloné, le mettre à jour avec git pull
|
|
-m, --master
|
|
Ne pas chercher à basculer sur la branche develop, même si elle
|
|
existe.
|
|
-r, --recursive
|
|
Cloner récursivement tous les dépôt à partir du chemin spécifié
|
|
depuis un serveur gitolite. La signature est alors sensiblement
|
|
différente:
|
|
clone -r git@host:basepath [destdir]
|
|
clone -r http://host/gituser/basepath [destdir]
|
|
L'arborescence en dessous de basepath est recréée à partir de
|
|
destdir, e.g le dépôt git@host:basepath/to/repo est cloné dans
|
|
destdir/to/repo
|
|
|
|
crone git@host:path/to/repo [destdir]
|
|
crone http://host/gituser/path/to/repo [destdir]
|
|
Créer un dépôt sur un serveur gitolite, puis le cloner. La commande
|
|
'create' doit avoir été activée sur ce serveur.
|
|
|
|
develop
|
|
release
|
|
hotfix
|
|
Démarrer le travail sur une branche respectivement de développement, de
|
|
release, ou de correction de bugs. Lancer chaque commande avec --help
|
|
pour les détails. Nécessite git.
|
|
|
|
archive
|
|
Créer une archive du projet courant. Nécessite git.
|
|
|
|
annex [args]
|
|
Lancer git annex avec les arguments spécifiés.
|
|
xadd
|
|
xunlock
|
|
xdrop
|
|
xwhereis
|
|
xwebapp
|
|
Chacune de ces commandes est un raccourci vers la commande
|
|
correspondante de git annex, sans le préfixe 'x'
|
|
xsync
|
|
Sur un dépot où git-annex est activé, lancer 'git annex sync' si on est
|
|
en mode indirect ou 'git annex sync --content' si on est en mode direct.
|
|
Sur un dépôt où git-annex n'est pas activé, faire l'équivalent des
|
|
commandes 'git add -A && git commit && git pull && git push'
|
|
xcopy
|
|
xmove
|
|
xget
|
|
Comme ci-dessus, mais si la commande s'exécute sans erreur, lancer
|
|
aussi 'git annex sync'
|
|
xinitial
|
|
Sur un dépôt fraichement cloné, initialiser le dépôt avec 'annex init'
|
|
s'il contient des fichiers annexés. Récupérer aussi ces fichiers avec
|
|
'annex get'
|
|
xconfig-export [dir]
|
|
Installer des hooks pour qu'un dépôt puisse être utilisé pour servir des
|
|
fichiers, par exemple avec un serveur web. Plus précisément, un hook
|
|
post-receive est créé avec la commande 'git annex merge', et un hook
|
|
post-update est créé avec la commande 'git update-server-info'
|
|
|
|
printml [-t TYPE]
|
|
Afficher le modeline pour un fichier du type spécifié
|
|
addml [-t TYPE] file
|
|
Ajouter un modele pour le fichier spécifié, s'il n'en a pas déjà un.
|
|
Si nécessaire, forcer le type du fichier au lieu de l'autodétecter
|
|
new [options] file [template options]
|
|
Créer un nouveau fichier à partir d'un modèle.
|
|
Avant le nom du fichier, les options suivantes sont valides:
|
|
-t TEMPLATE
|
|
Spécifier le modèle de fichier à utiliser. Par défaut, le modèle
|
|
à utiliser est déduit de l'extension ou du nom du fichier.
|
|
-e Editer le fichier après l'avoir créé.
|
|
Après le nom du fichier, toutes les options sont spécifiques au modèle
|
|
utilisé pour créer le nouveau fichier. Utiliser l'option --help pour
|
|
avoir une description des options disponibles."
|
|
}
|
|
|
|
SCRIPT_ALIASES=(
|
|
pv:vcs
|
|
pa:add prm:remove pcp:copy pmv:move pmd:mkdir
|
|
pci:commit pu:update pp:push pdiff:diff
|
|
pclone:clone
|
|
pcrone:crone
|
|
pxx:annex
|
|
pxa:xadd pxu:xunlock pxc:xcopy pxd:xdrop pxm:xmove
|
|
pxg:xget pxs:xsync pxw:xwhereis pxwa:xwebapp
|
|
pxinitial:xinitial
|
|
pnew:new
|
|
pgr:grep
|
|
paddml:addml
|
|
)
|
|
CMD_ALIASES=(
|
|
getrepo:getrepos repo:getrepos repos:getrepos
|
|
url:geturl
|
|
a:add
|
|
rm:remove del:remove delete:remove
|
|
cp:copy
|
|
mv:move ren:move rename:move
|
|
md:mkdir
|
|
ci:commit
|
|
s:status st:status
|
|
u:update upd:update
|
|
p:push
|
|
version:pver ver:pver
|
|
develop:pdev dev:pdev release:prel rel:prel hotfix:pfix fix:pfix
|
|
archive:pz arch:pz
|
|
xx:annex
|
|
xa:xadd
|
|
xu:xunlock
|
|
xc:xcopy
|
|
xd:xdrop
|
|
xm:xmove
|
|
xg:xget
|
|
xs:xsync
|
|
xw:xwhereis
|
|
xwa:xwebapp
|
|
xce:xconfig-export
|
|
gr:grep
|
|
)
|
|
DEFAULT_CMD=status
|
|
PY_CMDS=(new)
|
|
VCS_CMDS=(getvcs getroot getrepos geturl vcs add remove copy move mkdir commit status update push diff tag)
|
|
SH_CMDS=(pver pdev prel pfix pz)
|
|
GITANNEX_CMDS=(annex xadd xunlock xcopy xdrop xmove xget xsync xwhereis xwebapp xinitial)
|
|
ML_CMDS=(printml addml)
|
|
|
|
if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
|
|
# créer les liens
|
|
scriptname="$(basename "$0")"
|
|
for alias in p "${SCRIPT_ALIASES[@]}"; do
|
|
alias="${alias%:*}"
|
|
ln -s "$scriptname" "$alias"
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
|
|
urequire DEFAULTS modeline vcs
|
|
|
|
# Traduire le nom du script
|
|
for script_alias in "${SCRIPT_ALIASES[@]}"; do
|
|
splitpair "$script_alias" src dest
|
|
if [ "$scriptname" == "$src" ]; then
|
|
eval "set -- $dest \"\$@\""
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Parser les options de uproject
|
|
parse_opts + "${PRETTYOPTS[@]}" \
|
|
--help '$exit_with display_help' \
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
# Traduire la commande
|
|
[ -n "$*" ] || set -- "$DEFAULT_CMD"
|
|
CMD=
|
|
found_cmd=
|
|
while [ -z "$found_cmd" ]; do
|
|
CMD="$1"; shift; found_cmd=1
|
|
[ -n "$CMD" ] || break
|
|
|
|
for cmd_alias in "${CMD_ALIASES[@]}"; do
|
|
splitpair "$cmd_alias" src dest
|
|
if [ "$CMD" == "$src" ]; then
|
|
eval "set -- $dest \"\$@\""
|
|
found_cmd=
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
|
|
################################################################################
|
|
# Traiter les commandes
|
|
|
|
if [ "$CMD" == "grep" ]; then
|
|
## grep
|
|
if [ $# -eq 1 -a "$1" == "--help" ]; then
|
|
uecho "uproject grep: Lancer une recherche récursive en ignorant les répertoire de controle de version
|
|
|
|
USAGE
|
|
uproject grep [grep options]"
|
|
exit 0
|
|
fi
|
|
EXCLUDES=(--exclude-dir .svn --exclude-dir CVS --exclude-dir .git --exclude "*.pyc")
|
|
exec grep -r "${EXCLUDES[@]}" "$@"
|
|
|
|
elif array_contains SH_CMDS "$CMD"; then
|
|
exec "$scriptdir/$CMD" "$@"
|
|
|
|
elif array_contains ML_CMDS "$CMD"; then
|
|
"$CMD" "$@"
|
|
|
|
elif array_contains VCS_CMDS "$CMD"; then
|
|
"vcs_$CMD" "$@"
|
|
|
|
elif array_contains GITANNEX_CMDS "$CMD"; then
|
|
function xsync() {
|
|
if ! git_have_annex; then
|
|
setyesval offline "$UTOOLS_VCS_OFFLINE"
|
|
if git_commit -Al "Maj des fichiers"; then
|
|
[ -n "$offline" ] && return 0
|
|
git_have_remote || return 0
|
|
git pull && git_push
|
|
fi
|
|
elif is_yes "$(git config --get annex.direct)"; then
|
|
git annex add &&
|
|
git annex sync &&
|
|
git annex sync --content &&
|
|
git annex sync
|
|
else
|
|
git annex sync
|
|
fi
|
|
}
|
|
git_annex_use_ssh_wrapper
|
|
case "$CMD" in
|
|
annex) git annex "$@";;
|
|
xsync) xsync;;
|
|
xcopy|xmove|xget) git annex "${CMD#x}" "$@" && git annex sync;;
|
|
xinitial) git_annex_initial "$@";;
|
|
*) git annex "${CMD#x}" "$@";;
|
|
esac
|
|
|
|
elif [ "$CMD" == clone ]; then
|
|
git_annex_use_ssh_wrapper
|
|
function pclone() {
|
|
estep "$1 --> $(ppath "$2")"
|
|
mkdirof "$2" || return 1
|
|
git clone "$1" "$2" || return 1
|
|
if [ -z "$3" ]; then
|
|
(
|
|
cd "$2"
|
|
if git_have_rbranch develop; then
|
|
git checkout develop || exit 1
|
|
fi
|
|
) || return 1
|
|
fi
|
|
git_annex_initial "$2" || return 1
|
|
}
|
|
function gitolite_info() {
|
|
local mode="$1" urlbase="$2" pattern="$3"
|
|
case "$mode" in
|
|
http) curl -fs "$urlbase/info${pattern:+"?$pattern"}";;
|
|
ssh) ssh -q "$urlbase" info ${pattern:+"$pattern"} 2>/dev/null;;
|
|
esac
|
|
}
|
|
function filter_repos() {
|
|
awkrun prefix="$1" '
|
|
NR <= 2 { next }
|
|
{
|
|
# filtrer les projets qui ne sont pas encore créés
|
|
if (substr($0, 5, 2) == " C") next
|
|
repo = substr($0, 6)
|
|
# filtrer les projets de type wildcard
|
|
if (repo ~ /[\[\]\*]/) next
|
|
# enlever le prefixe
|
|
if (prefix != "" && substr(repo, 1, length(prefix)) != prefix) next
|
|
print repo
|
|
}
|
|
'
|
|
}
|
|
|
|
no_clone=
|
|
update=
|
|
nodevelop=
|
|
recursive=
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
|
-n,--no-clone no_clone=1 \
|
|
-u,--update update=1 \
|
|
-m,--master nodevelop=1 \
|
|
-r,--recursive recursive=1 \
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
if [ -n "$recursive" ]; then
|
|
repobase="$1"
|
|
[ -n "$repobase" ] || die "Vous devez spécifier l'url de base des dépôts à cloner"
|
|
if [ "${repobase#http://}" != "$repobase" -o "${repobase#https://}" != "$repobase" ]; then
|
|
# accès par http
|
|
mode=http
|
|
splitfsep "$repobase" :// scheme hostuserpath
|
|
splitfsep "$hostuserpath" / host userpath
|
|
splitfsep "$userpath" / user basepath
|
|
[ -n "$host" -a -n "$user" ] || die "Vous devez spécifier l'hôte e.g http://host/git/basepath"
|
|
urlbase="$scheme://$host/$user"
|
|
else
|
|
# accès par ssh
|
|
mode=ssh
|
|
splitfsep "$repobase" : userhost basepath
|
|
splituserhost "$userhost" user host
|
|
[ -n "$user" ] || user=git
|
|
[ -n "$host" ] || die "Vous devez spécifier l'hôte"
|
|
urlbase="$user@$host"
|
|
fi
|
|
basepath="${basepath%/}"
|
|
destbase="${2:-.}"
|
|
|
|
git_annex_use_ssh_wrapper
|
|
prefix="${basepath:+$basepath/}"
|
|
array_from_lines repos "$(set -o pipefail; gitolite_info "$mode" "$urlbase" "$prefix" | filter_repos "$prefix")" || die
|
|
for repo in "${repos[@]}"; do
|
|
case "$mode" in
|
|
http) repourl="$urlbase/$repo";;
|
|
ssh) repourl="$urlbase:$repo";;
|
|
esac
|
|
setx destdir=abspath "$destbase/${repo#$prefix}"
|
|
if [ -d "$destdir" ]; then
|
|
if [ -n "$update" ]; then
|
|
(
|
|
${no_clone:+qvals} cd "$destdir"
|
|
${no_clone:+qvals} git pull
|
|
) || die
|
|
else
|
|
estepe "$(ppath2 "$destdir"): répertoire existant"
|
|
fi
|
|
elif [ -n "$no_clone" ]; then
|
|
qvals git clone "$repourl" "$destdir"
|
|
else
|
|
pclone "$repourl" "$destdir" "$nodevelop" || die
|
|
fi
|
|
done
|
|
|
|
else
|
|
repourl="${1%.git}"
|
|
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt git"
|
|
|
|
destdir="$2"
|
|
if [ -z "$destdir" ]; then
|
|
splitfsep "$repourl" : userhost path
|
|
setx destdir=basename -- "$path"
|
|
destdir="${destdir%.git}"
|
|
fi
|
|
setx destdir=abspath "$destdir"
|
|
|
|
git_annex_use_ssh_wrapper
|
|
if [ -d "$destdir" ]; then
|
|
if [ -n "$update" ]; then
|
|
(
|
|
${no_clone:+qvals} cd "$destdir"
|
|
${no_clone:+qvals} git pull
|
|
) || die
|
|
else
|
|
estepe "$(ppath2 "$destdir"): répertoire existant"
|
|
fi
|
|
elif [ -n "$no_clone" ]; then
|
|
qvals git clone "$repourl" "$destdir"
|
|
else
|
|
pclone "$repourl" "$destdir" "$nodevelop" || die
|
|
fi
|
|
fi
|
|
|
|
elif [ "$CMD" == crone ]; then
|
|
repourl="${1%.git}"
|
|
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt git"
|
|
if [ "${repourl#http://}" != "$repourl" -o "${repourl#https://}" != "$repourl" ]; then
|
|
# accès par http
|
|
mode=http
|
|
splitfsep "$repourl" :// scheme hostuserpath
|
|
splitfsep "$hostuserpath" / host userpath
|
|
splitfsep "$userpath" / user path
|
|
[ -n "$host" -a -n "$user" ] || die "Vous devez spécifier l'hôte e.g http://host/git/repo"
|
|
hostuser="$scheme://$host/$user"
|
|
else
|
|
# accès par ssh
|
|
mode=ssh
|
|
splitfsep "$repourl" : userhost path
|
|
splituserhost "$userhost" user host
|
|
[ -n "$user" ] || user=git
|
|
[ -n "$host" ] || die "Vous devez spécifier l'hôte"
|
|
userhost="$user@$host"
|
|
fi
|
|
[ -n "$path" ] || die "Vous devez spécifier le chemin du dépôt git"
|
|
|
|
destdir="$2"
|
|
if [ -z "$destdir" ]; then
|
|
setx destdir=basename -- "$path"
|
|
destdir="${destdir%.git}"
|
|
fi
|
|
tmpdestdir=
|
|
if [ -d "$destdir" ]; then
|
|
[ -d "$destdir/.git" ] && die "$(ppath2 "$destdir"): un dépôt existe déjà"
|
|
ac_set_tmpdir tmpdestdir
|
|
fi
|
|
|
|
if [ "$mode" == http ]; then
|
|
setx result=curl -fs "$hostuser/create?$path" || die
|
|
echo "$result"
|
|
[[ "$result" == FATAL:* ]] && die
|
|
if [ -n "$tmpdestdir" ]; then
|
|
setxx destname=abspath "$destdir" // basename
|
|
git clone "$hostuser/$path" "$tmpdestdir/$destname" || die
|
|
mv "$tmpdestdir/$destname/.git" "$destdir" || die
|
|
ac_clean "$tmpdestdir"
|
|
else
|
|
git clone "$hostuser/$path" "$destdir" || die
|
|
fi
|
|
elif [ "$mode" == ssh ]; then
|
|
git_annex_use_ssh_wrapper
|
|
ssh "$userhost" create "$path" || die
|
|
if [ -n "$tmpdestdir" ]; then
|
|
setxx destname=abspath "$destdir" // basename
|
|
git clone "$userhost:$path" "$tmpdestdir/$destname" || die
|
|
mv "$tmpdestdir/$destname/.git" "$destdir" || die
|
|
ac_clean "$tmpdestdir"
|
|
else
|
|
git clone "$userhost:$path" "$destdir" || die
|
|
fi
|
|
else
|
|
die "bug: mode non prévu"
|
|
fi
|
|
if ask_yesno "Voulez-vous créer des fichiers .gitignore et .gitattributes initiaux?" O; then
|
|
echo >"$destdir/.gitignore" "\
|
|
.~lock*#
|
|
.*.swp"
|
|
echo >"$destdir/.gitattributes" "\
|
|
*.zip -delta
|
|
*.gz -delta
|
|
*.bz2 -delta
|
|
*.whl -delta
|
|
*.exe -delta"
|
|
fi
|
|
|
|
elif [ "$CMD" == xconfig-export ]; then
|
|
unset GIT_DIR; unset GIT_WORK_TREE
|
|
dir="${1:-.}"
|
|
[ -d "$dir" ] || die "$dir: répertoire introuvable"
|
|
setx dir=abspath "$dir"
|
|
setx repodir=ppath "$dir"
|
|
cd "$dir"
|
|
|
|
git rev-parse 2>/dev/null || die "$repodir: n'est pas un dépôt git"
|
|
[ -n "$(git config --get annex.uuid)" ] || die "$repodir: n'est pas un dépôt git-annex"
|
|
cd "$(__vcs_find_root "$dir")"
|
|
[ -d .git ] || die "$repodir: est un dépôt nu"
|
|
|
|
prhook=.git/hooks/post-receive
|
|
prscript='if [ -n "$GIT_DIR" ]; then cd "$GIT_DIR"; cd ..; unset GIT_DIR; fi
|
|
git annex merge'
|
|
puhook=.git/hooks/post-update
|
|
puscript='git update-server-info'
|
|
if [ -f "$prhook" ]; then
|
|
ewarn "Le fichier $prhook existe déjà dans $repodir
|
|
Vérifiez qu'il contient les commandes suivantes:
|
|
--------8<--------
|
|
$prscript
|
|
--------8<--------"
|
|
else
|
|
estep "post-receive"
|
|
echo "#!/bin/bash
|
|
$prscript" >"$prhook"
|
|
chmod +x "$prhook"
|
|
fi
|
|
if [ -f "$puhook" ]; then
|
|
ewarn "Le fichier $puhook existe déjà dans $repodir
|
|
Vérifiez qu'il contient les commandes suivantes:
|
|
--------8<--------
|
|
$puscript
|
|
--------8<--------"
|
|
else
|
|
estep "post-update"
|
|
echo "#!/bin/bash
|
|
$puscript" >"$puhook"
|
|
chmod +x "$puhook"
|
|
fi
|
|
|
|
elif array_contains PY_CMDS "$CMD"; then
|
|
exec "$scriptdir/lib/pywrapper" uproject.py "$CMD" "$@"
|
|
|
|
else
|
|
die "$CMD: commande inconnue"
|
|
fi
|