Index: src/tkt.c ================================================================== --- src/tkt.c +++ src/tkt.c @@ -899,10 +899,14 @@ ** ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** ** like set, but create a new ticket with the given values. ** +** %fossil ticket history TICKETUUID +** +** Show the complete change history for the ticket +** ** The values in set|add are not validated against the definitions ** given in "Ticket Common Script". */ void ticket_cmd(void){ int n; @@ -916,16 +920,16 @@ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } if( g.argc<3 ){ - usage("add|fieldlist|set|show"); + usage("add|fieldlist|set|show|history"); }else{ n = strlen(g.argv[2]); if( n==1 && g.argv[2][0]=='s' ){ /* set/show cannot be distinguished, so show the usage */ - usage("add|fieldlist|set|show"); + usage("add|fieldlist|set|show|history"); }else if( strncmp(g.argv[2],"list",n)==0 ){ if( g.argc==3 ){ usage("list fields|reports"); }else{ n = strlen(g.argv[3]); @@ -970,19 +974,24 @@ rptshow( zRep, zSep, zFilterUuid, tktEncoding ); } }else{ /* add a new ticket or update an existing ticket */ - enum { set,add,err } eCmd = err; + enum { set,add,history,err } eCmd = err; int i = 0; int rid; const char *zTktUuid = 0; Blob tktchng, cksum; /* get command type (set/add) and get uuid, if needed for set */ - if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){ - eCmd = set; + if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 || + strncmp(g.argv[2],"history",n)==0 ){ + if( strncmp(g.argv[2],"history",n)==0 ){ + eCmd = history; + }else{ + eCmd = set; + } if( g.argc==3 ){ usage("set TICKETUUID"); } zTktUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] @@ -996,13 +1005,90 @@ i = 3; zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } /* none of set/add, so show the usage! */ if( eCmd==err ){ - usage("add|fieldlist|set|show"); + usage("add|fieldlist|set|show|history"); } - + + /* we just handle history separately here, does not get out */ + if( eCmd==history ){ + Stmt q; + char *zTitle; + int tagid; + + if ( i != g.argc ){ + fossil_fatal("no other parameters expected to %s!",g.argv[2]); + } + tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zTktUuid); + if( tagid==0 ){ + fossil_fatal("no such ticket %h", zTktUuid); + } + db_prepare(&q, + "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL" + " FROM event, blob" + " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)" + " AND blob.rid=event.objid" + " UNION " + "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user" + " FROM attachment, blob" + " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)" + " AND blob.rid=attachid" + " ORDER BY 1 DESC", + tagid, tagid + ); + while( db_step(&q)==SQLITE_ROW ){ + Manifest *pTicket; + char zShort[12]; + const char *zDate = db_column_text(&q, 0); + int rid = db_column_int(&q, 1); + const char *zChngUuid = db_column_text(&q, 2); + const char *zFile = db_column_text(&q, 4); + memcpy(zShort, zChngUuid, 10); + zShort[10] = 0; + if( zFile!=0 ){ + const char *zSrc = db_column_text(&q, 3); + const char *zUser = db_column_text(&q, 5); + if( zSrc==0 || zSrc[0]==0 ){ + fossil_print("Delete attachment %h\n", zFile); + }else{ + fossil_print("Add attachment %h\n", zFile); + } + fossil_print(" by %h on %h\n", zUser, zDate); + }else{ + pTicket = manifest_get(rid, CFTYPE_TICKET); + if( pTicket ){ + int i; + + fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate); + for(i=0; inField; i++){ + Blob val; + const char *z; + z = pTicket->aField[i].zName; + blob_set(&val, pTicket->aField[i].zValue); + if( z[0]=='+' ){ + fossil_print(" Append to "); + z++; + }else{ + fossil_print(" Change "); + } + fossil_print("%h: ",z); + if( blob_size(&val)>50 || contains_newline(&val)) { + fossil_print("\n ",blob_str(&val)); + comment_print(blob_str(&val),4,79); + }else{ + fossil_print("%s\n",blob_str(&val)); + } + blob_reset(&val); + } + } + manifest_destroy(pTicket); + } + } + db_finalize(&q); + return; + } /* read all given ticket field/value pairs from command line */ if( i==g.argc ){ fossil_fatal("empty %s command aborted!",g.argv[2]); } getAllTicketFields();