#include #include #include #include #include #include "projet.h" /* #Sp‚cification: fusion / transaction / description Pour d‚crire complŠtement le r“le d'une ligne de transaction pour un fichier, on sp‚cifie 3 r‚visions, soit celle de la racine, du r‚pertoire et du fichier. En sp‚cifiant celle de la racine, on conserve le contexte qui a g‚n‚r‚ cette transaction. Sans cette information, on a de la difficult‚ … comprendre pourquoi ProjetX nous parle de certaine r‚vision. Le problŠme c'est que les livraisons d'un programmeur contiennent des choses qui ne sont pas de lui. On a l'exemple suivant # toto.c 4.1.2.1 6.7.15.4 6.7.13.1 # Dans cet exemple, on voit que le programmeur 2 est intervenu sur le fichier toto.c. La r‚vision 4.1.2.1 a ‚t‚ explicitement s‚lectionn‚e au moment de la pr‚paration de la fusion. L'op‚ration que le programmeur 2 a fait est subtile. Ce toto.c ne provient pas de lui. Il a tout simplement accapar‚ la r‚vision 6.7.15.4 du r‚pertoire contenant toto.c et cette r‚vision du r‚pertoire (livr‚ par le programmeur 15) provenait du programmeur 13!!! Sans ces trois niveaux d'information, on ne peut pas toujours s'y retrouver. */ # /* R‚visions d'un fichier par rapport … une r‚f‚rence de base. Utilis‚e lors de la lecture du fichier integre.dat. */ PUBLIC INTG_TRANS::INTG_TRANS( char _oper, // + = Fichier r‚vis‚ // - = Fichier ‚l‚min‚. const char *str) // Argument suppl‚mentaire { /* #Sp‚cification: fusion / integre.dat / format d'une transaction Chaque ligne de transaction a le format suivant. + R‚vision_fichier R‚vision_repertoire R‚vision_racine Path - R‚vision_repertoire R‚vision_racine ? R‚vision_fichier R‚vision_repertoire R‚vision_racine path + indique une r‚vision. - indique que le fichier a ‚t‚ effac‚e durant cette r‚vision ? indique que le fichier a ‚t‚ cr‚‚ lors de cette r‚vision. */ assert (_oper=='+' || _oper=='-' || _oper == '?'); str = str_copyword (revs.fichier,str); if (_oper == '-'){ path = NULL; oper = INTEGRE_EFFACE; strcpy (revs.dir,revs.fichier); strcpy (revs.root,revs.fichier); }else{ str = str_copyword (revs.dir,str_skip(str)); str = str_copyword (revs.root,str_skip(str)); str = str_skip(str); path = strdup_err (str); if (_oper == '+'){ oper = INTEGRE_MODIF; }else{ oper = INTEGRE_NOUVEAU; } } } /* R‚visions d'un fichier par rapport … une r‚f‚rence de base. Utilis‚e pour enregistrer une nouvelle transaction. */ PUBLIC INTG_TRANS::INTG_TRANS( char _oper, // + = Fichier r‚vis‚ // - = Fichier ‚l‚min‚. const char *rev_fichier, const char *rev_dir, const char *rev_root, const char *_path) { assert (_oper=='+' || _oper=='-' || _oper == '?'); strcpy (revs.fichier,rev_fichier); strcpy (revs.dir,rev_dir); strcpy (revs.root,rev_root); if (_oper == '-'){ path = NULL; oper = INTEGRE_EFFACE; }else{ // En principe, _path ne devrait jamais etre null, mais path = strdup_err (_path==NULL ? "???" : _path); if (_oper == '+'){ oper = INTEGRE_MODIF; }else{ oper = INTEGRE_NOUVEAU; } } } PUBLIC INTG_TRANS::~INTG_TRANS() { ::free (path); } /* Enregistre un transaction de r‚vision dans un fichier ascii */ PUBLIC void INTG_TRANS::write (FILE *fout) { if (oper == INTEGRE_MODIF){ fprintf (fout,"+ %s %s %s %s\n",revs.fichier,revs.dir,revs.root,path); }else if (oper == INTEGRE_NOUVEAU){ fprintf (fout,"? %s %s %s %s\n",revs.fichier,revs.dir,revs.root,path); }else{ fprintf (fout,"- %s %s\n",revs.dir,revs.root); } } /* Retourne la r‚vision du fichier de cette transaction */ PUBLIC const char *INTG_TRANS::getrevfile() { return revs.fichier; } /* Retourne la r‚vision du projet qui a livr‚ cette transaction */ PUBLIC const char *INTG_TRANS::getrevdir() { return revs.dir; } /* Retourne la r‚vision de la racine dont cette transaction fait partie. */ PUBLIC const char *INTG_TRANS::getrevroot() { return revs.root; } /* Collection de r‚visions … un fichier. _name est un path relatif au r‚pertoire. G‚n‚ralement c'est simplement un nom de fichier. */ PUBLIC INTEGRE_FILE::INTEGRE_FILE ( const char *_name, const REVISION *rev_fichier, const REVISION *rev_dir, const REVISION *rev_root, const char *_path, int _nouveau) // Ce fichier n'existe pas pour la r‚vision rev { strcpy (fname,_name); revs.root[0] = revs.dir[0] = revs.fichier[0] = '-'; revs.root[1] = revs.dir[1] = revs.fichier[1] = '\0'; if (rev_fichier != NULL) rev_fichier->format (revs.fichier); if (rev_dir != NULL) rev_dir->format (revs.dir); if (rev_root != NULL) rev_root->format (revs.root); path = NULL; if (_path != NULL) path = strdup_err (_path); nouveau = _nouveau; done = 0; } /* Utiliser pour la relecture du fichier integre.dat */ PROTECTED INTEGRE_FILE::INTEGRE_FILE (const char *buf) { /* #Sp‚cification: fusion / integre.dat / format Chaque fichier … int‚grer possŠde une ligne de description dans le fichier integre.dat, suivit de transaction. La description d'un fichier … int‚grer a le format suivant: nom R‚vision_fichier R‚vision_r‚pertoire R‚vision_racine nouveau path Le nom (sans path) peut soit repr‚senter un fichier ou un sous-r‚pertoire. Dans ce cas, le nom est celui du makefile.dat (dir/makefile.dat). R‚vision est la r‚vision effective du fichier. Nouveau est 0 ou 1: 0 : Le fichier existe dans la r‚vision r‚f‚rence. 1 : Le fichier a ‚t‚ cr‚‚ dans une r‚vision subs‚quente. path dans l'archive. */ const char *pt = str_copyword (fname,str_skip(buf)); pt = str_copyword (revs.fichier,str_skip(pt)); pt = str_copyword (revs.dir,str_skip(pt)); pt = str_copyword (revs.root,str_skip(pt)); pt = str_skip (pt); nouveau = atoi(pt); pt = str_skipdig (pt); pt = str_skip (pt); path = strdup_err (pt); done = 0; } PUBLIC INTEGRE_FILE::~INTEGRE_FILE() { ::free (path); } /* Retourne le nom (sans path) du fichier sp‚cifi‚ par INTEGRE_FILE. */ PUBLIC const char *INTEGRE_FILE::getfname() { return fname; } PUBLIC INTG_TRANS* INTEGRE_FILE::getitem(int no) const { return (INTG_TRANS*)ARRAY::getitem(no); } /* Pr‚serve les transactions dans un fichier ascii */ PUBLIC void INTEGRE_FILE::write (FILE *fout) { fprintf (fout,"%s %s %s %s %d %s\n",fname,revs.fichier ,revs.dir,revs.root,nouveau ,path==NULL ? "" : path); for (int i=0; iwrite(fout); n'est pas ‚quivalent … INTG_TRANS *tra = item(i); tra->write(fout); */ INTG_TRANS *tra = getitem(i); tra->write(fout); } } /* Compare deux chaines sauf que str1 et str2 peuvent ˆtre NULL. */ static int near strcmp_nul (const char *str1, const char *str2) { int ret; if (str1 == NULL){ ret = (str2 == NULL) ? 0 : -1; }else if (str2 == NULL){ ret = 1; }else{ ret = strcmp(str1,str2); } return ret; } /* Ajoute une transaction si elle n'y est pas d‚j…. Retourne -1 si pas ajout‚. */ PUBLIC int INTEGRE_FILE::addcond ( char oper, TRA_REV_SPEC &rev, const char *path) { int deja = 0; INTG_OPER ioper; if (oper == '+'){ ioper = INTEGRE_MODIF; }else if (oper == '-'){ ioper = INTEGRE_EFFACE; }else{ ioper = INTEGRE_NOUVEAU; } /* #Sp‚cification: fusion / pr‚paration / transaction Le fichier integre.dat peut contenir pour un fichier plusieurs r‚f‚rence … une mˆme r‚vision de celui-ci. Chaque transaction contient le num‚ro de r‚vision du fichier, le num‚ro de r‚vision du r‚pertoire et le num‚ro de r‚vision de la racine. Initialement, nous avons limit‚ le nombre de r‚visions affich‚es, mais cela mˆlait l'utilisateur. Il ne comprenait pas ais‚ment dans quel contexte une r‚vision ‚tait pr‚sent‚e. Maintenant, mˆme si ca donne un peu de r‚p‚tition, ca rend l'interpr‚tation plus facile. Voici un exemple. On fusionne trois livraisons: 4.1.1.1 4.1.2.2 4.1.3.4 et on obtient la sitiation suivante pour le fichier toto.c # Programmeur Rev_racine Rev_dir Rev source - 4.1 4.1 4.1 Pierre 4.1.1.1 4.1.1.1 4.1.2.1 Jacques 4.1.2.2 4.1.2.2 4.1.2.2 Jeanb 4.1.3.4 4.1.3.1 4.1.2.1 # Si l'int‚grateur est Jeanb (Il fait une fusion usager dans son environnement, la situation est tres claire: Jacques a modifi‚ deux fois toto.c et lors de la premiŠre livraison Pierre et Jeanb en ont pris un copie (via pget ou fusion usager complexe). Il voit mˆme qu'il s'est appropri‚ toto.c dans 4.1.3.1. Initialement, on ne mentionnait une r‚vision qu'une seule fois. La cl‚ ‚tait la r‚vision source. # Programmeur Rev_projet Rev source - 4.1 4.1 Pierre 4.1.1.1 4.1.2.1 Jacques 4.1.2.2 4.1.2.2 # Pour la mˆme situation, Jeanb est exclus du portrait. Et pourtant il se rappelle avoir eu un probleme avec toto.c. Donc, mˆme s'il y a un peu de redondance, la pr‚sentation est plus claire. */ for (int i=0; ioper == ioper && strcmp(trans->revs.fichier,rev.fichier) ==0 && strcmp(trans->revs.dir,rev.dir) ==0 && strcmp(trans->revs.root,rev.root) ==0 && strcmp_nul(trans->path,path) == 0){ deja = -1; break; } } if (!deja){ INTEGRE_FILE::add (new INTG_TRANS(oper,rev.fichier,rev.dir ,rev.root,path)); } return deja; } /* #Sp‚cification: fusion / integre.dat Le fichier integre.dat contient une s‚rie de nom de fichier accompagn‚ d'une s‚rie de r‚vision. Le format a l'apparence suivante: Description du fichier transaction de r‚vision ... fichier */ # /* Ce constructeur charge le fichier. L'absence de fichier n'est pas une erreur. Le destructeur r‚‚crit le fichier. */ PUBLIC INTEGRE_DAT::INTEGRE_DAT( const char *_fname) // Path du fichier integre.dat // Ou path d'un r‚pertoire, ajoute integre.dat { if (file_type(_fname)==1){ char tmp[MAXSIZ_PATH]; path_make (_fname,"integre.dat",tmp); fname = strdup_err (tmp); }else{ fname = strdup_err (_fname); } FILE *fin = fopen (fname,"r"); if (fin != NULL){ char buf[200]; INTEGRE_FILE *fich = NULL; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ strip_end (buf); if (buf[0] != '\0'){ char *pt = str_skip (buf+1); if (buf[0] == '+' || buf[0] == '-' || buf[0] == '?'){ assert(fich!=NULL); fich->add (new INTG_TRANS(buf[0],pt)); }else{ fich = new INTEGRE_FILE (buf); add (fich); } } } fclose (fin); } } PUBLIC INTEGRE_FILE *INTEGRE_DAT::getitem(int no) const { return (INTEGRE_FILE*)ARRAY::getitem(no); } /* Le destructeur sauve automatiquement le fichier ou d‚truit si vide. De plus il ‚limine les fichiers marqu‚ "done" */ PUBLIC INTEGRE_DAT::~INTEGRE_DAT() { /* #Suggestion: fusion / ‚dition / sauvegarde Lorsqu'on sauvegarde un fichier integre.dat, on ‚limine tous les records marqu‚ "done" avant de sauver. On pourrait pr‚serv‚ ce flag dans le fichier si bien que pourrait sortir d'un int‚gration et y revenir dans le mˆme ‚tat. Par exemple, en revenant dans un r‚pertoire, on obtiendrait la liste des fichier … fusionner ainsi que ceux qu'on a d‚j… fusionn‚. Il y a d‚j… un bouton m‚nage pour faire ca de toute fa‡on. */ for (int i=0; idone){ remove_del(i); i--; } } if (getnb()==0){ file_unlink (fname); }else{ FILE *fout = fopen (fname,"w"); if (fout == NULL){ /* #Sp‚cification: fusion / pr‚paration / nouveau r‚pertoire Si un nouveau r‚pertoire a ‚t‚ ajout‚ dans un projet par une des livraison a int‚grer, ce r‚pertoire sera cr‚er lors de l'‚criture du integre.dat si requis. */ char dpath[MAXSIZ_PATH]; path_splitlex (fname,dpath,NULL); file_mkdiranc (dpath); fout = fopen_err (fname,"w",0); } if (fout != NULL){ for (int i=0; iwrite(fout); } fclose (fout); } } free (fname); } /* Localise les transactions sur un fichier */ PUBLIC INTEGRE_FILE *INTEGRE_DAT::locate(const char *fname) { INTEGRE_FILE *ret = NULL; for (int i=0; ifname,fname)==0){ ret = fich; break; } } return ret; } /* Ajoute une transaction sur un fichier. Si c'est la premiŠre transaction, le fichier est ajouter … integre.dat. */ PUBLIC void INTEGRE_DAT::addtrans ( const char *fname, FILE_REV_SPEC &ref, const char *refpath, // Path dans kit\ombre de la r‚vision r‚f‚rence // ou NULL char oper, // + = R‚vision au fichier, ou ajout // - = Efface le fichier // ? = Nouveau fichier FILE_REV_SPEC &rev, const char *path) // Path dans kit\ombre ou NULL { INTEGRE_FILE *fich = locate (fname); if (fich == NULL){ fich = new INTEGRE_FILE (fname,ref.fichier,ref.dir,ref.root ,refpath,oper == '?'); if (fich != NULL) add (fich); } if (fich != NULL){ TRA_REV_SPEC rv; if (rev.fichier == NULL){ // Plus ou moins utile. Si rev.fichier == NULL, c'est que le // fichier existe pas pour cette r‚vision. Ca ‚vite // au moins qu'une chaine non initialis‚e se balade plus // bas dans ce programme. rev.dir->format (rv.fichier); }else{ rev.fichier->format (rv.fichier); } rev.dir->format (rv.dir); rev.root->format (rv.root); fich->addcond (oper,rv,path); } }