1897 lines
69 KiB
Bash
Executable File
1897 lines
69 KiB
Bash
Executable File
#!/bin/bash
|
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
SCRIPT_ALIASES=(
|
|
pf0:-0
|
|
pfn:-N
|
|
pfg:-g
|
|
pfb:-b
|
|
pfs:-s
|
|
pfa:-a
|
|
pfe:-e
|
|
pfd:-d
|
|
)
|
|
|
|
ORIGEXT=pff
|
|
DEFAULT_ORIGEXTS=(".$ORIGEXT" .origine .default)
|
|
PFFCONF=.pff.conf # ne pas modifier
|
|
DEFAULT_PROTECTS=(/.git/ .svn/ /pff/ "/$PFFCONF")
|
|
NUTOOLS_SH_ARCHIVE_SUPPORT=1
|
|
|
|
PFFCONFVARS=(
|
|
"VERSION//Version actuellement installée"
|
|
-a
|
|
"PVERSIONS//Versions en attente d'intégration"
|
|
"PROFILES//Profils définis"
|
|
-s
|
|
"DISTTYPE=auto//Type de distribution upstream: full ou patch"
|
|
-a
|
|
"ORIGEXTS=//Extensions origines"
|
|
"AUTO_CMDS//Commandes à lancer lors des vérifications automatiques"
|
|
"NEW_CMDS//Commandes à lancer avant l'action new"
|
|
"PROTECTS=//Fichiers locaux à protéger lors de l'intégration au format rsync e.g /dir/, /file, etc."
|
|
"FILTERS//Filtres appliqués aux fichiers lors de l'intégration, de la forme 'filespec:filter'. Cf la doc pour le détail du format"
|
|
"NOMERGES=//Fichiers qu'il ne faut pas chercher à fusionner. Cf la doc pour le détail du format"
|
|
-s
|
|
"NOUPSTREAM=//Indiquer qu'il n'y a pas de fichiers upstream. pff est uniquement utilisé pour gérer des profils de fichiers"
|
|
)
|
|
|
|
if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
|
|
# créer les liens
|
|
scriptname="$(basename -- "$0")"
|
|
for alias in "${SCRIPT_ALIASES[@]}"; do
|
|
alias="${alias%:*}"
|
|
ln -s "$scriptname" "$alias"
|
|
done
|
|
exit 0
|
|
elif [ $# -eq 1 -a "$1" == --nutools-completion ]; then
|
|
echo '
|
|
function __pff_profiles() {
|
|
local cwd="$(pwd)"
|
|
local pffdir="$cwd"
|
|
while true; do
|
|
if [ -f "$pffdir/'"$PFFCONF"'" -a -d "$pffdir/pff" ]; then
|
|
cd "$pffdir/pff"
|
|
/bin/ls -1d * | while read f; do
|
|
[ -d "$f" -a "$f" != Current ] || continue
|
|
f="$1$f"
|
|
if [[ "${f:0:2}" == -[eEn] ]]; then
|
|
echo -n -
|
|
echo "${f:1}"
|
|
else
|
|
echo "$f"
|
|
fi
|
|
done
|
|
cd "$cwd"
|
|
break
|
|
fi
|
|
[ "$pffdir" == / -o "$pffdir" == "$HOME" ] && break
|
|
pffdir="$(dirname -- "$pffdir")"
|
|
done
|
|
}
|
|
function __pfe_profiles() {
|
|
echo ALL
|
|
__pff_profiles "$@" | grep -vxF Base
|
|
}
|
|
function __pfs_completion() {
|
|
local cur
|
|
_get_comp_words_by_ref cur
|
|
COMPREPLY=($(compgen -W "$(__pff_profiles)" "$cur"))
|
|
}
|
|
complete -F __pfs_completion -o bashdefault -o default pfs
|
|
function __pfe_completion() {
|
|
local cur prev opt comp
|
|
_get_comp_words_by_ref cur prev
|
|
if [[ "$prev" == -*p ]]; then
|
|
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
|
|
elif [ "$prev" == --profile ]; then
|
|
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
|
|
elif [[ "$cur" == -*p* ]]; then
|
|
comp="${cur#-*p}"; opt="${cur:0:$((${#cur}-${#comp}))}"
|
|
COMPREPLY=($(compgen -W "$(__pfe_profiles "$opt")" -- "$cur"))
|
|
fi
|
|
}
|
|
complete -F __pfe_completion -o bashdefault -o default pfe
|
|
function __pff_completion() {
|
|
local cur prev opt comp
|
|
_get_comp_words_by_ref cur prev
|
|
if [ "${COMP_WORDS[1]}" == -e -o "${COMP_WORDS[1]}" == --edit ]; then
|
|
# ne compléter -p que si on est en mode --edit
|
|
if [[ "$prev" == -*p ]]; then
|
|
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
|
|
elif [ "$prev" == --profile ]; then
|
|
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
|
|
elif [[ "$cur" == -*p* ]]; then
|
|
comp="${cur#-*p}"; opt="${cur:0:$((${#cur}-${#comp}))}"
|
|
COMPREPLY=($(compgen -W "$(__pfe_profiles "$opt")" -- "$cur"))
|
|
fi
|
|
elif [[ "$prev" == -*s ]]; then
|
|
COMPREPLY=($(compgen -W "$(__pff_profiles)" -- "$cur"))
|
|
elif [ "$prev" == --switch ]; then
|
|
COMPREPLY=($(compgen -W "$(__pff_profiles)" -- "$cur"))
|
|
elif [[ "$cur" == -*s* ]]; then
|
|
comp="${cur#-*s}"; opt="${cur:0:$((${#cur}-${#comp}))}"
|
|
COMPREPLY=($(compgen -W "$(__pff_profiles "$opt")" -- "$cur"))
|
|
fi
|
|
}
|
|
complete -F __pff_completion -o bashdefault -o default pff
|
|
'
|
|
exit 0
|
|
fi
|
|
|
|
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
|
|
urequire multiconf vcs javaproperties
|
|
|
|
function display_help() {
|
|
uecho "$scriptname: gestion de modifications locales
|
|
|
|
Un produit est distribué par un partenaire, et il faut maintenir certaines
|
|
modifications locales tout en continuant d'accepter des modififications de
|
|
l'upstream. Ce script aide à maintenir un tel scénario.
|
|
|
|
En général, la distribution upstream identifie les fichiers modifiables en leur
|
|
donnant une extension particulière, par exemple 'file.origine'. On peut aussi
|
|
décider de modifier des fichiers qui n'ont pas été prévus comme tels par
|
|
l'upstream. On peut aussi avoir plusieurs ensembles de modifications, rassemblés
|
|
en profils, e.g prod ou test pour les modifications à déployer en prod ou en
|
|
test.
|
|
|
|
Terminologie: Les fichiers pour lesquels il faut maintenir une version locale
|
|
sont appelés 'fichiers locaux', qu'ils viennent de la distribution upstream ou
|
|
non. Les autres fichiers qui proviennent de la distribution sont appelés
|
|
'fichiers upstream'. Les fichiers livrés dans la distribution upstream avec une
|
|
extension particulière pour indiquer qu'ils sont modifiables sont appelés
|
|
'fichiers origine'.
|
|
|
|
Les fichiers sont classés dans des profils spécifiques. Les profils reconnus
|
|
sont:
|
|
- Base est le profil des fichiers upstream non modifiés. Les fichiers origine
|
|
sont intégrés dans ce profil.
|
|
- Common est le profil des fichiers upstream modifiés. Tous les fichiers
|
|
devraient être modifiés dans ce profil.
|
|
- Les autres profils sont basés sur Common en priorité puis sur Base en cas de
|
|
non existence dans Common. Il peut s'agir de profils comme prod ou test, qui
|
|
contiennent des modifications spécifiques à différents cas d'utilisation.
|
|
|
|
USAGE
|
|
$scriptname [options]
|
|
|
|
CONFIGURATION
|
|
Le fichier $PFFCONF contient des variables qui conditionnent le comportement de
|
|
pff:
|
|
|
|
PROTECTS -- liste de spécifications de fichiers ou de répertoires à protéger
|
|
lors de l'intégration. Ces spécifications sont au format rsync, e.g
|
|
/dir/, /file, etc.
|
|
|
|
FILTERS -- liste de définitions de filtres à appliquer aux fichiers lors de
|
|
l'intégration. ces spécifications sont de la forme 'filespec:filter'.
|
|
Les spécifications de fichier peuvent contenir des wildcards et sont de
|
|
deux sortes:
|
|
- sans chemin, e.g 'myfile' ou '*.c', tous les fichiers de ce nom
|
|
correspondent
|
|
- avec un chemin, e.g '/myfile', 'path/to/*.jsp', la correspondance est
|
|
cherchée relativement au répertoire du projet. ainsi '/*.c' ne matche
|
|
que les fichiers ayant l'extension .c situés dans le répertoire du
|
|
projet
|
|
Les filtres disponibles sont:
|
|
nl2lf -- forcer le caractère de fin de ligne à LF
|
|
nl2crlf -- forcer le caractère de fin de ligne à CR+LF
|
|
nl2cr -- forcer le caractère de fin de ligne à CR
|
|
normalize_properties -- normaliser fichier de propriétés java
|
|
Il est possible de créer de nouveaux filtres en définissant des
|
|
fonctions de la forme pff_filter_NAME(). Ces fonctions, si elles sont
|
|
appelées sans argument, doivent filtrer l'entrée standard. Si un
|
|
argument est spécifié, il faut filtrer ce fichier-là.
|
|
|
|
NOMERGES -- liste de spécifications de fichiers qu'il ne faut pas chercher à
|
|
fusionner. Les spécifications de fichier peuvent contenir des wildcards
|
|
et sont de deux sortes:
|
|
- sans chemin, e.g 'myfile' ou '*.c', tous les fichiers de ce nom
|
|
correspondent
|
|
- avec un chemin, e.g '/myfile', 'path/to/*.jsp', la correspondance est
|
|
cherchée relativement au répertoire du projet. ainsi '/*.c' ne matche
|
|
que les fichiers ayant l'extension .c situés dans le répertoire du
|
|
projet
|
|
|
|
NOUPSTREAM -- indiquer qu'il n'y a pas de distribution upstream ni de
|
|
fichiers origine. pff est alors uniquement utilisé pour gérer des
|
|
profils de fichier.
|
|
Pour le moment, la seule différence fonctionnelle est que le mode des
|
|
fichiers de pff/Base n'est pas forcé à 0444. Ainsi, ces fichiers sont
|
|
traités au même titre que ceux du répertoire pff/Common
|
|
Dans ce mode, on supporte aussi les liens vers des répertoires entiers:
|
|
il suffit de créer le répertoire dans pff/Base avec un unique fichier
|
|
.pffdir à l'intérieur. Lors du switch le répertoire correspondant est
|
|
lié s'il existe dans un profil.
|
|
NB: s'il existe d'autres fichiers dans le répertoire, ils sont ignorés.
|
|
Cela permet d'avoir un ensemble de fichiers par défaut, mais les liens
|
|
symboliques doivent être faits manuellement.
|
|
|
|
COMMANDES / OPTIONS
|
|
Les arguments du script dépendent de la commande utilisée. Les commandes
|
|
supportées sont:
|
|
-0, --init [WORKDIR [ARCHIVE]]
|
|
Initialiser un répertoire pour le suivi des distributions upstream. Le
|
|
fichier $PFFCONF contient toutes les informations paramétrables sur la
|
|
gestion du projet.
|
|
--s, --origext .EXT
|
|
Ajouter une extension à la liste des extensions origines, c'est à dire
|
|
les fichiers identifiés dans la distribution upstream comme contenus
|
|
modifiables. Par défaut, les extensions suivantes sont reconnues:
|
|
${DEFAULT_ORIGEXTS[*]}
|
|
Cette option peut être utilisée autant de fois que nécessaire.
|
|
--k, --clear-origexts
|
|
Supprimer la liste par défaut des extensions origines. Cette option doit
|
|
être spécifiée avant l'option --origext pour construire une nouvelle
|
|
liste. La liste des extensions ne doit pas être vide. Si c'est le cas,
|
|
elle est modifiée pour contenir l'unique élément (.$ORIGEXT)
|
|
|
|
-N, --new ARCHIVE [WORKDIR]
|
|
-M, --new-only ARCHIVE [WORKDIR]
|
|
Intégrer une nouvelle distribution upstream, sous forme d'une archive ou
|
|
d'un répertoire. Les fichiers origine et les fichiers locaux sont placés
|
|
en attente d'intégration. Les autres sont intégrés sans modification.
|
|
La variante --new appelle automatiquement --patch après l'intégration de
|
|
l'archive.
|
|
-V, --version VERSION
|
|
Spécifier la version de l'archive qui est intégrée avec l'option --new.
|
|
Normalement, cette information est calculée automatiquement à partir du
|
|
nom de l'archive. Si la source est un répertoire ou une archive sans
|
|
numéro de version, cette option est requise.
|
|
-F, --full-archive
|
|
Intégrer une distribution complète. Par défaut, les archives avec
|
|
l'extension .war sont considérées commes des livraisons complètes. Si
|
|
la source est un répertoire ou une archive sans extension, l'une des
|
|
options -F ou -H est requise.
|
|
-H, --patch-archive
|
|
Intégrer un patch. Par défaut, les archives avec l'extension .zip sont
|
|
considérées comme des patches. Si la source est un répertoire ou une
|
|
archive sans extension, l'une des options -F ou -H est requise.
|
|
Avec une distribution de type patch, on considère que les fichiers
|
|
livrés ne sont pas des fichiers origines. Il n'y a donc aucun traitement
|
|
particulier: l'archive est simplement intégrée telle quelle.
|
|
--auto-unwrap
|
|
Si l'archive ne contient qu'un seul répertoire à la racine nommé d'après
|
|
le nom de base de l'archive, c'est le contenu de ce répertoire qui est
|
|
considéré. C'est l'option par défaut.
|
|
Le test est effectué avec et sans la version. Par exemple, si l'archive
|
|
est product-x.y.zip, et qu'elle contient un unique répertoire nommé
|
|
'product-x.y' ou 'product' alors intégrer le contenu de ce répertoire.
|
|
-E, --unwrap
|
|
Si l'archive ne contient qu'un seul répertoire à la racine, intégrer
|
|
inconditionellement le contenu de se répertoire.
|
|
--no-unwrap
|
|
Intégrer tel quel le contenu de l'archive.
|
|
|
|
-g, --patch [WORKDIR]
|
|
Intégrer les modifications entrantes sur les fichiers nouvellement
|
|
arrivés via l'option --new
|
|
-o, --ours
|
|
Utiliser la stratégie '-s recursive -X ours' en cas de conflit lors de
|
|
l'intégration de la version.
|
|
--ask-commit
|
|
-c, --commit
|
|
--no-commit
|
|
Après l'intégration avec succès d'un patch, demander à l'utilisateur
|
|
s'il veut faire commit & push dans git (--ask-commit, la valeur par
|
|
défaut), le faire sans confirmation (--commit), ou ne jamais le faire
|
|
(--no-commit)
|
|
--no-push
|
|
Ne pas faire de push après un commit. On peut aussi lancer la commande
|
|
suivante avant pff:
|
|
export UTOOLS_VCS_OFFLINE=1
|
|
|
|
-b, --add-global FILES...
|
|
Ajouter/Identifier un fichier comme un fichier local pour tous les
|
|
profils. Le fichier est copié dans le profil Base.
|
|
|
|
--locals [WORKDIR]
|
|
Lister les fichiers locaux
|
|
|
|
--profiles [WORKDIR]
|
|
Lister les profils valides
|
|
|
|
-s, --switch PROFILE [WORKDIR]
|
|
Basculer le profil courant
|
|
|
|
-a, --add-local FILES...
|
|
Ajouter/Identifier un fichier comme un fichier local et le rendre
|
|
spécifique au profil courant.
|
|
-e, --edit FILES...
|
|
Editer des fichiers, implique --add-local
|
|
-p, --profile PROFILE
|
|
Pour l'option --edit, sélectionner le profil spécifié comme celui
|
|
concerné pour les fichier mentionnés après cette option. Par exemple:
|
|
$scriptname -e A -pprod B C -ptest D
|
|
Edite le fichier A du profil courant, les fichiers B et C du profil prod
|
|
et le fichier D du profil test.
|
|
Le profil ALL est spécial et permet d'éditer le fichier dans tous les
|
|
profils *où il existe*, excepté Base parce que ce profil contient des
|
|
fichiers qui ne devraient pas être modifiés.
|
|
Pour tous les autres profils, si le fichier n'existe pas dans le profil
|
|
spécifié il est rajouté dans le profil avant son édition.
|
|
Attention: pour que l'option -p/--profile soit correctement reconnue
|
|
avec l'option -e/--edit, il faut que cette dernière option soit
|
|
mentionnée en premier sur la ligne de commande.
|
|
-P, --prod
|
|
-T, --test
|
|
-A, --all-profiles
|
|
Raccourcis pour respectivement -pprod, -ptest et -pALL
|
|
|
|
-d, --diff [DESTP [WORKDIR]]
|
|
-d, --diff [SRCP DESTP [WORKDIR]]
|
|
Afficher la différence entre entre deux profils. Avec la première
|
|
syntaxe, comparer le profil DESTP au profil courant. Avec la deuxième
|
|
syntaxe, comparer le profil DESTP au profil SRCP.
|
|
-l, --list-names
|
|
N'afficher que les noms des fichiers qui sont différents
|
|
|
|
--infos [WORKDIR]
|
|
Afficher des informations sur le projet courant: profils, fichiers
|
|
locaux, profil courant, etc. C'est la commande par défaut.
|
|
Les fichiers locaux sont taggés avec les valeurs suivantes
|
|
${COULEUR_ROUGE}P${COULEUR_NORMALE} il existe un patch pour ce fichier dans le profil courant
|
|
${COULEUR_BLEUE}*${COULEUR_NORMALE} ce fichier local est spécifique à ce profil
|
|
$(get_color YELLOW)C${COULEUR_NORMALE} ce fichier local est spécifique au profil Common
|
|
-l, --show-all
|
|
Afficher tous les fichiers locaux au lieu de se contenter des fichiers
|
|
modifiés dans le profil courant."
|
|
}
|
|
|
|
# Nomenclature pour le nommage des fichiers traités:
|
|
# pfile: le chemin absolu du fichier dans le projet
|
|
# rfile: le chemin relatif du fichier dans le projet
|
|
# bfile: le chemin absolu du fichier dans pff/Base/
|
|
# Cfile: le chemin absolu du fichier dans pff/Common/
|
|
# cfile: le chemin absolu du fichier dans pff/Current/
|
|
# Pfile: le chemin absolu du fichier dans pff/ANYPROFILE/
|
|
# plink: la destination du lien pfile
|
|
# clink: la destination du lien cfile
|
|
# Plink: la destination du lien Pfile
|
|
|
|
function flexists() {
|
|
[ -e "$1" -o -L "$1" ]
|
|
}
|
|
|
|
function multiups() {
|
|
# afficher un chemin vers le haut e.g ../../.. avec autant d'éléments que
|
|
# les répertoires du chemin relatif $1.
|
|
# méthode: commencer avec la valeur de départ $2 et préfixer avec autant de
|
|
# ../ que nécessaire. puis afficher le résultat.
|
|
local tmp="$1" link="$2"
|
|
setx tmp=dirname -- "$tmp"
|
|
while [ "$tmp" != . ]; do
|
|
[ -n "$link" ] && link="/$link"
|
|
link="..$link"
|
|
setx tmp=dirname -- "$tmp"
|
|
done
|
|
echo "$link"
|
|
}
|
|
|
|
function find_pffdir() {
|
|
# trouver le répertoire du projet pff à partir du répertoire $2(=.) et
|
|
# mettre le chemin absolu dans la variable $1(=pffdir)
|
|
# si le répertoire n'est pas trouvé, retourner 1
|
|
local destvar="${1:-pffdir}" pffdir
|
|
setx pffdir=abspath "${2:-.}"
|
|
while true; do
|
|
if [ -f "$pffdir/$PFFCONF" -a -d "$pffdir/pff" ]; then
|
|
local "$destvar"
|
|
upvar "$destvar" "$pffdir"
|
|
return 0
|
|
fi
|
|
[ "$pffdir" == / -o "$pffdir" == "$HOME" ] && break
|
|
setx pffdir=dirname -- "$pffdir"
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function ensure_pffdir() {
|
|
# trouver le répertoire du projet pff à partir du répertoire $2(=.) et
|
|
# mettre le chemin absolu dans la variable $1(=pffdir)
|
|
# si le répertoire n'est pas trouvé, arrêter le script avec un code d'erreur
|
|
local destvar="${1:-pffdir}" pffdir
|
|
if find_pffdir pffdir "$2"; then
|
|
conf_init "${PFFCONFVARS[@]}"
|
|
source "$pffdir/$PFFCONF"
|
|
local "$destvar"; upvar "$destvar" "$pffdir"
|
|
return
|
|
fi
|
|
local msg="Projet pff introuvable (utiliser --init ?)"
|
|
[ -n "$2" ] && die "$2: $msg" || die "$msg"
|
|
}
|
|
|
|
function get_current_profile() {
|
|
# afficher le profil courant du projet pff $1, s'il est défini
|
|
local pffdir="$1"
|
|
[ -L "$pffdir/pff/.Current" ] && readlink "$pffdir/pff/.Current"
|
|
}
|
|
|
|
function get_profiles() {
|
|
# afficher tous les profils valides du projet pff $1
|
|
local pffdir="$1"
|
|
(for profile in "${PROFILES[@]}"; do echo "$profile"; done
|
|
list_dirs "$pffdir/pff") | sort -u | grep -vxF Current
|
|
}
|
|
|
|
function get_user_profiles() {
|
|
# afficher tous les profils modifiables du projet pff $1 (c'est à dire tous
|
|
# les profils valides excepté Base)
|
|
get_profiles "$@" | grep -vxF Base
|
|
}
|
|
|
|
function get_first_profile() {
|
|
# afficher le premier profil autre que Base du projet pff $1
|
|
local profile
|
|
profile="${PROFILES[0]}"
|
|
if [ -z "$profile" -o "$profile" == Base ]; then
|
|
get_user_profiles "$@" | head -n1
|
|
else
|
|
echo "$profile"
|
|
fi
|
|
}
|
|
|
|
function get_local_files() {
|
|
# afficher tous les fichiers locaux exprimés relativement au répertoire du
|
|
# projet pff $1
|
|
# pour les répertoires liés, ne lister que le fichier .pffdir
|
|
local pffdir="$1" files file dirs dir isald
|
|
files="$(find "$pffdir/pff/Base" -type f | sed "s|^$pffdir/pff/Base/||" | grep -v '/__pv-[^/_]*__[^/]*$')"
|
|
dirs="$(echo "$files" | grep '/\.pffdir$' | sed 's/\.pffdir$//')"
|
|
setx -a files=echo "$files"
|
|
setx -a dirs=echo "$dirs"
|
|
for file in "${files[@]}"; do
|
|
isald=
|
|
for dir in "${dirs[@]}"; do
|
|
if [ "${file#$dir}" != "$file" ]; then
|
|
isald=1
|
|
break
|
|
fi
|
|
done
|
|
[ -z "$isald" -o "$file" == "${dir}.pffdir" ] && echo "$file"
|
|
done
|
|
}
|
|
|
|
function get_rfile() {
|
|
# obtenir le chemin relatif du fichier $1 exprimé par rapport au répertoire
|
|
# du projet pff $2. Si c'est un fichier d'un répertoire de profil,
|
|
# l'exprimer comme un chemin du répertoire de projet, e.g pff/Profile/path
|
|
# devient path
|
|
# retourner 1 si le chemin est invalide (est situé en dehors de pffdir ou
|
|
# pas dans un répertoire de profil)
|
|
local rfile="$1" pffdir="$2"
|
|
setx rfile=abspath "$rfile"
|
|
[ "${rfile#$pffdir/}" != "$rfile" ] || return 1
|
|
rfile="${rfile#$pffdir/}"
|
|
if [[ "$rfile" == pff/*/* ]]; then
|
|
rfile="${rfile#pff/*/}"
|
|
elif [[ "$rfile" == pff/* ]]; then
|
|
return 1
|
|
fi
|
|
echo "$rfile"
|
|
}
|
|
function get_pfile() {
|
|
# obtenir le chemin du fichier $1 exprimé par rapport au répertoire du
|
|
# profil $2 dans le répertoire de projet $3
|
|
# retourner 1 si le chemin est invalide (est situé en dehors de pffdir ou
|
|
# pas dans un répertoire de profil)
|
|
local pfile="$1" profile="$2" pffdir="$3"
|
|
setx pfile=abspath "$pfile"
|
|
[ "${pfile#$pffdir/}" != "$pfile" ] || return 1
|
|
pfile="${pfile#$pffdir/}"
|
|
if [[ "$pfile" == pff/*/* ]]; then
|
|
pfile="${pfile#pff/*/}"
|
|
elif [[ "$pfile" == pff/* ]]; then
|
|
return 1
|
|
fi
|
|
echo "$pffdir/pff/$profile/$pfile"
|
|
}
|
|
function get_bfile() { get_pfile "$1" Base "$2"; }
|
|
function get_Cfile() { get_pfile "$1" Common "$2"; }
|
|
function get_cfile() { get_pfile "$1" Current "$2"; }
|
|
|
|
function get_vlfiles_nostrip() {
|
|
# afficher tous les fichiers de version
|
|
local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
|
|
[ -d "$pffdir/pff/$profile" ] || return
|
|
if [ -n "$version" ]; then
|
|
if [ -n "$rfile" ]; then
|
|
setx rfile=add_pv "$rfile" "$version"
|
|
find "$pffdir/pff/$profile" \
|
|
-type f -path "$pffdir/pff/$profile/${rfile}" -o \
|
|
-type l -path "$pffdir/pff/$profile/${rfile}"
|
|
else
|
|
find "$pffdir/pff/$profile" \
|
|
-type f -name "__pv-${version}__*" -o \
|
|
-type l -name "__pv-${version}__*"
|
|
fi
|
|
else
|
|
if [ -n "$rfile" ]; then
|
|
setx rfile=add_pv "$rfile" "*"
|
|
find "$pffdir/pff/$profile" \
|
|
-type f -path "$pffdir/pff/$profile/${rfile}" -o \
|
|
-type l -path "$pffdir/pff/$profile/${rfile}"
|
|
else
|
|
find "$pffdir/pff/$profile" \
|
|
-type f -name "__pv-*__*" -o \
|
|
-type l -name "__pv-*__*"
|
|
fi
|
|
fi
|
|
}
|
|
function get_vlfiles() {
|
|
local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
|
|
get_vlfiles_nostrip "$@" | sed "s|^$pffdir/pff/$profile/||"
|
|
}
|
|
function strip_pv() {
|
|
local vlfile="$1" version="$2" vldir vlname
|
|
if [[ "$vlfile" == */* ]]; then
|
|
vldir="${vlfile%/*}/"
|
|
vlname="${vlfile##*/}"
|
|
else
|
|
vldir=
|
|
vlname="$vlfile"
|
|
fi
|
|
if [ -n "$version" ]; then
|
|
vlname="${vlname#__pv-${version}__}"
|
|
else
|
|
vlname="${vlname#__pv-*__}"
|
|
fi
|
|
echo "$vldir$vlname"
|
|
}
|
|
function add_pv() {
|
|
local vlfile="$1" version="$2" vldir vlname
|
|
if [[ "$vlfile" == */* ]]; then
|
|
vldir="${vlfile%/*}/"
|
|
vlname="${vlfile##*/}"
|
|
else
|
|
vldir=
|
|
vlname="$vlfile"
|
|
fi
|
|
echo "${vldir}__pv-${version}__${vlname}"
|
|
}
|
|
|
|
function is_nomerge() {
|
|
local file="$1" pffdir="$2"
|
|
local nomerge rfile
|
|
setx rfile=get_rfile "$file" "$pffdir"
|
|
setx file=basename -- "$rfile" # utilisé pour le match sur le nom du fichier
|
|
for nomerge in "${NOMERGES[@]}"; do
|
|
if [[ "$nomerge" == */* ]]; then
|
|
# matcher sur le chemin relatif
|
|
nomerge="${nomerge#/}"
|
|
if eval "[[ $(qval "$rfile") == $(qwc "$nomerge") ]]"; then
|
|
return 0
|
|
fi
|
|
else
|
|
# matcher uniquement sur le nom du fichier
|
|
if eval "[[ $(qval "$file") == $(qwc "$nomerge") ]]"; then
|
|
return 0
|
|
fi
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function sync_vlfiles() {
|
|
# synchroniser les fichiers de version $3..@ dans tous les répertoires de
|
|
# profil, ou seulement le répertoire de profil $2 si la valeur n'est pas
|
|
# vide.
|
|
local pffdir="$1"; shift
|
|
local profile="$1"; shift
|
|
local -a profiles
|
|
if [ -n "$profile" ]; then
|
|
profiles=("$profile")
|
|
else
|
|
array_from_lines profiles "$(get_user_profiles "$pffdir")"
|
|
fi
|
|
|
|
local vlfile rfile prefix pfile plink tmp
|
|
for vlfile in "$@"; do
|
|
setx rfile=strip_pv "$vlfile"
|
|
for profile in "${profiles[@]}"; do
|
|
prefix="$pffdir/pff/$profile"
|
|
flexists "$prefix/$rfile" || continue
|
|
pfile="$prefix/$vlfile"
|
|
setx plink=multiups "$profile/$vlfile" "Base/$vlfile"
|
|
if [ -L "$pfile" ]; then
|
|
# correction éventuelle du lien existant
|
|
setx tmp=readlink "$pfile"
|
|
[ "$tmp" == "$plink" ] || ln -sfT "$plink" "$pfile"
|
|
else
|
|
ln -sf "$plink" "$pfile" || return
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
function select_profile() {
|
|
# sélectionner le profil $1 dans le projet pff $2. créer d'abord le profil
|
|
# s'il n'existe pas.
|
|
local profile="$1" pffdir="$2"
|
|
# créer le répertoire de profil si nécessaire
|
|
mkdir -p "$pffdir/pff/$profile" || return 1
|
|
# mettre à jour les liens
|
|
local -a lfiles; local lfile src dest
|
|
setx -a lfiles=get_local_files "$pffdir"
|
|
for lfile in "${lfiles[@]}"; do
|
|
src="$pffdir/pff/Current/$lfile"
|
|
if [[ "$lfile" == */.pffdir ]]; then
|
|
# répertoires entiers
|
|
lfile="${lfile%/.pffdir}"
|
|
src="${src%/.pffdir}"
|
|
if [ -d "$pffdir/pff/$profile/$lfile" ]; then
|
|
dest="$profile/$lfile"
|
|
elif [ "$profile" != Common -a -d "$pffdir/pff/Common/$lfile" ]; then
|
|
dest="Common/$lfile"
|
|
else
|
|
dest="Base/$lfile"
|
|
fi
|
|
elif [ -f "$pffdir/pff/$profile/$lfile" ]; then
|
|
dest="$profile/$lfile"
|
|
elif [ "$profile" != Common -a -f "$pffdir/pff/Common/$lfile" ]; then
|
|
dest="Common/$lfile"
|
|
else
|
|
dest="Base/$lfile"
|
|
fi
|
|
setx dest=multiups "Current/$lfile" "$dest"
|
|
[ -L "$src" ] || mkdirof "$src"
|
|
ln -sfT "$dest" "$src"
|
|
done
|
|
# maj du lien "profil courant"
|
|
ln -sfT "$profile" "$pffdir/pff/.Current"
|
|
}
|
|
|
|
function autoinit() {
|
|
# vérifications automatiques: créer les répertoires de base nécessaire au
|
|
# fonctionnement de pff dans le projet pff $1
|
|
local pffdir="$1" profile
|
|
[ -d "$pffdir/pff/Current" ] || mkdir -p "$pffdir/pff/Current"
|
|
[ -d "$pffdir/pff/Base" ] || mkdir -p "$pffdir/pff/Base"
|
|
# tous les fichiers du profil Base doivent être en lecture seule, sauf si
|
|
# NOUPSTREAM=1
|
|
if [ -z "$NOUPSTREAM" ]; then
|
|
find "$pffdir/pff/Base" -type f -perm /222 -exec chmod a-w '{}' +
|
|
fi
|
|
# Lancer les commandes AUTO_CMDS
|
|
local cwd cmd
|
|
if [ ${#AUTO_CMDS[*]} -gt 0 ]; then
|
|
etitle "AutoCmds"
|
|
cwd="$(pwd)"
|
|
cd "$pffdir"
|
|
for cmd in "${AUTO_CMDS[@]}"; do
|
|
estep "\$ $cmd"
|
|
eval "$cmd"
|
|
done
|
|
cd "$cwd"
|
|
eend
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function autoselect() {
|
|
# vérification automatiques: sélectionner le premier profil défini si aucun
|
|
# profil n'est sélectionné dans le projet pff $1
|
|
local pffdir="$1" profile
|
|
if [ ! -L "$pffdir/pff/.Current" ]; then
|
|
setx profile=get_first_profile "$pffdir"
|
|
[ -n "$profile" ] || profile=Base
|
|
enote "Autosélection du profil $profile"
|
|
select_profile "$profile" "$pffdir"
|
|
fi
|
|
}
|
|
|
|
function autofix() {
|
|
autoinit "$1"
|
|
autoselect "$1"
|
|
}
|
|
|
|
################################################################################
|
|
# Filtres
|
|
|
|
function pff_filter_normalize_properties() {
|
|
if [ $# -eq 0 ]; then
|
|
__norm_properties
|
|
else
|
|
local mode r
|
|
mode="$(fix_mode "$1")"
|
|
norm_properties "$1"; r=$?
|
|
unfix_mode "$1" "$mode"
|
|
return $r
|
|
fi
|
|
}
|
|
function pff_filter_nl2lf() {
|
|
if [ $# -eq 0 ]; then
|
|
_nl2lf
|
|
else
|
|
local mode r
|
|
mode="$(fix_mode "$1")"
|
|
doinplace "$1" _nl2lf; r=$?
|
|
unfix_mode "$1" "$mode"
|
|
return $r
|
|
fi
|
|
}
|
|
function pff_filter_nl2crlf() {
|
|
if [ $# -eq 0 ]; then
|
|
_nl2crlf
|
|
else
|
|
local mode r
|
|
mode="$(fix_mode "$1")"
|
|
doinplace "$1" _nl2crlf; r=$?
|
|
unfix_mode "$1" "$mode"
|
|
return $r
|
|
fi
|
|
}
|
|
function pff_filter_nl2cr() {
|
|
if [ $# -eq 0 ]; then
|
|
_nl2cr
|
|
else
|
|
local mode r
|
|
mode="$(fix_mode "$1")"
|
|
doinplace "$1" _nl2cr; r=$?
|
|
unfix_mode "$1" "$mode"
|
|
return $r
|
|
fi
|
|
}
|
|
|
|
function apply_filter() {
|
|
# Appliquer les filtres définis au fichier $1 dans le projet pff $2
|
|
# $3 est le nom du fichier sans préfixe, pour la sélection du filtre
|
|
# retourner 0 si un filtre a été appliqué avec succès, 1 si une erreur s'est
|
|
# produite, 2 si aucun filtre n'existe pour ce fichier
|
|
local pfile="$1" pffdir="$2"
|
|
local realfile="${3:-$pfile}"
|
|
local rfile ffile filter r=2 filedir filespec
|
|
setx rfile=get_rfile "$realfile" "$pffdir"
|
|
setx file=basename -- "$rfile" # utilisé pour le match sur le nom du fichier
|
|
for filter in "${FILTERS[@]}"; do
|
|
splitpair "$filter" ffile filter
|
|
if [[ "$ffile" == */* ]]; then
|
|
# matcher sur le chemin relatif
|
|
ffile="${ffile#/}"
|
|
if eval "[[ $(qval "$rfile") == $(qwc "$ffile") ]]"; then
|
|
"pff_filter_$filter" "$pfile" && r=0 || r=1
|
|
fi
|
|
else
|
|
# matcher uniquement sur le nom du fichier
|
|
if eval "[[ $(qval "$file") == $(qwc "$ffile") ]]"; then
|
|
"pff_filter_$filter" "$pfile" && r=0 || r=1
|
|
fi
|
|
fi
|
|
done
|
|
return $r
|
|
}
|
|
|
|
################################################################################
|
|
# Commandes
|
|
|
|
#===========================================================
|
|
# pff --init
|
|
|
|
function init_cmd() {
|
|
local workdir="$1" archive="$2"
|
|
local pffdir
|
|
|
|
if [ -z "$workdir" ]; then
|
|
estep "Initialisation d'un nouveau projet pff"
|
|
read_value "Veuillez entrer le répertoire du projet" workdir
|
|
fi
|
|
if find_pffdir pffdir "$workdir"; then
|
|
enote "$(ppath "$pffdir"): est déjà un projet pff"
|
|
if ask_yesno "Voulez-vous vérifier la configuration et la mettre à jour?" O; then
|
|
conf_init "${PFFCONFVARS[@]}"
|
|
source "$pffdir/$PFFCONF"
|
|
conf_upgrade "$workdir/$PFFCONF"
|
|
conf_update -n "$workdir/$PFFCONF" ORIGEXTS
|
|
fi
|
|
return 0
|
|
fi
|
|
if [ ! -d "$workdir" ]; then
|
|
ewarn "$workdir: ce répertoire n'existe pas"
|
|
ask_yesno "Voulez-vous le créer?" O || return 1
|
|
mkdir -p "$workdir" || return 1
|
|
fi
|
|
enote "Initialisation d'un nouveau projet pff dans $workdir"
|
|
# fichier de configuration
|
|
conf_upgrade "$workdir/$PFFCONF" "${PFFCONFVARS[@]}"
|
|
if [ "${ORIGEXTS[*]}" != "${DEFAULT_ORIGEXTS[*]}" ]; then
|
|
conf_update "$workdir/$PFFCONF" ORIGEXTS
|
|
fi
|
|
array_addu PROFILES Common
|
|
conf_update "$workdir/$PFFCONF" PROFILES
|
|
# répertoire pff
|
|
mkdir -p "$workdir/pff"
|
|
[ -f "$workdir/pff/.gitignore" ] || echo >"$workdir/pff/.gitignore" "\
|
|
/Current/
|
|
/.Current"
|
|
autofix "$workdir"
|
|
return 0
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --new
|
|
|
|
function new__prepare_archive() {
|
|
# préparer l'archive fournie en la copiant dans un répertoire temporaire
|
|
# initialiser la variable $1(=srcdir) avec le chemin du répertoire résultat
|
|
local destvar="${1:-srcdir}"; shift
|
|
local destver="${1:-version}"; shift
|
|
local archive="$1" version="$2" unwrap="$3"
|
|
|
|
local srcdir
|
|
if [ -f "$archive" ]; then
|
|
is_archive "$archive" || die "$archive: doit être un fichier archive"
|
|
[ -n "$version" ] || version="$(get_archive_version "$archive")"
|
|
archive="$(abspath "$archive")"
|
|
elif [ -d "$archive" ]; then
|
|
srcdir="$(abspath "$archive")"
|
|
archive=
|
|
else
|
|
die "$archive: fichier introuvable"
|
|
fi
|
|
|
|
read_value "Entrez un identifiant pour cette version" version "$version" || die
|
|
|
|
local tmpd
|
|
if [ -n "$archive" ]; then
|
|
if [ "$DISTTYPE" == auto ]; then
|
|
case "$archive" in
|
|
*.war)
|
|
enote "Auto-sélection du type distribution complète sur la base de l'extension .war"
|
|
DISTTYPE=full
|
|
;;
|
|
*.zip)
|
|
enote "Auto-sélection du type distribution de patch sur la base de l'extension .zip"
|
|
DISTTYPE=patch
|
|
;;
|
|
*.sh)
|
|
die "Ce type d'archive ne contient pas l'information du type de distribution. Vous devez spécifier l'une des options -F ou -H"
|
|
;;
|
|
*)
|
|
die "L'extension de l'archive n'est pas reconnue. Vous devez spécifier l'une des options -F ou -H"
|
|
;;
|
|
esac
|
|
fi
|
|
local -a tmpargs
|
|
case "$archive" in
|
|
*.sh) tmpargs=(--exclude cleanup.sh --exclude install.sh);;
|
|
esac
|
|
|
|
ac_set_tmpdir tmpd
|
|
estep "Extraction de $(ppath "$archive" "$cwd")"
|
|
extract_archive "$archive" "$tmpd" "${tmpargs[@]}" || die
|
|
srcdir="$tmpd"
|
|
|
|
if [ -n "$unwrap" ]; then
|
|
local -a files
|
|
array_lsall files "$srcdir" "*" ".*"
|
|
if [ "${#files[*]}" -eq 1 ]; then
|
|
local file="${files[0]}"
|
|
if [ "$unwrap" == auto ]; then
|
|
# nom de l'archive avec la version
|
|
local banv="$(get_archive_basename "$archive")"
|
|
# nom de l'archive sans la version
|
|
local ban="${banv%$(get_archive_versionsuffix "$archive")}"
|
|
local filename="$(basename "$file")"
|
|
if [ "$filename" == "$banv" -o "$filename" == "$ban" ]; then
|
|
# le nom du répertoire correspond au nom de l'archive. cool!
|
|
unwrap=1
|
|
elif [ "${filename%-$version}" == "$ban" ]; then
|
|
# le nom du répertoire n'est pas contruit correctement
|
|
# mais ce n'est pas grave (e.g ban-VERSION au lieu de
|
|
# banVERSION comme l'archive)
|
|
unwrap=1
|
|
else
|
|
unwrap=
|
|
fi
|
|
fi
|
|
[ -n "$unwrap" -a -d "$file" ] && srcdir="$file"
|
|
fi
|
|
fi
|
|
elif [ -d "$srcdir" ]; then
|
|
if [ "$DISTTYPE" == auto ]; then
|
|
die "La source est un répertoire. Vous devez spécifier l'une des option -F ou -H"
|
|
fi
|
|
|
|
ac_set_tmpdir tmpd
|
|
estep "Création d'une copie de travail de $(ppath "$srcdir" "$cwd")"
|
|
rsync ${QUIET:+-q} -a "$srcdir/" "$tmpd" || die
|
|
srcdir="$tmpd"
|
|
fi
|
|
|
|
local "$destvar"; upvar "$destvar" "$srcdir"
|
|
local "$destver"; upvar "$destver" "$version"
|
|
}
|
|
|
|
function new_cmd_amue_clean() {
|
|
# en mode distribution "patch", supprimer le contenu des répertoires
|
|
# spécifiés s'ils sont relivrés
|
|
[ "$DISTTYPE" == patch ] || return
|
|
[ $# -gt 0 ] || set -- WEB-INF/{lib,classes/{gouv,com,org}} wsdl
|
|
local dir
|
|
for dir in "$@"; do
|
|
if [ -n "$(/bin/ls -d "$srcdir/$dir"/* 2>/dev/null)" ]; then
|
|
eval "rm -rf $dir/*"
|
|
fi
|
|
done
|
|
}
|
|
# compat
|
|
function new_cmd_amue_clean_libs() { new_cmd_amue_clean WEB-INF/lib; }
|
|
function new_cmd_amue_clean_wsdl() { new_cmd_amue_clean wsdl; }
|
|
|
|
function new_cmd() {
|
|
local autopatch="$1" version="$2" disttype="$3" unwrap="$4" merge_strategy="$5" commit_policy="$6"; shift; shift; shift; shift; shift; shift
|
|
local archive="$1" pffdir="$2"
|
|
|
|
ensure_pffdir pffdir "$pffdir"
|
|
[ -n "$disttype" ] && DISTTYPE="$disttype"
|
|
|
|
## préparer la source: les fichiers entrants
|
|
local srcdir full
|
|
new__prepare_archive srcdir version "$archive" "$version" "$unwrap"
|
|
[ "$DISTTYPE" == full ] && full=1
|
|
|
|
## Lancer les commandes
|
|
local cwd cmd
|
|
if [ ${#NEW_CMDS[*]} -gt 0 ]; then
|
|
etitle "NewCmds"
|
|
cwd="$(pwd)"
|
|
cd "$pffdir"
|
|
for cmd in "${NEW_CMDS[@]}"; do
|
|
estep "\$ $cmd"
|
|
eval "$cmd"
|
|
done
|
|
cd "$cwd"
|
|
eend
|
|
fi
|
|
|
|
## lister les fichiers dans la source et la destination
|
|
local workdir
|
|
ac_set_tmpdir workdir
|
|
|
|
# fichiers sources
|
|
local fnsrc0="$workdir/nsrc0" fosrc0="$workdir/osrc0"
|
|
>"$fnsrc0"
|
|
>"$fosrc0"
|
|
find "$srcdir" -type f | awkrun -f ORIGEXTS[@] prefix="$srcdir/" fnsrc="$fnsrc0" fosrc="$fosrc0" '{
|
|
found = 0
|
|
for (i = 1; i <= ORIGEXTS_count; i++) {
|
|
sub("^" prefix, "")
|
|
re_origext = qregexp(ORIGEXTS[i]) "(/|$)"
|
|
if ($0 ~ re_origext) {
|
|
print >fosrc
|
|
found = 1
|
|
break
|
|
}
|
|
}
|
|
if (!found) {
|
|
print >fnsrc
|
|
}
|
|
}'
|
|
# fichiers destination
|
|
local -a find; local p
|
|
local fndest0="$workdir/ndest0" fldest0="$workdir/ldest0"
|
|
get_local_files "$pffdir" >"$fldest0"
|
|
for p in "${PROTECTS[@]}" "${DEFAULT_PROTECTS[@]}"; do
|
|
[ ${#find[*]} -gt 0 ] && find=("${find[@]}" -o)
|
|
if [ "${p#/}" != "$p" ]; then
|
|
# les fichiers à la racine
|
|
p="${p#/}"
|
|
if [ "${p%/}" != "$p" ]; then
|
|
find=("${find[@]}" -type d -path "$pffdir/${p%/}" -prune)
|
|
else
|
|
find=("${find[@]}" -type f -path "$pffdir/$p" -o -type l -path "$pffdir/$p")
|
|
fi
|
|
else
|
|
# fichiers n'importe où dans l'arborescence
|
|
if [ "${p%/}" != "$p" ]; then
|
|
find=("${find[@]}" -type d -name "${p%/}" -prune)
|
|
else
|
|
find=("${find[@]}" -type f -name "$p" -o -type l -name "$p")
|
|
fi
|
|
fi
|
|
done
|
|
[ ${#find[*]} -gt 0 ] && find=("${find[@]}" -o)
|
|
find=(find "$pffdir" "${find[@]}" -type f -print -o -type l -print)
|
|
"${find[@]}" | sed "s|^$pffdir/||" | grep -vxf "$fldest0" >"$fndest0"
|
|
|
|
## Traiter les fichiers normaux
|
|
|
|
local fnsrc="$workdir/nsrc" fndest="$workdir/ndest"
|
|
grep -vxf "$fldest0" "$fnsrc0" | csort >"$fnsrc"
|
|
csort "$fndest0" >"$fndest"
|
|
|
|
local fcreates="$workdir/creates" fdeletes="$workdir/deletes" fcds="$workdir/cds" fupdates="$workdir/updates"
|
|
>"$fcreates"
|
|
>"$fdeletes"
|
|
diff "$fnsrc" "$fndest" | awkrun fcreates="$fcreates" fupdates="$fupdates" fdeletes="$fdeletes" '{
|
|
c = substr($0, 1, 1)
|
|
f = substr($0, 3)
|
|
if (c == "<") print f >fcreates
|
|
else if (c == ">") print f >fdeletes
|
|
}'
|
|
cat "$fcreates" "$fdeletes" >"$fcds"
|
|
grep -vxf "$fcds" "$fnsrc" >"$fupdates"
|
|
|
|
local -a creates updates deletes r i have_creates have_updates have_deletes src dest
|
|
array_from_lines creates "$(<"$fcreates")"; [ ${#creates[*]} -gt 0 ] && have_creates=1 || have_creates=
|
|
array_from_lines updates "$(<"$fupdates")"; [ ${#updates[*]} -gt 0 ] && have_updates=1 || have_updates=
|
|
array_from_lines deletes "$(<"$fdeletes")"; [ ${#deletes[*]} -gt 0 ] && have_deletes=1 || have_deletes=
|
|
enote "Fichiers normaux: $((${#creates[*]} + ${#updates[*]})) au total, ${#creates[*]} nouveau(x)${full:+", ${#deletes[*]} à supprimer"}"
|
|
ask_any -i "Voulez-vous continuer?" +Od; r=$?
|
|
if [ $r == 2 ]; then
|
|
for i in "${updates[@]}"; do
|
|
echo " $i"
|
|
done
|
|
for i in "${creates[@]}"; do
|
|
echo "+$i"
|
|
done
|
|
if [ -n "$full" ]; then
|
|
for i in "${deletes[@]}"; do
|
|
echo "-$i"
|
|
done
|
|
fi
|
|
ask_yesno "Voulez-vous continuer?" O || return 1
|
|
elif [ $r == 1 ]; then
|
|
return 1
|
|
fi
|
|
|
|
if [ -n "$have_creates" -o -n "$have_updates" -o -n "$have_deletes" ]; then
|
|
ebegin "Copie des fichiers"
|
|
r=0
|
|
for i in "${creates[@]}"; do
|
|
src="$srcdir/$i"
|
|
dest="$pffdir/$i"
|
|
mkdirof "$dest"; r=$?
|
|
edot $r "mkdirof $i"; [ $r -eq 0 ] || break
|
|
cp "$src" "$dest"; r=$?
|
|
edot $r "create $i"; [ $r -eq 0 ] || break
|
|
apply_filter "$dest" "$pffdir"; r=$?
|
|
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
|
|
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
|
|
done
|
|
[ $r -eq 0 ] || { eend $r; return 1; }
|
|
for i in "${updates[@]}"; do
|
|
src="$srcdir/$i"
|
|
dest="$pffdir/$i"
|
|
if diff -q "$src" "$dest" >&/dev/null; then
|
|
# pas la peine de copier si les fichiers sont identiques
|
|
show_debug && edot 0 "$i: non modifié"
|
|
continue
|
|
fi
|
|
cat "$src" >"$dest"; r=$?
|
|
edot $r "update $i"; [ $r -eq 0 ] || break
|
|
apply_filter "$dest" "$pffdir"; r=$?
|
|
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
|
|
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
|
|
done
|
|
[ $r -eq 0 ] || { eend $r; return 1; }
|
|
if [ -n "$full" ]; then
|
|
# ne faire les suppression qu'en mode full
|
|
for i in "${deletes[@]}"; do
|
|
rm "$pffdir/$i"; r=$?
|
|
edot $r "delete $i"; [ $r -eq 0 ] || break
|
|
done
|
|
fi
|
|
eend $r
|
|
fi
|
|
|
|
## Traiter les fichiers origines
|
|
|
|
local ftmp="$workdir/tmp"
|
|
local fosrc="$workdir/osrc" flosrc="$workdir/losrc" flsrc="$workdir/lsrc" fldest="$workdir/ldest"
|
|
csort "$fosrc0" >"$fosrc"
|
|
>"$flsrc"
|
|
>"$flosrc"
|
|
<"$fosrc0" awkrun -f ORIGEXTS[@] flsrc="$flsrc" flosrc="$flosrc" '{
|
|
for (i = 1; i <= ORIGEXTS_count; i++) {
|
|
re_origext = qregexp(ORIGEXTS[i]) "(/|$)"
|
|
if ($0 ~ re_origext) {
|
|
orig = $0
|
|
local = gensub(re_origext, "\\1", 1, $0)
|
|
print local ":" orig >flosrc
|
|
print local >flsrc
|
|
break
|
|
}
|
|
}
|
|
}'
|
|
csort "$flsrc" >"$ftmp"; cat "$ftmp" >"$flsrc"
|
|
csort "$fldest0" >"$fldest"
|
|
|
|
local losrc lsrc osrc; local -a tmpa; declare -A losrcs
|
|
array_from_lines tmpa "$(<"$flosrc")"
|
|
for losrc in "${tmpa[@]}"; do
|
|
splitpair "$losrc" lsrc osrc
|
|
losrcs["$lsrc"]="$osrc"
|
|
done
|
|
|
|
>"$fcreates"
|
|
>"$fdeletes"
|
|
diff "$flsrc" "$fldest" | awkrun fcreates="$fcreates" fupdates="$fupdates" fdeletes="$fdeletes" '{
|
|
c = substr($0, 1, 1)
|
|
f = substr($0, 3)
|
|
if (c == "<") print f >fcreates
|
|
else if (c == ">") print f >fdeletes
|
|
}'
|
|
# contrairement aux fichiers normaux, ajouter le contenu de fdeletes à fupdates
|
|
# les fichiers de fdeletes sont des fichiers locaux non identifiés comme tels dans l'origine
|
|
{ grep -vxf "$fcreates" "$flsrc"; cat "$fdeletes"; } >"$fupdates"
|
|
|
|
array_from_lines creates "$(<"$fcreates")"; [ ${#creates[*]} -gt 0 ] && have_creates=1 || have_creates=
|
|
array_from_lines updates "$(<"$fupdates")"; [ ${#updates[*]} -gt 0 ] && have_updates=1 || have_updates=
|
|
enote "Fichiers origines: $((${#creates[*]} + ${#updates[*]})) au total, ${#creates[*]} nouveau(x)"
|
|
ask_any -i "Voulez-vous continuer?" +Od; r=$?
|
|
if [ $r == 2 ]; then
|
|
for i in "${updates[@]}"; do
|
|
echo " $i"
|
|
done
|
|
for i in "${creates[@]}"; do
|
|
echo "+$i"
|
|
done
|
|
ask_yesno "Voulez-vous continuer?" O || return 1
|
|
elif [ $r == 1 ]; then
|
|
return 1
|
|
fi
|
|
|
|
local vlfile; local -a vlfiles
|
|
if [ -n "$have_creates" -o -n "$have_updates" ]; then
|
|
ebegin "Copie des fichiers"
|
|
r=0
|
|
for i in "${creates[@]}"; do
|
|
src="$srcdir/${losrcs[$i]}"
|
|
dest="$pffdir/$i"
|
|
mkdirof "$dest"; r=$?
|
|
edot $r "mkdirof $i"; [ $r -eq 0 ] || break
|
|
cp "$src" "$dest"; r=$?
|
|
edot $r "create $i"; [ $r -eq 0 ] || break
|
|
apply_filter "$dest" "$pffdir"; r=$?
|
|
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
|
|
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
|
|
add_global__link "$dest" "$pffdir"
|
|
edot $r "add_global $i"; [ $r -eq 0 ] || break
|
|
done
|
|
[ $r -eq 0 ] || { eend $r; return 1; }
|
|
for i in "${updates[@]}"; do
|
|
# du fait qu'on intègre fdeletes dans fupdates, il est possible que
|
|
# le fichier n'existe pas dans la source. de plus, l'association
|
|
# dans losrcs n'existe pas non plus. dans ce cas, il suffit de
|
|
# d'ignorer le fichier
|
|
if [ -n "${losrcs[$i]+set}" ]; then
|
|
src="$srcdir/${losrcs[$i]}"
|
|
else
|
|
src="$srcdir/$i"
|
|
fi
|
|
setx vlfile=add_pv "$i" "$version"
|
|
dest="$pffdir/pff/Base/$vlfile"
|
|
if flexists "$src"; then
|
|
mkdirof "$dest"; r=$?
|
|
edot $r "mkdirof $vlfile"; [ $r -eq 0 ] || break
|
|
cp "$src" "$dest"; r=$?
|
|
edot $r "create $vlfile"; [ $r -eq 0 ] || break
|
|
apply_filter "$dest" "$pffdir" "$pffdir/pff/Base/$i"; r=$?
|
|
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $vlfile"
|
|
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
|
|
array_add vlfiles "$vlfile"
|
|
fi
|
|
done
|
|
[ $r -eq 0 ] || { eend $r; return 1; }
|
|
eend $r
|
|
fi
|
|
|
|
sync_vlfiles "$pffdir" "" "${vlfiles[@]}"
|
|
array_addu PVERSIONS "$version"
|
|
conf_update "$pffdir/$PFFCONF" PVERSIONS
|
|
|
|
## Fin du traitement
|
|
ac_clean "$srcdir" "$workdir"
|
|
|
|
if [ -n "$autopatch" ]; then
|
|
# lancer la commande patch pour intégrer les modifications
|
|
ask_yesno "Voulez-vous passer à l'intégration de cette version?" O &&
|
|
patch_cmd "$merge_strategy" "$commit_policy" "$pffdir"
|
|
fi
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --patch
|
|
|
|
function patch_cmd() {
|
|
local merge_strategy="$1" commit_policy="$2"; shift; shift
|
|
local pffdir="$1"
|
|
local gitproject was_patched eop_version
|
|
local version profile rcfile curdir workdir controlfile control tmpfile
|
|
local -a profiles vlfiles tmpfiles
|
|
|
|
ensure_pffdir pffdir "$pffdir"
|
|
|
|
# est-ce un projet suivi dans git?
|
|
(cd "$pffdir"; git_check_gitvcs) && gitproject=1 || gitproject=
|
|
|
|
array_from_lines profiles "$(get_user_profiles "$pffdir")"
|
|
if array_contains profiles Common; then
|
|
# toujours traiter Common en dernier
|
|
array_del profiles Common
|
|
array_add profiles Common
|
|
fi
|
|
|
|
case "$merge_strategy" in
|
|
ours) merge_strategy=(-s recursive -X ours);;
|
|
theirs) merge_strategy=(-s recursive -X theirs);;
|
|
*) merge_strategy=();;
|
|
esac
|
|
|
|
while true; do
|
|
# si pas de version en cours, il n'y a rien à patcher
|
|
[ ${#PVERSIONS[*]} -gt 0 ] || break
|
|
|
|
eop_version=
|
|
for version in "${PVERSIONS[@]}"; do
|
|
have_profile_vlfiles=
|
|
have_base_vlfiles=
|
|
for profile in "${profiles[@]}"; do
|
|
array_from_lines vlfiles "$(get_vlfiles_nostrip "$pffdir" "" "$profile" "$version")"
|
|
# filtrer les fichiers de NOMERGES
|
|
mergefiles=()
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx rfile=strip_pv "$vlfile" "$version"
|
|
setx rfile=get_rfile "$rfile" "$pffdir"
|
|
is_nomerge "$rfile" "$pffdir" && continue
|
|
array_add mergefiles "$vlfile"
|
|
done
|
|
vlfiles=("${mergefiles[@]}")
|
|
# on en a trouvé
|
|
if [ ${#vlfiles[*]} -gt 0 ]; then
|
|
have_profile_vlfiles=1
|
|
break
|
|
fi
|
|
done
|
|
[ -n "$have_profile_vlfiles" ] && break
|
|
array_from_lines vlfiles "$(get_vlfiles_nostrip "$pffdir" "" Base "$version")"
|
|
# filtrer les fichiers de NOMERGES
|
|
mergefiles=()
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx rfile=strip_pv "$vlfile" "$version"
|
|
setx rfile=get_rfile "$rfile" "$pffdir"
|
|
is_nomerge "$rfile" "$pffdir" && continue
|
|
array_add mergefiles "$vlfile"
|
|
done
|
|
vlfiles=("${mergefiles[@]}")
|
|
have_base_vlfiles=1
|
|
break
|
|
done
|
|
if [ -n "$have_profile_vlfiles" ]; then
|
|
# il faut patcher les fichiers du profil
|
|
etitle "Intégration de la version $version dans le profil $profile"
|
|
[ -n "$rcfile" ] || ac_set_tmpfile rcfile
|
|
[ -n "$controlfile" ] || ac_set_tmpfile controlfile
|
|
[ -n "$workdir" ] && ac_clean "$workdir"
|
|
ac_set_tmpdir workdir
|
|
curdir="$(pwd)"
|
|
cd "$workdir"
|
|
git init -q .
|
|
git checkout -q --orphan upstream
|
|
# rajouter les fichiers de Base dans la branche upstream
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx bfile=strip_pv "$vlfile" "$version"
|
|
setx bfile=get_bfile "$bfile" "$pffdir"
|
|
setx rfile=get_rfile "$bfile" "$pffdir"
|
|
mkdirof "$rfile"
|
|
cp -a "$bfile" "$rfile"
|
|
[ -z "$NOUPSTREAM" ] && chmod +w "$rfile"
|
|
done
|
|
git add -A
|
|
[ -n "$(git status --porcelain)" ] && git commit -qm "Base"
|
|
# créer la branche v$version depuis upstream
|
|
git checkout -qb "v$version"
|
|
# rajouter les fichiers de Common s'ils existent (sauf si la branche c'est Common ^^)
|
|
if [ "$profile" != Common ]; then
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx Cfile=strip_pv "$vlfile" "$version"
|
|
setx Cfile=get_Cfile "$Cfile" "$pffdir"
|
|
if flexists "$Cfile"; then
|
|
setx rfile=get_rfile "$Cfile" "$pffdir"
|
|
mkdirof "$rfile"
|
|
cp -a "$Cfile" "$rfile"
|
|
fi
|
|
done
|
|
git add -A
|
|
[ -n "$(git status --porcelain)" ] && git commit -qm "Common"
|
|
fi
|
|
# rajouter les fichiers du profil
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx pfile=strip_pv "$vlfile" "$version"
|
|
setx pfile=get_pfile "$pfile" "$profile" "$pffdir"
|
|
setx rfile=get_rfile "$pfile" "$pffdir"
|
|
mkdirof "$rfile"
|
|
cp -a "$pfile" "$rfile"
|
|
done
|
|
git add -A
|
|
[ -n "$(git status --porcelain)" ] && git commit -qm "$profile"
|
|
# rebasculer vers upstream et rajouter les fichiers de patch
|
|
git checkout -q upstream
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx rfile=strip_pv "$vlfile" "$version"
|
|
setx rfile=get_rfile "$rfile" "$pffdir"
|
|
mkdirof "$rfile"
|
|
cp -L "$vlfile" "$rfile"
|
|
done
|
|
git add -A
|
|
[ -n "$(git status --porcelain)" ] && git commit -qm "$version"
|
|
# basculer vers la branche de version et tenter de merger upstream dans version
|
|
git checkout -q "v$version"
|
|
if git merge -q "${merge_strategy[@]}" --no-commit upstream; then
|
|
# tout s'est bien passé
|
|
[ -n "$(git status --porcelain)" ] && git commit -qm "v$version --> $profile"
|
|
else
|
|
# il y a eu une erreur. laisser l'utilisateur décider quoi faire
|
|
echo >"$rcfile" "#
|
|
[ -f /etc/bash.bashrc ] && . /etc/bash.bashrc
|
|
[ -f ~/.bashrc ] && . ~/.bashrc
|
|
$(qvals source "$ULIBDIR/ulib")
|
|
urequire DEFAULTS
|
|
function Commit() { echo COMMIT >$(qvals "$controlfile"); exit; }
|
|
# raccourcis pour Commit
|
|
function C() { Commit \"\$@\"; }; function c() { Commit \"\$@\"; }; function commit() { Commit \"\$@\"; }
|
|
function Abort() { echo ABORT >$(qvals "$controlfile"); exit; }
|
|
# raccourcis pour Abort
|
|
function A() { Abort \"\$@\"; }; function a() { Abort \"\$@\"; }; function abort() { Abort \"\$@\"; }
|
|
$(qvals cd "$workdir")
|
|
$(qvals eerror "Une erreur s'est produite: consultez les fichiers concernés et faites les corrections nécessaires. En cas de conflit, vous devez les résoudre en examinant les lignes marquées
|
|
<<<<<<< HEAD
|
|
=======
|
|
>>>>>>> upstream
|
|
puis en gardant la partie HEAD pour les modifications locales et upstream pour les modifications entrantes")
|
|
$(qvals eimportant "Puis tapez ${COULEUR_VERTE}Commit${COULEUR_NORMALE} pour valider l'intégration de cette version")
|
|
$(qvals eimportant "Sinon, tapez ${COULEUR_ROUGE}Abort${COULEUR_NORMALE} pour arrêter l'intégration de cette version")
|
|
"
|
|
>"$controlfile"
|
|
"${SHELL:-bash}" --rcfile "$rcfile"
|
|
control="$(<"$controlfile")"
|
|
if [ -z "$control" ]; then
|
|
# demander à l'utilisateur sa décision
|
|
eerror "Vous n'avez pas terminé votre session par ${COULEUR_VERTE}Commit${COULEUR_NORMALE} ou ${COULEUR_ROUGE}Abort${COULEUR_NORMALE}"
|
|
ask_yesno "Voulez-vous valider l'intégration de la version?" N && control=COMMIT || control=ABORT
|
|
fi
|
|
if [ "$control" == COMMIT ]; then
|
|
array_from_lines tmpfiles "$(git status --porcelain | awk '{print substr($0, 4)}')"
|
|
for tmpfile in "${tmpfiles[@]}"; do
|
|
if grep -qE '^(<<<<<<<|=======|>>>>>>>)' "$tmpfile"; then
|
|
ewarn "Vous avez choisi de valider l'intégration des modifications mais..."
|
|
ewarn "$tmpfile: ce fichier semble encore contenir des marques de conflit"
|
|
if ! ask_yesno -y "Etes-vous sûr d'avoir correctement édité le fichier?" N; then
|
|
eecho "Annulation de l'intégration de la version"
|
|
control=ABORT
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
if [ "$control" == ABORT ]; then
|
|
enote "Vous pouvez reprendre l'intégration de la version $version avec la commande pff -g"
|
|
break # abort
|
|
fi
|
|
fi
|
|
# récupérer les versions modifiées et supprimer les fichiers de patch
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx pfile=strip_pv "$vlfile" "$version"
|
|
setx pfile=get_pfile "$pfile" "$profile" "$pffdir"
|
|
setx rfile=get_rfile "$pfile" "$pffdir"
|
|
cp -a "$rfile" "$pfile"
|
|
rm "$vlfile"
|
|
done
|
|
cd "$curdir"
|
|
eend
|
|
elif [ -n "$have_base_vlfiles" ]; then
|
|
# il faut intégrer la nouvelle version dans Base
|
|
etitle "Finaliser intégration de la version $version"
|
|
for vlfile in "${vlfiles[@]}"; do
|
|
setx bfile=strip_pv "$vlfile" "$version"
|
|
[ -z "$NOUPSTREAM" ] && chmod +w "$bfile"
|
|
mv "$vlfile" "$bfile"
|
|
[ -z "$NOUPSTREAM" ] && chmod a-w "$bfile"
|
|
done
|
|
eop_version=1
|
|
VERSION="$version"
|
|
array_del PVERSIONS "$VERSION"
|
|
conf_update "$pffdir/$PFFCONF" VERSION PVERSIONS
|
|
eend
|
|
fi
|
|
|
|
if [ -n "$gitproject" ]; then
|
|
local commit default
|
|
if [ -n "$eop_version" ]; then
|
|
msg="Intégration de la version $version"
|
|
else
|
|
msg="Correction du profil $profile pour la version $version"
|
|
fi
|
|
commit="$commit_policy"
|
|
if [ "$commit" == ask ]; then
|
|
if [ -n "$eop_version" ]; then
|
|
enote "Vous avez terminé l'intégration des patches de la version $version"
|
|
default=O
|
|
else
|
|
einfo "Vous avez intégré les patches de la version $version pour le profil $profile"
|
|
default=N
|
|
fi
|
|
ask_yesno "Voulez-vous enregistrer les modifications dans git?" $default || commit=
|
|
fi
|
|
if [ -n "$commit" ]; then
|
|
git add -A && git commit -m "$msg" || return
|
|
if [ -z "$UTOOLS_VCS_OFFLINE" -a -n "$(git_get_branch_remote)" ]; then
|
|
git push
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --add-global
|
|
|
|
function add_global__link() {
|
|
local pfile="$1" pffdir="$2"
|
|
local rfile bfile cfile plink clink tmp
|
|
|
|
setx rfile=get_rfile "$pfile" "$pffdir" || return
|
|
flexists "$pffdir/$rfile" || touch "$pffdir/$rfile"
|
|
|
|
setx bfile=get_bfile "$pfile" "$pffdir"
|
|
setx cfile=get_cfile "$pfile" "$pffdir"
|
|
setx plink=multiups "$rfile" "pff/Current/$rfile"
|
|
setx clink=multiups "Current/$rfile" "Base/$rfile"
|
|
if flexists "$bfile" && [ -L "$pfile" -a -L "$cfile" ]; then
|
|
: # ok pour les liens
|
|
else
|
|
# Création des liens pour $rfile
|
|
mkdirof "$bfile" || return
|
|
mv "$pfile" "$bfile" || return
|
|
if [ -z "$NOUPSTREAM" ]; then
|
|
chmod a-w "$bfile" || return
|
|
fi
|
|
[ -L "$pfile" ] || ln -sf "$plink" "$pfile" || return
|
|
mkdirof "$cfile" || return
|
|
[ -L "$cfile" ] || ln -sf "$clink" "$cfile" || return
|
|
fi
|
|
# correction éventuelle du lien existant
|
|
setx tmp=readlink "$pfile"
|
|
[ "$tmp" == "$plink" ] || ln -sfT "$plink" "$pfile"
|
|
return 0
|
|
}
|
|
|
|
function add_global_cmd() {
|
|
local pffdir file pfile
|
|
|
|
ensure_pffdir pffdir
|
|
for file in "$@"; do
|
|
if [ -d "$file" ]; then
|
|
ewarn "$file: est un répertoire. argument ignoré"
|
|
continue
|
|
elif flexists "$file"; then
|
|
:
|
|
else
|
|
ewarn "$file: fichier introuvable"
|
|
ask_yesno "Voulez-vous le créer?" O || continue
|
|
fi
|
|
setx pfile=abspath "$file"
|
|
setx rfile=get_rfile "$pfile" || {
|
|
eerror "$file: chemin invalide. argument ignoré"
|
|
continue
|
|
}
|
|
add_global__link "$pfile" "$pffdir" || return
|
|
done
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --locals
|
|
|
|
function list_locals_cmd() {
|
|
local pffdir="$1"
|
|
|
|
ensure_pffdir pffdir "$pffdir"
|
|
get_local_files "$pffdir" | sed 's/\/.pffdir$/\//'
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --profiles
|
|
|
|
function list_profiles_cmd() {
|
|
local pffdir="$1"
|
|
|
|
ensure_pffdir pffdir "$pffdir"
|
|
get_profiles "$pffdir"
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --switch
|
|
|
|
function switch_cmd() {
|
|
local profile="$1" pffdir="$2"
|
|
local -a profiles
|
|
|
|
ensure_pffdir pffdir "$pffdir"
|
|
autoinit "$pffdir"
|
|
[ -n "$profile" ] || setx profile=get_first_profile "$pffdir"
|
|
|
|
setx -a profiles=get_profiles "$pffdir"
|
|
if ! array_contains profiles "$profile"; then
|
|
if ! array_contains PROFILES "$profile"; then
|
|
ewarn "$profile: ce profil n'existe pas"
|
|
ask_yesno "Voulez-vous le créer?" O || return 1
|
|
|
|
array_addu PROFILES "$profile"
|
|
conf_update "$pffdir/$PFFCONF" PROFILES
|
|
fi
|
|
fi
|
|
enote "Sélection du profil $profile"
|
|
select_profile "$profile" "$pffdir"
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --add-local
|
|
|
|
function add_local__link() {
|
|
local pfile="$1" profile="$2" pffdir="$3"
|
|
local rfile bfile pfile Pfile plink Plink
|
|
# vérifier validité du chemin
|
|
setx rfile=get_rfile "$pfile" "$pffdir" || return
|
|
flexists "$pffdir/$rfile" || touch "$pffdir/$rfile"
|
|
# vérifier la présence du lien global
|
|
setx bfile=get_bfile "$pfile" "$pffdir"
|
|
if ! flexists "$bfile"; then
|
|
add_global__link "$pfile" "$pffdir" || return
|
|
fi
|
|
# vérifier la présence du lien local
|
|
setx Pfile=get_pfile "$pfile" "$profile" "$pffdir"
|
|
flexists "$Pfile" && return 0
|
|
# créer le fichier local
|
|
setx Cfile=get_Cfile "$pfile" "$pffdir"
|
|
mkdirof "$Pfile"
|
|
if flexists "$Cfile"; then
|
|
# copier depuis le profil Common par défaut
|
|
cp "$Cfile" "$Pfile"
|
|
else
|
|
cp "$bfile" "$Pfile"
|
|
[ -z "$NOUPSTREAM" ] && chmod +w "$Pfile"
|
|
fi
|
|
# mettre à jour le profil courant
|
|
setx cfile=get_cfile "$pfile" "$pffdir"
|
|
setx clink=multiups "Current/$rfile" "$profile/$rfile"
|
|
ln -sf "$clink" "$cfile"
|
|
# intégrer les fichiers de version
|
|
local -a vlfiles
|
|
array_from_lines vlfiles "$(get_vlfiles "$pffdir" "$rfile")"
|
|
sync_vlfiles "$pffdir" "$profile" "${vlfiles[@]}"
|
|
return 0
|
|
}
|
|
|
|
function add_local_cmd() {
|
|
local pffdir file rfile pfile
|
|
local profile r
|
|
|
|
ensure_pffdir pffdir
|
|
setx profile=get_current_profile "$pffdir"
|
|
r=0
|
|
for file in "$@"; do
|
|
if [ -d "$file" ]; then
|
|
ewarn "$file: est un répertoire. argument ignoré"
|
|
continue
|
|
elif flexists "$file"; then
|
|
:
|
|
else
|
|
ewarn "$file: fichier introuvable"
|
|
ask_yesno "Voulez-vous le créer?" O || continue
|
|
fi
|
|
setx pfile=abspath "$file"
|
|
setx rfile=get_rfile "$pfile" || {
|
|
eerror "$file: chemin invalide. argument ignoré"
|
|
r=1
|
|
continue
|
|
}
|
|
add_local__link "$pfile" "$profile" "$pffdir" || {
|
|
ewarn "$file: erreur lors de la création du lien local"
|
|
r=1
|
|
continue
|
|
}
|
|
done
|
|
return $r
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --edit
|
|
|
|
function edit_cmd() {
|
|
local profile="$1"; shift
|
|
local pffdir file rfile pfile Pfile r
|
|
local -a profiles edits args
|
|
|
|
ensure_pffdir pffdir
|
|
array_from_lines profiles "$(get_user_profiles "$pffdir")"
|
|
[ -n "$profile" ] || setx profile=get_current_profile "$pffdir"
|
|
enote "Dans le profil $profile:"
|
|
|
|
r=0
|
|
while [ $# -gt 0 ]; do
|
|
if [[ "$1" == -* ]]; then
|
|
# nouvelle option
|
|
local prev_profile="$profile"
|
|
args=(+ -p:,--profile: profile=)
|
|
parse_args "$@"; set -- "${args[@]}"
|
|
[ "$profile" != "$prev_profile" ] && enote "Dans le profil $profile:"
|
|
continue
|
|
fi
|
|
file="$1"; shift
|
|
|
|
if [ -d "$file" ]; then
|
|
ewarn "$file: est un répertoire. argument ignoré"
|
|
continue
|
|
fi
|
|
setx pfile=abspath "$file"
|
|
setx rfile=get_rfile "$pfile" || {
|
|
eerror "$file: chemin invalide. argument ignoré"
|
|
r=1
|
|
continue
|
|
}
|
|
if [ "$profile" == ALL ]; then
|
|
local tmpp
|
|
for tmpp in "${profiles[@]}"; do
|
|
setx Pfile=get_pfile "$pfile" "$tmpp" "$pffdir"
|
|
[ -e "$Pfile" ] || continue
|
|
estep "Edition de $(ppath "$Pfile")"
|
|
array_add edits "$Pfile"
|
|
done
|
|
else
|
|
add_local__link "$pfile" "$profile" "$pffdir" || {
|
|
ewarn "$file: erreur lors de la création du lien local"
|
|
r=1
|
|
continue
|
|
}
|
|
setx Pfile=get_pfile "$pfile" "$profile" "$pffdir"
|
|
estep "Edition de $(ppath "$Pfile")"
|
|
array_add edits "$Pfile"
|
|
fi
|
|
done
|
|
|
|
if [ ${#edits[*]} -gt 0 ]; then
|
|
"${EDITOR:-vi}" "${edits[@]}" || r=$?
|
|
else
|
|
r=2
|
|
fi
|
|
return $r
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --diff
|
|
|
|
function diff_cmd() {
|
|
local list_names="$1"; shift
|
|
|
|
local pffdir profile srcp destp desc
|
|
case $# in
|
|
0)
|
|
ensure_pffdir pffdir
|
|
setx srcp=get_current_profile "$pffdir"
|
|
destp=
|
|
;;
|
|
1)
|
|
if withpath "$1"; then
|
|
ensure_pffdir pffdir "$1"
|
|
setx srcp=get_current_profile "$pffdir"
|
|
destp=
|
|
else
|
|
ensure_pffdir pffdir
|
|
setx srcp=get_current_profile "$pffdir"
|
|
destp="$1"
|
|
fi
|
|
;;
|
|
2)
|
|
if withpath "$2"; then
|
|
ensure_pffdir pffdir "$2"
|
|
setx srcp=get_current_profile "$pffdir"
|
|
destp="$1"
|
|
else
|
|
ensure_pffdir pffdir
|
|
srcp="$1"
|
|
destp="$2"
|
|
fi
|
|
;;
|
|
*)
|
|
ensure_pffdir pffdir "$3"
|
|
srcp="$1"
|
|
destp="$2"
|
|
;;
|
|
esac
|
|
[ "$destp" == "Common|Base" ] && destp=
|
|
if [ -z "$destp" ]; then
|
|
if [ "$srcp" == Base -o "$srcp" == Common ]; then
|
|
desc="pff --diff $srcp Base"
|
|
else
|
|
desc="pff --diff $srcp ${destp:-"Common|Base"}"
|
|
fi
|
|
else
|
|
desc="pff --diff $srcp $destp"
|
|
fi
|
|
|
|
local -a lfiles; local rfile bfile Cfile srcfile destfile
|
|
setx -a lfiles=get_local_files "$pffdir"
|
|
|
|
local -a diffcolor
|
|
isatty && diffcolor=(--color) || diffcolor=(--no-color)
|
|
for rfile in "${lfiles[@]}"; do
|
|
setx srcfile=get_pfile "$pffdir/$rfile" "$srcp" "$pffdir"
|
|
flexists "$srcfile" || continue
|
|
if [ -n "$destp" ]; then
|
|
setx destfile=get_pfile "$pffdir/$rfile" "$destp" "$pffdir"
|
|
else
|
|
setx Cfile=get_Cfile "$pffdir/$rfile" "$pffdir"
|
|
setx bfile=get_bfile "$pffdir/$rfile" "$pffdir"
|
|
# si on précise pas le profil destination, sélectionner dans l'ordre
|
|
# Common ou Base
|
|
if [ "$srcp" == Base -o "$srcp" == Common ]; then
|
|
destfile="$bfile"
|
|
elif flexists "$Cfile"; then
|
|
destfile="$Cfile"
|
|
else
|
|
destfile="$bfile"
|
|
fi
|
|
fi
|
|
if [ -n "$list_names" ]; then
|
|
diff -q "$destfile" "$srcfile" >&/dev/null || echo "$srcfile"
|
|
else
|
|
[ -n "$desc" ] && echo "$desc"
|
|
desc=
|
|
if [ -n "$PFF_USE_REGULAR_DIFF" ]; then
|
|
# au cas où git n'est pas disponible
|
|
diff -ur "$destfile" "$srcfile"
|
|
else
|
|
git diff "${diffcolor[@]}" --no-index "$destfile" "$srcfile"
|
|
fi
|
|
fi
|
|
done | page_maybe
|
|
}
|
|
|
|
#===========================================================
|
|
# pff --infos
|
|
|
|
function infos_cmd() {
|
|
local show_all="$1"; shift
|
|
local pffdir="$1"
|
|
local -a profiles vlfiles
|
|
local rfile Pfile flag
|
|
|
|
if find_pffdir pffdir "$pffdir"; then
|
|
ensure_pffdir pffdir "$pffdir"
|
|
autofix "$pffdir"
|
|
setx -a lfiles=get_local_files "$pffdir"
|
|
setx profile=get_current_profile "$pffdir"
|
|
setx -a profiles=get_profiles "$pffdir"
|
|
|
|
if [ ${#lfiles[*]} -gt 0 ]; then
|
|
[ ${#lfiles[*]} -gt 1 ] && estep "${#lfiles[*]} fichiers locaux" || estep "1 fichier local"
|
|
for rfile in "${lfiles[@]}"; do
|
|
setx -a vlfiles=get_vlfiles "$pffdir" "$rfile" "$profile"
|
|
setx Pfile=get_pfile "$pffdir/$rfile" "$profile" "$pffdir"
|
|
setx Cfile=get_Cfile "$pffdir/$rfile" "$pffdir"
|
|
if [[ "$rfile" == */.pffdir ]]; then
|
|
# répertoires entiers
|
|
rfile="${rfile%.pffdir}"
|
|
Pfile="${Pfile%/.pffdir}"
|
|
Cfile="${Cfile%/.pffdir}"
|
|
if [ -d "$Pfile" ]; then
|
|
flag="${COULEUR_BLEUE}*${COULEUR_NORMALE} "
|
|
elif [ "$profile" != Common -a -d "$Cfile" ]; then
|
|
flag="$(get_color YELLOW)C${COULEUR_NORMALE} "
|
|
elif [ -z "$show_all" ]; then
|
|
continue
|
|
else
|
|
flag=" "
|
|
fi
|
|
elif [ ${#vlfiles[*]} -gt 0 ]; then
|
|
flag="${COULEUR_ROUGE}P${COULEUR_NORMALE} "
|
|
elif [ -f "$Pfile" ]; then
|
|
flag="${COULEUR_BLEUE}*${COULEUR_NORMALE} "
|
|
elif [ "$profile" != Common -a -f "$Cfile" ]; then
|
|
flag="$(get_color YELLOW)C${COULEUR_NORMALE} "
|
|
elif [ -z "$show_all" ]; then
|
|
continue
|
|
else
|
|
flag=" "
|
|
fi
|
|
uecho " $flag$rfile"
|
|
done
|
|
else
|
|
estep "Pas de fichiers locaux définis"
|
|
fi
|
|
estep "Version courante: ${COULEUR_BLEUE}$VERSION${COULEUR_NORMALE}"
|
|
estep "Versions en attente: ${COULEUR_ROUGE}${PVERSIONS[*]}${COULEUR_NORMALE}"
|
|
[ -n "$profile" ] && estep "Profil courant: ${COULEUR_BLEUE}$profile${COULEUR_NORMALE}" || estep "${COULEUR_JAUNE}Pas de profil courant${COULEUR_NORMALE}"
|
|
[ ${#profiles[*]} -gt 0 ] && estep "Profils valides: ${profiles[*]}" || estep "Pas de profils définis"
|
|
elif [ -f "${pffdir:-.}/$PFFCONF" ]; then
|
|
: "${pffdir:=.}"
|
|
conf_init "${PFFCONFVARS[@]}"
|
|
source "$pffdir/$PFFCONF"
|
|
estep "[Projet pff déployé] Version courante: ${COULEUR_BLEUE}$VERSION${COULEUR_NORMALE}"
|
|
fi
|
|
}
|
|
|
|
################################################################################
|
|
# Programme principal
|
|
|
|
for script_alias in "${SCRIPT_ALIASES[@]}"; do
|
|
splitpair "$script_alias" src option
|
|
if [ "$scriptname" == "$src" ]; then
|
|
eval "set -- $option \"\$@\""
|
|
break
|
|
fi
|
|
done
|
|
|
|
QUIET=1 # masquer les messages de git et rsync?
|
|
VERYQUIET=1 # masquer les messages de git commit?
|
|
|
|
parse_mode=
|
|
if [ "$1" == -e -o "$1" == --edit ]; then
|
|
parse_mode=+
|
|
fi
|
|
|
|
action=infos
|
|
autopatch=1
|
|
version=
|
|
disttype=
|
|
ORIGEXTS=("${DEFAULT_ORIGEXTS[@]}")
|
|
unwrap=auto
|
|
merge_strategy=
|
|
commit_policy=ask
|
|
profile=
|
|
alternate=
|
|
args=($parse_mode
|
|
--help '$exit_with display_help'
|
|
-0,--init action=init
|
|
--s:,--origext: '$add@ ORIGEXTS'
|
|
--k,--clear-origexts '$ORIGEXTS=()'
|
|
-N,--new action=new
|
|
-M,--new-only '$action=new; autopatch='
|
|
-V:,--version: version=
|
|
--auto-archive disttype=auto
|
|
-F,--full-archive disttype=full
|
|
-H,--patch-archive disttype=patch
|
|
--auto-unwrap unwrap=auto
|
|
--no-unwrap unwrap=
|
|
-E,--unwrap unwrap=1
|
|
-g,--patch action=patch
|
|
-o,--ours merge_strategy=ours
|
|
--theirs merge_strategy=theirs
|
|
--ask-commit commit_policy=ask
|
|
-c,--comit commit_policy=1
|
|
--no-commit commit_policy=
|
|
--no-push UTOOLS_VCS_OFFLINE=1
|
|
-b,--add-global action=add-global
|
|
--locals action=list-locals
|
|
--profiles action=list-profiles
|
|
-s,--switch action=switch
|
|
-a,--add-local action=add-local
|
|
-e,--edit action=edit
|
|
-p:,--profile: profile=
|
|
-P,--prod profile=prod
|
|
-T,--test profile=test
|
|
-A,--all-profiles profile=ALL
|
|
-d,--diff action=diff
|
|
--infos action=infos
|
|
-l,--list-names,--show-all alternate=1
|
|
)
|
|
parse_args "$@"; set -- "${args[@]}"
|
|
|
|
array_fix_paths ORIGEXTS
|
|
|
|
case "$action" in
|
|
init) init_cmd "$@";;
|
|
new) new_cmd "$autopatch" "$version" "$disttype" "$unwrap" "$merge_strategy" "$commit_policy" "$@";;
|
|
patch) patch_cmd "$merge_strategy" "$commit_policy" "$@";;
|
|
add-global) add_global_cmd "$@";;
|
|
list-locals) list_locals_cmd "$@";;
|
|
list-profiles) list_profiles_cmd "$@";;
|
|
switch) switch_cmd "$@";;
|
|
add-local) add_local_cmd "$@";;
|
|
edit) edit_cmd "$profile" "$@";;
|
|
diff) diff_cmd "$alternate" "$@";;
|
|
infos) infos_cmd "$alternate" "$@";;
|
|
esac
|