Fossil SCM
Add the a=, b=, and n= query parameters to the finfo web method. Show all duplicate entries in a timeline in the right-most rail.
Commit
cbc84ad65cfcb8b138e750c1114934efc8af24c4
Parent
c8f10e551ec413b…
2 files changed
+26
-4
+28
-13
+26
-4
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -186,27 +186,38 @@ | ||
| 186 | 186 | |
| 187 | 187 | /* |
| 188 | 188 | ** WEBPAGE: finfo |
| 189 | 189 | ** URL: /finfo?name=FILENAME |
| 190 | 190 | ** |
| 191 | -** Show the complete change history for a single file. | |
| 191 | +** Show the change history for a single file. | |
| 192 | +** | |
| 193 | +** Additional query parameters: | |
| 194 | +** | |
| 195 | +** a=DATE Only show changes after DATE | |
| 196 | +** b=DATE Only show changes before DATE | |
| 197 | +** n=NUM Show the first NUM changes only | |
| 192 | 198 | */ |
| 193 | 199 | void finfo_page(void){ |
| 194 | 200 | Stmt q; |
| 195 | 201 | const char *zFilename; |
| 196 | 202 | char zPrevDate[20]; |
| 203 | + const char *zA; | |
| 204 | + const char *zB; | |
| 205 | + int n; | |
| 197 | 206 | Blob title; |
| 207 | + Blob sql; | |
| 198 | 208 | GraphContext *pGraph; |
| 199 | 209 | |
| 200 | 210 | login_check_credentials(); |
| 201 | 211 | if( !g.okRead ){ login_needed(); return; } |
| 202 | 212 | style_header("File History"); |
| 203 | 213 | login_anonymous_available(); |
| 204 | 214 | |
| 205 | 215 | zPrevDate[0] = 0; |
| 206 | 216 | zFilename = PD("name",""); |
| 207 | - db_prepare(&q, | |
| 217 | + blob_zero(&sql); | |
| 218 | + blob_appendf(&sql, | |
| 208 | 219 | "SELECT" |
| 209 | 220 | " datetime(event.mtime,'localtime')," /* Date of change */ |
| 210 | 221 | " coalesce(event.ecomment, event.comment)," /* Check-in comment */ |
| 211 | 222 | " coalesce(event.euser, event.user)," /* User who made chng */ |
| 212 | 223 | " mlink.pid," /* Parent rid */ |
| @@ -217,15 +228,26 @@ | ||
| 217 | 228 | " event.bgcolor," /* Background color */ |
| 218 | 229 | " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" |
| 219 | 230 | " AND tagxref.rid=mlink.mid)" /* Tags */ |
| 220 | 231 | " FROM mlink, event" |
| 221 | 232 | " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" |
| 222 | - " AND event.objid=mlink.mid" | |
| 223 | - " ORDER BY event.mtime DESC /*sort*/", | |
| 233 | + " AND event.objid=mlink.mid", | |
| 224 | 234 | TAG_BRANCH, |
| 225 | 235 | zFilename |
| 226 | 236 | ); |
| 237 | + if( (zA = P("a"))!=0 ){ | |
| 238 | + blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zA); | |
| 239 | + } | |
| 240 | + if( (zB = P("b"))!=0 ){ | |
| 241 | + blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zB); | |
| 242 | + } | |
| 243 | + blob_appendf(&sql," ORDER BY event.mtime DESC /*sort*/"); | |
| 244 | + if( (n = atoi(PD("n","0")))>0 ){ | |
| 245 | + blob_appendf(&sql, " LIMIT %d", n); | |
| 246 | + } | |
| 247 | + db_prepare(&q, blob_str(&sql)); | |
| 248 | + blob_reset(&sql); | |
| 227 | 249 | blob_zero(&title); |
| 228 | 250 | blob_appendf(&title, "History of "); |
| 229 | 251 | hyperlinked_path(zFilename, &title, 0); |
| 230 | 252 | @ <h2>%b(&title)</h2> |
| 231 | 253 | blob_reset(&title); |
| 232 | 254 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -186,27 +186,38 @@ | |
| 186 | |
| 187 | /* |
| 188 | ** WEBPAGE: finfo |
| 189 | ** URL: /finfo?name=FILENAME |
| 190 | ** |
| 191 | ** Show the complete change history for a single file. |
| 192 | */ |
| 193 | void finfo_page(void){ |
| 194 | Stmt q; |
| 195 | const char *zFilename; |
| 196 | char zPrevDate[20]; |
| 197 | Blob title; |
| 198 | GraphContext *pGraph; |
| 199 | |
| 200 | login_check_credentials(); |
| 201 | if( !g.okRead ){ login_needed(); return; } |
| 202 | style_header("File History"); |
| 203 | login_anonymous_available(); |
| 204 | |
| 205 | zPrevDate[0] = 0; |
| 206 | zFilename = PD("name",""); |
| 207 | db_prepare(&q, |
| 208 | "SELECT" |
| 209 | " datetime(event.mtime,'localtime')," /* Date of change */ |
| 210 | " coalesce(event.ecomment, event.comment)," /* Check-in comment */ |
| 211 | " coalesce(event.euser, event.user)," /* User who made chng */ |
| 212 | " mlink.pid," /* Parent rid */ |
| @@ -217,15 +228,26 @@ | |
| 217 | " event.bgcolor," /* Background color */ |
| 218 | " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" |
| 219 | " AND tagxref.rid=mlink.mid)" /* Tags */ |
| 220 | " FROM mlink, event" |
| 221 | " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" |
| 222 | " AND event.objid=mlink.mid" |
| 223 | " ORDER BY event.mtime DESC /*sort*/", |
| 224 | TAG_BRANCH, |
| 225 | zFilename |
| 226 | ); |
| 227 | blob_zero(&title); |
| 228 | blob_appendf(&title, "History of "); |
| 229 | hyperlinked_path(zFilename, &title, 0); |
| 230 | @ <h2>%b(&title)</h2> |
| 231 | blob_reset(&title); |
| 232 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -186,27 +186,38 @@ | |
| 186 | |
| 187 | /* |
| 188 | ** WEBPAGE: finfo |
| 189 | ** URL: /finfo?name=FILENAME |
| 190 | ** |
| 191 | ** Show the change history for a single file. |
| 192 | ** |
| 193 | ** Additional query parameters: |
| 194 | ** |
| 195 | ** a=DATE Only show changes after DATE |
| 196 | ** b=DATE Only show changes before DATE |
| 197 | ** n=NUM Show the first NUM changes only |
| 198 | */ |
| 199 | void finfo_page(void){ |
| 200 | Stmt q; |
| 201 | const char *zFilename; |
| 202 | char zPrevDate[20]; |
| 203 | const char *zA; |
| 204 | const char *zB; |
| 205 | int n; |
| 206 | Blob title; |
| 207 | Blob sql; |
| 208 | GraphContext *pGraph; |
| 209 | |
| 210 | login_check_credentials(); |
| 211 | if( !g.okRead ){ login_needed(); return; } |
| 212 | style_header("File History"); |
| 213 | login_anonymous_available(); |
| 214 | |
| 215 | zPrevDate[0] = 0; |
| 216 | zFilename = PD("name",""); |
| 217 | blob_zero(&sql); |
| 218 | blob_appendf(&sql, |
| 219 | "SELECT" |
| 220 | " datetime(event.mtime,'localtime')," /* Date of change */ |
| 221 | " coalesce(event.ecomment, event.comment)," /* Check-in comment */ |
| 222 | " coalesce(event.euser, event.user)," /* User who made chng */ |
| 223 | " mlink.pid," /* Parent rid */ |
| @@ -217,15 +228,26 @@ | |
| 228 | " event.bgcolor," /* Background color */ |
| 229 | " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" |
| 230 | " AND tagxref.rid=mlink.mid)" /* Tags */ |
| 231 | " FROM mlink, event" |
| 232 | " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" |
| 233 | " AND event.objid=mlink.mid", |
| 234 | TAG_BRANCH, |
| 235 | zFilename |
| 236 | ); |
| 237 | if( (zA = P("a"))!=0 ){ |
| 238 | blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zA); |
| 239 | } |
| 240 | if( (zB = P("b"))!=0 ){ |
| 241 | blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zB); |
| 242 | } |
| 243 | blob_appendf(&sql," ORDER BY event.mtime DESC /*sort*/"); |
| 244 | if( (n = atoi(PD("n","0")))>0 ){ |
| 245 | blob_appendf(&sql, " LIMIT %d", n); |
| 246 | } |
| 247 | db_prepare(&q, blob_str(&sql)); |
| 248 | blob_reset(&sql); |
| 249 | blob_zero(&title); |
| 250 | blob_appendf(&title, "History of "); |
| 251 | hyperlinked_path(zFilename, &title, 0); |
| 252 | @ <h2>%b(&title)</h2> |
| 253 | blob_reset(&title); |
| 254 |
+28
-13
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -69,10 +69,15 @@ | ||
| 69 | 69 | int nRow; /* Number of rows */ |
| 70 | 70 | int nHash; /* Number of slots in apHash[] */ |
| 71 | 71 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 72 | 72 | }; |
| 73 | 73 | |
| 74 | +/* Options for graph_finish(): | |
| 75 | +*/ | |
| 76 | +#define GRAPH_DISJOINT 0x0001 /* All elements disjoint */ | |
| 77 | +#define GRAPH_ISOLATE_MERGEIN 0x0002 /* Merge-in nodes isolated */ | |
| 78 | + | |
| 74 | 79 | #endif |
| 75 | 80 | |
| 76 | 81 | /* |
| 77 | 82 | ** Malloc for zeroed space. Panic if unable to provide the |
| 78 | 83 | ** requested space. |
| @@ -306,20 +311,35 @@ | ||
| 306 | 311 | } |
| 307 | 312 | } |
| 308 | 313 | pChild->mergeIn[pParent->mergeOut/4] = (pParent->mergeOut&3)+1; |
| 309 | 314 | } |
| 310 | 315 | |
| 316 | +/* | |
| 317 | +** Compute the maximum rail number. | |
| 318 | +*/ | |
| 319 | +static void find_max_rail(GraphContext *p){ | |
| 320 | + GraphRow *pRow; | |
| 321 | + p->mxRail = 0; | |
| 322 | + for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 323 | + if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; | |
| 324 | + if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4; | |
| 325 | + while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>((1<<(p->mxRail+1))-1) ){ | |
| 326 | + p->mxRail++; | |
| 327 | + } | |
| 328 | + } | |
| 329 | +} | |
| 330 | + | |
| 311 | 331 | |
| 312 | 332 | /* |
| 313 | 333 | ** Compute the complete graph |
| 314 | 334 | */ |
| 315 | 335 | void graph_finish(GraphContext *p, int omitDescenders){ |
| 316 | 336 | GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent; |
| 317 | 337 | int i; |
| 318 | 338 | u32 mask; |
| 319 | 339 | u32 inUse; |
| 320 | - int hasDup = 0; /* True if one or more isDup entries */ | |
| 340 | + int hasDup = 0; /* True if one or more isDup entries */ | |
| 321 | 341 | const char *zTrunk; |
| 322 | 342 | |
| 323 | 343 | if( p==0 || p->pFirst==0 || p->nErr ) return; |
| 324 | 344 | p->nErr = 1; /* Assume an error until proven otherwise */ |
| 325 | 345 | |
| @@ -435,14 +455,11 @@ | ||
| 435 | 455 | } |
| 436 | 456 | } |
| 437 | 457 | continue; |
| 438 | 458 | } |
| 439 | 459 | if( pRow->isDup ){ |
| 440 | - pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0); | |
| 441 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 442 | - pDesc = pRow; | |
| 443 | - pParent = 0; | |
| 460 | + continue; | |
| 444 | 461 | }else{ |
| 445 | 462 | assert( pRow->nParent>0 ); |
| 446 | 463 | parentRid = pRow->aParent[0]; |
| 447 | 464 | pParent = hashFind(p, parentRid); |
| 448 | 465 | if( pParent==0 ){ |
| @@ -515,12 +532,17 @@ | ||
| 515 | 532 | |
| 516 | 533 | /* |
| 517 | 534 | ** Insert merge rails from primaries to duplicates. |
| 518 | 535 | */ |
| 519 | 536 | if( hasDup ){ |
| 537 | + int dupRail; | |
| 538 | + find_max_rail(p); | |
| 539 | + dupRail = ++p->mxRail; | |
| 540 | + if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 520 | 541 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 521 | 542 | if( !pRow->isDup ) continue; |
| 543 | + pRow->iRail = dupRail; | |
| 522 | 544 | pDesc = hashFind(p, pRow->rid); |
| 523 | 545 | assert( pDesc!=0 && pDesc!=pRow ); |
| 524 | 546 | createMergeRiser(p, pDesc, pRow); |
| 525 | 547 | } |
| 526 | 548 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| @@ -527,15 +549,8 @@ | ||
| 527 | 549 | } |
| 528 | 550 | |
| 529 | 551 | /* |
| 530 | 552 | ** Find the maximum rail number. |
| 531 | 553 | */ |
| 532 | - p->mxRail = 0; | |
| 533 | - for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 534 | - if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; | |
| 535 | - if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4; | |
| 536 | - while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>((1<<(p->mxRail+1))-1) ){ | |
| 537 | - p->mxRail++; | |
| 538 | - } | |
| 539 | - } | |
| 554 | + find_max_rail(p); | |
| 540 | 555 | p->nErr = 0; |
| 541 | 556 | } |
| 542 | 557 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -69,10 +69,15 @@ | |
| 69 | int nRow; /* Number of rows */ |
| 70 | int nHash; /* Number of slots in apHash[] */ |
| 71 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 72 | }; |
| 73 | |
| 74 | #endif |
| 75 | |
| 76 | /* |
| 77 | ** Malloc for zeroed space. Panic if unable to provide the |
| 78 | ** requested space. |
| @@ -306,20 +311,35 @@ | |
| 306 | } |
| 307 | } |
| 308 | pChild->mergeIn[pParent->mergeOut/4] = (pParent->mergeOut&3)+1; |
| 309 | } |
| 310 | |
| 311 | |
| 312 | /* |
| 313 | ** Compute the complete graph |
| 314 | */ |
| 315 | void graph_finish(GraphContext *p, int omitDescenders){ |
| 316 | GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent; |
| 317 | int i; |
| 318 | u32 mask; |
| 319 | u32 inUse; |
| 320 | int hasDup = 0; /* True if one or more isDup entries */ |
| 321 | const char *zTrunk; |
| 322 | |
| 323 | if( p==0 || p->pFirst==0 || p->nErr ) return; |
| 324 | p->nErr = 1; /* Assume an error until proven otherwise */ |
| 325 | |
| @@ -435,14 +455,11 @@ | |
| 435 | } |
| 436 | } |
| 437 | continue; |
| 438 | } |
| 439 | if( pRow->isDup ){ |
| 440 | pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0); |
| 441 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 442 | pDesc = pRow; |
| 443 | pParent = 0; |
| 444 | }else{ |
| 445 | assert( pRow->nParent>0 ); |
| 446 | parentRid = pRow->aParent[0]; |
| 447 | pParent = hashFind(p, parentRid); |
| 448 | if( pParent==0 ){ |
| @@ -515,12 +532,17 @@ | |
| 515 | |
| 516 | /* |
| 517 | ** Insert merge rails from primaries to duplicates. |
| 518 | */ |
| 519 | if( hasDup ){ |
| 520 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 521 | if( !pRow->isDup ) continue; |
| 522 | pDesc = hashFind(p, pRow->rid); |
| 523 | assert( pDesc!=0 && pDesc!=pRow ); |
| 524 | createMergeRiser(p, pDesc, pRow); |
| 525 | } |
| 526 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| @@ -527,15 +549,8 @@ | |
| 527 | } |
| 528 | |
| 529 | /* |
| 530 | ** Find the maximum rail number. |
| 531 | */ |
| 532 | p->mxRail = 0; |
| 533 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 534 | if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; |
| 535 | if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4; |
| 536 | while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>((1<<(p->mxRail+1))-1) ){ |
| 537 | p->mxRail++; |
| 538 | } |
| 539 | } |
| 540 | p->nErr = 0; |
| 541 | } |
| 542 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -69,10 +69,15 @@ | |
| 69 | int nRow; /* Number of rows */ |
| 70 | int nHash; /* Number of slots in apHash[] */ |
| 71 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 72 | }; |
| 73 | |
| 74 | /* Options for graph_finish(): |
| 75 | */ |
| 76 | #define GRAPH_DISJOINT 0x0001 /* All elements disjoint */ |
| 77 | #define GRAPH_ISOLATE_MERGEIN 0x0002 /* Merge-in nodes isolated */ |
| 78 | |
| 79 | #endif |
| 80 | |
| 81 | /* |
| 82 | ** Malloc for zeroed space. Panic if unable to provide the |
| 83 | ** requested space. |
| @@ -306,20 +311,35 @@ | |
| 311 | } |
| 312 | } |
| 313 | pChild->mergeIn[pParent->mergeOut/4] = (pParent->mergeOut&3)+1; |
| 314 | } |
| 315 | |
| 316 | /* |
| 317 | ** Compute the maximum rail number. |
| 318 | */ |
| 319 | static void find_max_rail(GraphContext *p){ |
| 320 | GraphRow *pRow; |
| 321 | p->mxRail = 0; |
| 322 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 323 | if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; |
| 324 | if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4; |
| 325 | while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>((1<<(p->mxRail+1))-1) ){ |
| 326 | p->mxRail++; |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | |
| 332 | /* |
| 333 | ** Compute the complete graph |
| 334 | */ |
| 335 | void graph_finish(GraphContext *p, int omitDescenders){ |
| 336 | GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent; |
| 337 | int i; |
| 338 | u32 mask; |
| 339 | u32 inUse; |
| 340 | int hasDup = 0; /* True if one or more isDup entries */ |
| 341 | const char *zTrunk; |
| 342 | |
| 343 | if( p==0 || p->pFirst==0 || p->nErr ) return; |
| 344 | p->nErr = 1; /* Assume an error until proven otherwise */ |
| 345 | |
| @@ -435,14 +455,11 @@ | |
| 455 | } |
| 456 | } |
| 457 | continue; |
| 458 | } |
| 459 | if( pRow->isDup ){ |
| 460 | continue; |
| 461 | }else{ |
| 462 | assert( pRow->nParent>0 ); |
| 463 | parentRid = pRow->aParent[0]; |
| 464 | pParent = hashFind(p, parentRid); |
| 465 | if( pParent==0 ){ |
| @@ -515,12 +532,17 @@ | |
| 532 | |
| 533 | /* |
| 534 | ** Insert merge rails from primaries to duplicates. |
| 535 | */ |
| 536 | if( hasDup ){ |
| 537 | int dupRail; |
| 538 | find_max_rail(p); |
| 539 | dupRail = ++p->mxRail; |
| 540 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 541 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 542 | if( !pRow->isDup ) continue; |
| 543 | pRow->iRail = dupRail; |
| 544 | pDesc = hashFind(p, pRow->rid); |
| 545 | assert( pDesc!=0 && pDesc!=pRow ); |
| 546 | createMergeRiser(p, pDesc, pRow); |
| 547 | } |
| 548 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| @@ -527,15 +549,8 @@ | |
| 549 | } |
| 550 | |
| 551 | /* |
| 552 | ** Find the maximum rail number. |
| 553 | */ |
| 554 | find_max_rail(p); |
| 555 | p->nErr = 0; |
| 556 | } |
| 557 |