#include #include #include #include #include "projet.h" #include "projetx.m" #include "fusion.h" #include "prjlist.h" /* #Sp‚cification: fusion / introduction La fusion permet de combiner diff‚rentes r‚visions et produire une nouvelle. Il y a deux cas: La fusion simple et le cauchemar. La fusion simple est l'op‚ration qui permet de combiner les modifications de r‚visions qui possŠdent la mˆme r‚vision r‚f‚rence. exemple: R‚vision r‚f‚rence 1.0 Le programmeur A commande cette r‚vision et la modifie. Il livre 1.0.1.1. Le programmeur B fait de mˆme et livre 1.0.2.1. +-- 1.0.1.1 ---+ 1.0 --| |-- 1.1 +-- 1.0.2.1 ---+ On dit que cette fusion est simple parce que les changements qui ont ‚t‚ fait peuvent facilement ˆtre d‚termin‚ en comparant avec la r‚vision 1.0. Le cauchemar se produit lorsqu'on doit travailler toute une nuit pour faire une fusion. Paradoxalement, on ne dort pas durant le cauchemar. La fusion complexe (dite cauchemar) se produit lorsqu'on combine des r‚visions qui n'ont pas la mˆme r‚vision r‚f‚rence. Ou encore ont une r‚vision r‚f‚rences commune tres lointaine (ultimement 0.0). Voici un exemple de cauchemar que Picasso lui-mˆme ne saurait peindre, lui qui pourtant voyait tout de travers. +- 0.0.1.1 -+ +-- 0.2.1.1 -+...- 0.2.1.9 -+ | |-- 0.2 --| | | +- 0.0.2.1 -+ +-- 0.2.2.1 -+ |- 0.12 | | | 0.0 --| |- 0.11 -------+ +- 0.0.3.1 -+ | +- 0.0.4.1 -+-- 0.1 -- ... 0.10 ---+ +- 0.0.5.1 -+ Dans le dessin suivant, on veut fusionner 0.11 avec 0.2.1.9. Ce qui nous interessent, ce sont les changement qui ont ‚t‚ fait depuis 0.2.1.1. Ceci est particuliŠrement complexe parce que la version 0.2.1.9 est trŠs diff‚rentes de la version 0.11 et aussi 0.10. Pour s'en sortir, on choisit la r‚vision de base: 0.11. Ensuite on choisit une r‚vision r‚f‚rence: 0.2.1.1. On choisit la r‚vision … fusionner: 0.2.1.9. On associe cette derniŠre … 0.2.1.1 (sa r‚f‚rence). Le programme ‚tablira la liste de tous les fichiers qui ont chang‚ entre 0.2.1.1 et 0.2.1.9 et permettra … l'usager de comparer les version 0.11 0.2.1.1 et 0.2.1.9. */ # /* Met … jour le fichier version.dat pour "r‚server" la nouvelle r‚vision. */ static void near bfusion_setintgrev( USERINFO *intg, VERSION_DAT *version, VERSION_PAIRE tbpaire[], int nbvar) { /* #Specification: fusion / extraction Lorsque l'on extrait l'information pour faire une int‚gration dans l'espace commun, la version destination est cr‚‚e imm‚diatement avec des commentaires permettant de connaitre son origine. Le fichier version.dat est imm‚diatement corrig‚. */ char path[MAXSIZ_PATH]; intg->makrefpath("",path); path_make (path,"version.dat",path); const REVISION *revdst = intg->getrevdst(); VERSION_ONE *one = version->get (revdst); // Si one existe, c'est qu'on fait un intŠgration dans l'espace usager if (one == NULL) one = version->add (intg->getversion(),NULL,revdst); char revstr[MAXSIZ_REVISION]; intg->getrevsrc()->format (revstr); one->addinfo ("Int‚gration de %s",revstr); for (int i=0; irev.format (revstr); one->addinfo (" + %s",revstr); } version->save(intg); } /* Balaye les sous-r‚pertoires et pr‚pare les integre.dat */ static int near bfusion_setintg ( USERINFO *user, VERSION_DAT &version, VERSION_PAIRE tbpaire[], int nbvar, const char *tbprj[], int nbprj) { int ret = -1; WINDOW_LOG log ("fusion.log",1); log.show(); if (projet_chkusrenv(user) != -1){ ret = projet_get (user,tbprj,nbprj,false,true,false,&log); if (ret != -1){ /* Il faut s'assurer que les sources de toutes les r‚visions dans tbvar sont disponibles dans kit/ombre. On fait simplement un extraction dans \kit\build. */ /* On doit aussi produire pour chaque r‚pertoires de tous les projets s‚lectionn‚s, un r‚sum‚ des diff‚rences entre la version de base et la version int‚gr‚. Ce fichier permettra de r‚soudre les conflits, de visualiser les fichiers qui n'existe plus et de voir ceux qui se sont ajout‚s. L'option edition-fusion utilisera ce r‚sum‚ pour piloter le programmeur responsable de l'int‚gration. */ for (int i=0; igetversion() ,&tbpaire[i].var->rev); USERBUILD ref (tbpaire[i].ref->getversion() ,&tbpaire[i].ref->rev); ret = bfusion_extractrev (&build,&ref,user ,tbprj,nbprj,&log); } if (ret != -1){ bfusion_setintgrev (user,&version,tbpaire,nbvar); } } } return ret; } // Extraction de plusieurs version interm‚diaire pour int‚gration // dans l'environnement de recompilation. class PRJLISTE_INTEGRE: public PRJLISTE{ VERSION_PAIRE *tbpaire; int nbpaire; VERSION_DAT &version; /*~PROTOBEG~ PRJLISTE_INTEGRE */ public: PRJLISTE_INTEGRE (VERSION_PAIRE _tbpaire[], int _nbpaire, VERSION_DAT&_version); int exec (USERINFO *user, int opr, const char *tbprj[], int nbprj); const char **getbutton (void); int getlist (USERINFO *user, PRJ_INFO tbprj[], int); /*~PROTOEND~ PRJLISTE_INTEGRE */ }; PUBLIC PRJLISTE_INTEGRE::PRJLISTE_INTEGRE( VERSION_PAIRE _tbpaire[], int _nbpaire, VERSION_DAT &_version) : tbpaire(_tbpaire), nbpaire(_nbpaire), version(_version) { } PUBLIC int PRJLISTE_INTEGRE::getlist( USERINFO *user, PRJ_INFO tbprj[], // Contiendra les noms de projets a int‚grer int ) // maxtb { int nbprj = 0; VERSION_PAIRE *ptp = tbpaire; /* Sp‚cification: int‚gration / liste de projet / attribut La liste des projets s‚lectionnable est l'union de tous les projet membre des r‚visions … fusionner et non pas seulement la liste des projets de la r‚vision r‚f‚rence. Pour aider … la s‚lection des projets a fusionner, sp‚cialement pour les fusions en espace usager, on pr‚sente vis … vis chaque projet un petit message (attribut). Ce message permet de visualiser rapidement les projets "actifs". Voici la liste des messages: + : Indique qu'une r‚vision contient une r‚vision modifi‚e de ce projet. * : Indique que plusieurs r‚visions contiennent une r‚vision modifi‚es de ce projets rien: Aucune r‚vision n'a modifi‚ ce projet. */ for (int i=0; ivar->getrev(),"",user); MAKEFILE mkfref (NULL,false,ptp->ref->getrev(),"",user); mkfvar.setiter(); MAKEFILE_FILE *var_file; while ((var_file=mkfvar.iter("*/makefile.dat",0))!=NULL){ const char *nom = var_file->getnom(); PRJ_INFO *ptprj = tbprj; int j; for (j=0; jnom,nom)==0) break; } if (j==nbprj){ ptprj->nom = strdup_err (nom); nbprj++; } MAKEFILE_FILE *ref_file = mkfref.locate(nom); if (ref_file == NULL || ref_file->getrev()->cmp(var_file->getrev())!=0){ char *ptatr = &ptprj->atr; if (*ptatr == ' '){ *ptatr = '+'; }else if (*ptatr == '+'){ *ptatr = '*'; } } } } // EnlŠve le /makefile.dat PRJ_INFO *ptprj = tbprj; for (int i=0; inom,ptprj->nom,NULL); } return nbprj; } PUBLIC int PRJLISTE_INTEGRE::exec ( USERINFO *user, int opr, // Op‚ration command‚. const char *tbprj[], // Projets s‚lectionn‚s int nbprj) { int ret = -1; if (nbprj == 0){ xconf_error (MSG_U(E_AUMOINS1 ,"C'est pas en ne choisissant rien\n" "que vous allez améliorer votre productivité.")); }else if (opr == 0){ ret = bfusion_setintg (user,version ,tbpaire,nbpaire,tbprj,nbprj); } return ret; } PUBLIC const char **PRJLISTE_INTEGRE::getbutton() { static const char *tb[]={MSG_U(B_INTEGRE,"Intègre"),NULL}; return tb; } /* Installation d'une combinaison de r‚vision pour int‚gration. Cette extraction servira … faire la fusion des modifications et faire la recompilation du projet. Retourne -1 si abandon ou erreur. */ int bfusion_setup ( USERINFO *user, bool fusion_usager) // Les fichiers integre.dat // sont accumul‚s dans l'environnement d‚finie // par user, ou dans une nouvelle r‚vision // de /kit/build { int ret = -1; PRJLOCK racine(user,"_racine"); if (racine.isok()){ VERSION_PAIRE tbpaire[50]; VERSION_DAT version(user); #ifdef MISSING int nbvar = bfusion_compose (user,tbpaire,version); #else int nbvar = 0; #endif if (nbvar > 0){ PRJLISTE_INTEGRE g(tbpaire,nbvar,version); if (fusion_usager){ projet_select (user,g); }else{ REVISION revnext; // La r‚vision qu'on veut produire. VERSION_ONE *cur = version.get(user->getrevdst()); if (cur != NULL && cur->isinteg()){ /* #Sp‚cification: fusion / livraison tardive Si on pr‚pare une fusion dans une fusion non livr‚e, les fichiers integre.dat seront ajout‚s … cette r‚vision. Cela permet de d‚buter une int‚gration et d'ajouter des livraisons … celle-ci en cours de route. Cela rŠgle le problŠme suivant: L'int‚grateur a d‚j… commencer et un programmeur lui demande "J'ai finalement r‚gler un problŠme important, est-ce trop tard ?". La r‚ponse pourra ˆtre "non" maintenant. */ revnext = *user->getrevdst(); }else{ /* Trouve le plus grand num‚ro de r‚vision pour la version de base. Etablie le num‚ro de r‚vision pour l'int‚gration … partir de cette r‚vision. */ VERSION_ONE *nextintg = version.select (user->getversion() ,NULL); revnext = nextintg->rev; revnext.next(0,REVISION_INTEGRE); } /* #Sp‚cification: fusion / ‚tapes / installation AprŠs avoir s‚lectionner les diff‚rentes r‚visions requises pour la fusion, il faut installer une copie de la r‚vision de base dans l'environnement d'int‚gration /kit/build. On installe imm‚diatement cette r‚vision sous le num‚ro de la r‚vision qui sera produite par l'int‚gration. exemple: 1.0 + ( 1.0.1.1 , 1.0.2.1, 1.0.3.1 ) -> 1.1 La r‚vision 1.0 va ˆtre install‚ dans /kit/build/_1_1 */ USERINTG intg (user->getversion(),user->getrevsrc(),&revnext); projet_select (&intg,g); } } } return ret; }