nutools/prel

504 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
if [ $# -eq 1 -a "$1" == --nutools-completion ]; then
echo '
function __prel_completion() {
local cur
_get_comp_words_by_ref cur
COMPREPLY=($(compgen -W "$(__prel_branches)" "$cur"))
}
complete -F __prel_completion prel
'
exit 0
fi
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS ptools
# XXX Ajouter une option pour fusionner les modifications d'une branche de
# pré-release dans develop
function display_help() {
uecho "$scriptname: basculer sur une branche de release
USAGE
$scriptname -u [SOURCE]
$scriptname -c [RELEASE [SOURCE]]
$scriptname -m|-l|-d [RELEASE]
- Vérifier s'il n'y a pas de modifications locales. Sinon, proposer de faire un
commit ou un stash.
- Avec l'option -c, s'il existe une branche de release, proposer de basculer
vers elle ou sur la branche master. Sinon, basculer sur la branche master.
- Avec l'option -u, proposer ou fixer une branche de release à créer. Si elle
existe déjà, basculer vers elle. Sinon, la créer en la basant sur SOURCE, qui
vaut par défaut develop
OPTIONS
-C, --projdir PROJDIR
Spécifier le répertoire de base du projet qui est dans git. Par défaut,
on travaille dans le répertoire courant et on laisse git trouver le
répertoire de base du projet. Avec cette option, le répertoire courant
est modifié avant de lancer les commandes git.
-O, --origin ORIGIN
Spécifier le nom de l'origine. Par défaut, utiliser 'origin'
-o, --offline
En cas de création d'une branche, ne pas pousser vers l'origine; ne pas
tenter le cas échéant de traquer la branche dans l'origine; ne pas
supprimer une branche dans l'origine. Cette option est automatiquement
activée si la variable UTOOLS_VCS_OFFLINE est définie.
--online
Annuler l'effet de la variable UTOOLS_VCS_OFFLINE: forcer le mode online
-c, --checkout
Basculer vers une branche de release existante. C'est l'option par
défaut. Si aucune branche de release n'existe, basculer vers master
-u, --update
Préparer une nouvelle release. Utiliser une des options -x, -z ou -p
pour spécifier le type de release à préparer. Si la branche qui serait
créée pour le type de release existe déjà, basculer vers cette branche.
S'il faut la créer, la baser sur la branche SOURCE, qui vaut par défaut
develop
--menu
-x, --major
-z, --minor
-p, --patchlevel
Utilisé avec l'option -u, soit afficher un menu pour choisir la version
de la nouvelle release (par défaut), soit préparer respectivement une
release majeure, mineure, ou pour correction de bug.
-v-OPT
Avec l'option -u, spécifier une option de pver permettant de choisir la
version de la nouvelle release. Les options supportées sont -v, -l, -a,
-b, -r et -R. Par exemple, si la version actuelle sur la branche master
est 0.2.3, les options '-uz -v-lbeta' permettent de préparer la release
0.3.0-beta
En principe, cette option n'a pas à être utilisée, puisque dans une
branche de release, on peut faire vivre les versions de pré-release
jusqu'à la release finale. Ainsi, la branche de release est nommée
d'après la version finale, mais le projet peut recevoir une version de
pré-release incrémentale.
-w, --write
Si une nouvelle branche est créée avec -u, mettre à jour le fichier
VERSION.txt avec pver. C'est l'option par défaut.
-n, --no-write
Si une nouvelle branche est créée avec -u, NE PAS mettre à jour le
fichier VERSION.txt avec pver. Utiliser cette option si la mise à jour
du numéro de version doit être faite d'une manière particulière.
-e, --edit
Editer le fichier CHANGES.txt autogénéré par -u -w
Cette option est surtout utile si -m est utilisé avec -u, pour donner la
possibilité de corriger la liste des modifications avant leur
enregistrement définitif.
-m, --merge
Si la branche actuelle est une branche de release, ou s'il existe une
branche de release, la merger dans master, puis dans develop, puis la
supprimer. Puis basculer sur la branche master.
S'il n'existe pas de branche de release, proposer de fusionner les
modifications de la branche develop dans la branche master, sans
préparer de branche de release au préalable.
--delete
Supprimer une branche de release, à condition qu'elle aie déjà été
entièrement fusionnée dans la branch master
--force-delete
Supprimer une branche de release, même si elle n'a pas encore été
fusionnée dans la branche master
-s, --summary
Afficher la liste des différences entre la branche develop et la branche
master, comme elle serait générée par les options -u -w pour le fichier
CHANGES.txt
-l, --log
Afficher les modifications actuellement effectuée dans la branche de
release par rapport à develop.
-d, --diff
Afficher les modifications actuellement effectuée dans la branche de
release par rapport à develop, sous forme de diff."
}
projdir=
origin=origin
action=auto
checkout=
update=
incversion=
pver_opts=()
write=1
edit_changes=
merge=
force_delete=
log=
diff=
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-C:,--projdir: projdir= \
-O:,--origin: origin= \
-o,--offline UTOOLS_VCS_OFFLINE=1 \
--online UTOOLS_VCS_OFFLINE= \
-c,--checkout checkout=1 \
-u,--update '$update=1; [ -z "$incversion" ] && incversion=auto' \
--menu '$update=1; incversion=menu' \
-x,--major '$update=1; incversion=major' \
-z,--minor '$update=1; incversion=minor' \
-p,--patchlevel '$update=1; incversion=patchlevel' \
-v: '$update=1; add@ pver_opts' \
-w,--write write=1 \
-n,--no-write write= \
-e,--edit edit_changes=1 \
-m,--merge merge=1 \
--delete action=delete \
--force-delete '$action=delete; force_delete=1' \
-s,--summary action=summary \
-l,--log '$action=diff; log=1' \
-d,--diff '$action=diff; diff=1' \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
if [ "$action" == auto ]; then
if [ -n "$update" ]; then
action=update
elif [ -n "$merge" ]; then
action=merge
elif [ -n "$checkout" ]; then
action=checkout
else
# checkout par défaut
action=checkout
fi
fi
if [ -n "$projdir" ]; then
cd "$projdir" || die
fi
git_ensure_gitvcs
push_branches=()
push_tags=()
push_deferred=
[ -n "$UTOOLS_VCS_OFFLINE" ] && push_deferred= || push_deferred=1
[ -n "$update" -a -n "$merge" ] && UTOOLS_VCS_OFFLINE=1
setx branch=git_get_branch
if [ "$action" == update ]; then
setx oldver=pver -g ""
newver=
if [ "$incversion" == auto ]; then
if [ ${#pver_opts[*]} -gt 0 ]; then
# des options ont été spécifiées, les honorer
setx newver=pver -s "$oldver" "${pver_opts[@]}"
release="release-$newver"
else
# sinon, prendre une décision en fonction des branches de release
# qui existent déjà
setx -a branches=list_release_branches "$origin"
if [ ${#branches[*]} -eq 0 ]; then
# en l'absence de branche de release, proposer d'en créer une
incversion=menu
elif [ ${#branches[*]} -eq 1 ]; then
# s'il n'y en a qu'une, la prendre
release="${branches[0]}"
else
# sinon, donner le choix dans un menu
array_add branches master
default_branch="$branch"
array_contains branches "$default_branch" || default_branch="${branches[0]}"
simple_menu release branches -d "$default_branch" \
-t "Basculer vers une branche de release" \
-m "Veuillez choisir la branche vers laquelle basculer"
fi
fi
fi
case "$incversion" in
menu)
setx majorv=pver -s "$oldver" -ux "${pver_opts[@]}"
setx minorv=pver -s "$oldver" -uz "${pver_opts[@]}"
setx patchlevelv=pver -s "$oldver" -up "${pver_opts[@]}"
release="release-$minorv"
branches=("release-$majorv" "release-$minorv" "release-$patchlevelv" master)
simple_menu release branches \
-t "Basculer vers une nouvelle branche de release" \
-m "Veuillez choisir la branche à créer"
[ "$release" != master ] && newver="${release#release-}"
;;
major)
setx newver=pver -s "$oldver" -ux "${pver_opts[@]}"
release="release-$newver"
;;
minor)
setx newver=pver -s "$oldver" -uz "${pver_opts[@]}"
release="release-$newver"
;;
patchlevel)
setx newver=pver -s "$oldver" -up "${pver_opts[@]}"
release="release-$newver"
;;
esac
set -- "$release" "$1"
action=checkout
fi
if [ "$action" == checkout ]; then
release="$1"
source="${2:-develop}"
if [ -z "$release" ]; then
setx -a branches=list_release_branches "$origin"
if [ ${#branches[*]} -eq 0 ]; then
# en l'absence de branche de release, basculer sur master
release=master
elif [ ${#branches[*]} -eq 1 ]; then
# s'il n'y en a qu'une, la prendre
release="${branches[0]}"
else
# sinon, donner le choix dans un menu
array_add branches master
default_branch="$branch"
array_contains branches "$default_branch" || default_branch="${branches[0]}"
simple_menu release branches -d "$default_branch" \
-t "Basculer vers une branche de release" \
-m "Veuillez choisir la branche vers laquelle basculer"
fi
fi
# On est peut-être déjà sur la bonne branche
if git_is_branch "$release"; then
if [ -z "$UTOOLS_VCS_OFFLINE" ]; then
git_track_branch "$release" "$origin"
fi
git_fast_forward "$release" "" "$origin"
exit 0
fi
# Créer/basculer vers une release branch
git_ensure_cleancheckout
is_any_branch "$release" master release || die "$release: ce n'est pas une branche de release"
r=0
newbranch=
if git_have_branch "$release"; then
git checkout "$release"; r=$?
elif git_have_rbranch "$release"; then
git checkout "$release"; r=$?
else
estepn "\
Vous allez créer la nouvelle branche de release ${COULEUR_VERTE}$release${COULEUR_NORMALE}
à partir de la branche source ${COULEUR_BLEUE}$source${COULEUR_NORMALE}"
ask_yesno "Voulez-vous continuer?" O || die
newbranch=1
git_ensure_branch "$release" "$source" "$origin"
[ $? -eq 2 ] && die "Impossible de créer la branche $release. Veuillez vérifier que la branche $source existe"
git checkout "$release"; r=$?
if [ -z "$newver" ]; then
# le cas échéant, tenter de calculer la version en fonction de la release
is_release_branch "$release" && newver="${release#release-}"
fi
if [ "$r" -eq 0 -a -n "$write" ]; then
set cwd=pwd
setx workdir=git rev-parse --show-toplevel
[ -d "$workdir" ] && cd "$workdir"
commitmsg="Init changelog"
if [ -n "$newver" ]; then
pver -uv "$newver" && commitmsg="Init changelog & version $newver"
fi
version="$newver"
[ -n "$version" ] || version="$release"
changelog="## Version $version du $(date +%d/%m/%Y-%H:%M)"
setx mergebase=git merge-base master "$release"
setxp modifs=git log --oneline "$mergebase..$release" // grep -v "Intégration de la branche release-"
[ -n "$modifs" ] && changelog="$changelog
$modifs"
ac_set_tmpfile tmpchanges
echo "$changelog" >>"$tmpchanges"
if [ -n "$edit_changes" ]; then
estep "Lancement d'un éditeur pour vérifier la liste des modifications"
"${EDITOR:-vi}" "$tmpchanges"
fi
if [ -f CHANGES.txt ]; then
echo >>"$tmpchanges"
cat CHANGES.txt >>"$tmpchanges"
fi
cat "$tmpchanges" >CHANGES.txt
ac_clean "$tmpchanges"
git add -A
git commit -m "$commitmsg"
cd "$cwd"
fi
fi
if [ "$r" -eq 0 ]; then
# éventuellement fast-forwarder automatiquement
git_fast_forward "$release" "" "$origin"
fi
set -- "$release"
if [ "$r" -eq 0 -a -n "$merge" ]; then
# mettre à jour la branche sur laquelle on se trouve
setx branch=git_get_branch
action=merge
elif [ "$r" -eq 0 -a -n "$newbranch" ]; then
action=push
array_addu push_branches "$release"
else
exit "$r"
fi
fi
setx -a branches=list_release_branches
setb have_release_branches=[ ${#branches[*]} -gt 0 ]
release="$1"
if [ -n "$release" ]; then
if [ -n "$have_release_branches" ]; then
is_release_branch "$release" || die "$release: ce n'est pas une branche de release"
elif ! is_develop_branch "$release"; then
die "Aucune branche de release n'existe, vous devez fusionner à partir de develop"
fi
git_have_branch "$release" || die "$release: branche introuvable"
elif is_release_branch "$branch"; then
release="$branch"
fi
if [ "$action" == merge ]; then
confirm=
if [ -z "$release" ]; then
if [ ${#branches[*]} -eq 0 ]; then
ewarn "Aucune branche de release n'a été préparée."
ewarn "La branche develop sera fusionnée directement dans master."
release=develop
confirm=-y
elif [ ${#branches[*]} -eq 1 ]; then
release="${branches[0]}"
estepn "Autosélection de $release"
else
default_release="$branch"
array_contains branches "$default_release" || default_release="${branches[0]}"
simple_menu release branches -d "$default_release" \
-t "Choix de la branche de release" \
-m "Veuillez choisir la branche"
fi
fi
estepn "Intégration ${COULEUR_VERTE}$release${COULEUR_NORMALE} --> ${COULEUR_BLEUE}master${COULEUR_NORMALE}"
ask_yesno $confirm "Voulez-vous continuer?" O || die
git checkout master || die
git merge "$release" -m "Intégration de la branche $release" --no-ff || die
if [ -z "$newver" ]; then
# le cas échéant, tenter de calculer la version en fonction de la release
is_release_branch "$release" && newver="${release#release-}"
fi
if [ -n "$newver" ]; then
estepn "Création du tag $newver"
array_addu push_tags "$newver"
git tag --force "$newver" || die
if git_have_remote "$origin"; then
if [ -z "$UTOOLS_VCS_OFFLINE" ]; then
git push "$origin" tag "$newver"
elif [ -z "$push_deferred" ]; then
eimportant "Le tag $newver n'a pas été poussé vers l'origine.
Il faudra le faire manuellement avec la commande suivante:
$(qvals git push "$origin" tag "$newver")
ou celle-ci pour pour pousser TOUS les tags:
$(qvals git push "$origin" --tags)"
fi
fi
fi
if [ "$release" != develop ]; then
estepn "Intégration ${COULEUR_VERTE}$release${COULEUR_NORMALE} --> ${COULEUR_BLEUE}develop${COULEUR_NORMALE}"
git checkout develop
git merge "$release" -m "Intégration de la branche $release" --no-ff || die
fi
# mettre à jour la branche sur laquelle on se trouve
git checkout master || die
setx branch=git_get_branch
action=delete
fi
if [ "$action" == delete -a "$release" != develop ]; then
if [ -z "$force_delete" ]; then
# vérifier que la branche a été fusionnée
git_is_merged "$release" master || die "Refus de supprimer la branche $release: elle n'a pas été fusionnée dans master"
git_is_merged "$release" develop || die "Refus de supprimer la branche $release: elle n'a pas été fusionnée dans develop"
fi
if [ "$branch" == "$release" ]; then
# si on est sur la branche en question, en sortir pour pouvoir la
# supprimer
git checkout master || die
fi
estepi "Suppression de la branche locale"
git branch -D "$release"
if git_have_remote "$origin"; then
if [ -z "$UTOOLS_VCS_OFFLINE" ]; then
estepi "Suppression de la branche distante"
git push "$origin" ":$release"
elif git_have_rbranch "$release" "$origin"; then
eimportant "\
La branche $origin/$release n'a plus lieu d'être, mais la configuration actuelle interdit de la supprimer.
Veuillez le faire manuellement avec la commande suivante:
$(qvals git push "$origin" ":$release")"
fi
fi
action=push
array_addu push_branches master
array_addu push_branches develop
fi
if [ "$action" == push -a -n "$push_deferred" ]; then
if git_have_remote "$origin"; then
estepi "Mise à jour de l'origine"
for tag in "${push_tags[@]}"; do
git push "$origin" tag "$tag"
done
for branch in "${push_branches[@]}"; do
setx rbranch=git_get_branch_rbranch "$branch" "$origin"
rbranch="${rbranch#refs/remotes/$origin/}"
git push "$origin" "$branch:$rbranch"
done
fi
fi
if [ "$action" == summary ]; then
setx mergebase=git merge-base master develop
git log --oneline "$mergebase..develop" | grep -v "Intégration de la branche release-"
elif [ "$action" == diff ]; then
if [ -n "$log" ]; then
if [ -n "$release" ]; then
git log ${diff:+-p} master.."$release"
else
git log ${diff:+-p} master..develop
fi
elif [ -n "$diff" ]; then
if [ -n "$release" ]; then
git diff master.."$release"
else
git diff master..develop
fi
fi
fi