1851 lines
		
	
	
		
			67 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1851 lines
		
	
	
		
			67 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 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
 | |
| 
 | |
| 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 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
 | |
|     local pffdir="$1"
 | |
|     find "$pffdir/pff/Base" -type f | sed "s|^$pffdir/pff/Base/||" | grep -v '/__pv-[^/_]*__[^/]*$'
 | |
| }
 | |
| 
 | |
| 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 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 [ -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"
 | |
| }
 | |
| 
 | |
| #===========================================================
 | |
| # 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 [ ${#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
 |