Fossil SCM

Enhancements to the /finfo page so that it follows the file across name changes if the ci=HASH query parameter is used.

drh 2020-10-17 14:57 trunk
Commit b54d9396f9de3cbcc47a210bf7c3f301e9d7033ff7d04d0393ebc61a203b7628
2 files changed +134 -37 +21 -14
+134 -37
--- src/finfo.c
+++ src/finfo.c
@@ -271,27 +271,42 @@
271271
/* Values for the debug= query parameter to finfo */
272272
#define FINFO_DEBUG_MLINK 0x01
273273
274274
/*
275275
** WEBPAGE: finfo
276
-** URL: /finfo?name=FILENAME
276
+** Usage:
277
+** * /finfo?name=FILENAME
278
+** * /finfo?name=FILENAME&ci=HASH
277279
**
278
-** Show the change history for a single file.
280
+** Show the change history for a single file. The name=FILENAME query
281
+** parameter gives the filename and is a required parameter. If the
282
+** ci=HASH parameter is also supplied, then the FILENAME,HASH combination
283
+** identifies a particular version of a file, and in that case all changes
284
+** to that one file version are tracked across both edits and renames.
285
+** If only the name=FILENAME parameter is supplied (if ci=HASH is omitted)
286
+** then the graph shows all changes to any file while it happened
287
+** to be called FILENAME and changes are not tracked across renames.
279288
**
280289
** Additional query parameters:
281290
**
282291
** a=DATETIME Only show changes after DATETIME
283292
** b=DATETIME Only show changes before DATETIME
284
-** m=HASH Mark this particular file version
293
+** ci=HASH identify a particular version of a file and then
294
+** track changes to that file across renames
295
+** m=HASH Mark this particular file version.
285296
** n=NUM Show the first NUM changes only
286297
** name=FILENAME (Required) name of file whose history to show
287298
** brbg Background color by branch name
288299
** ubg Background color by user name
289
-** from=HASH Ancestors of a particular check-in
300
+** from=HASH Ancestors only (not descendents) of the version of
301
+** the file in this particular check-in.
290302
** to=HASH If both from= and to= are supplied, only show those
291
-** changes on the direct path between them.
303
+** changes on the direct path between the two given
304
+** checkins.
292305
** showid Show RID values for debugging
306
+** showsql Show the SQL query used to gather the data for
307
+** the graph
293308
**
294309
** DATETIME may be in any of usual formats, including "now",
295310
** "YYYY-MM-DDTHH:MM:SS.SSS", "YYYYMMDDHHMM", and others.
296311
*/
297312
void finfo_page(void){
@@ -301,10 +316,12 @@
301316
const char *zA;
302317
const char *zB;
303318
int n;
304319
int ridFrom;
305320
int ridTo = 0;
321
+ int ridCi = 0;
322
+ const char *zCI = P("ci");
306323
int fnid;
307324
Blob title;
308325
Blob sql;
309326
HQuery url;
310327
GraphContext *pGraph;
@@ -320,14 +337,17 @@
320337
int selRid = 0; /* RID of the marked file version */
321338
322339
login_check_credentials();
323340
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
324341
fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
342
+ ridCi = zCI ? name_to_rid_www("ci") : 0;
325343
if( fnid==0 ){
326344
style_header("No such file");
345
+ }else if( ridCi==0 ){
346
+ style_header("All files named \"%s\"", zFilename);
327347
}else{
328
- style_header("History for %s", zFilename);
348
+ style_header("History of %s of %s",zFilename, zCI);
329349
}
330350
login_anonymous_available();
331351
tmFlags = timeline_ss_submenu();
332352
if( tmFlags & TIMELINE_COLUMNAR ){
333353
zStyle = "Columnar";
@@ -362,46 +382,113 @@
362382
compute_direct_ancestors(ridFrom);
363383
}
364384
}
365385
url_add_parameter(&url, "name", zFilename);
366386
blob_zero(&sql);
387
+ if( ridCi ){
388
+ /* If we will be tracking changes across renames, some extra temp
389
+ ** tables (implemented as CTEs) are required */
390
+ blob_append_sql(&sql,
391
+ /* The fns(fnid) table holds the list of all filename-IDs that
392
+ ** might possibly exist in the output. This is an optimization
393
+ ** used to reduce the size and computation efforts for subsequent
394
+ ** CTEs.
395
+ */
396
+ "WITH RECURSIVE fns(fnid) AS (\n"
397
+ " SELECT %d\n" /* <---- fnid */
398
+ " UNION\n"
399
+ " SELECT pfnid FROM mlink, fns\n"
400
+ " WHERE mlink.fnid=fns.fnid\n"
401
+ " AND pfnid>0\n"
402
+ "),\n"
403
+
404
+ /* The flink(fid,fnid,pfid,pfnid) table indicates that there
405
+ ** is an edit and/or rename arc connecting two files (fid,fnid)
406
+ ** and (pfid,pfnid). This is similar to the built-in mlink
407
+ ** table except that flink() is bidirectional. Also the pfnid
408
+ ** column is always set even no rename occurs.
409
+ */
410
+ "flink(fid,fnid,pfid,pfnid) AS (\n"
411
+ " SELECT fid, fnid, pid,\n"
412
+ " CASE WHEN pfnid>0 THEN pfnid ELSE fnid END\n"
413
+ " FROM mlink\n"
414
+ " WHERE NOT isaux AND fid>0 AND pid>0 AND fnid IN fns\n"
415
+ " UNION\n"
416
+ " SELECT pid,\n"
417
+ " CASE WHEN pfnid>0 THEN pfnid ELSE fnid END,\n"
418
+ " fid, fnid\n"
419
+ " FROM mlink\n"
420
+ " WHERE NOT isaux AND pid>0 AND fid>0 AND fnid IN fns\n"
421
+ "),\n"
422
+
423
+ /* The clade(fid,fnid) table is the set of all (fid,fnid) pairs
424
+ ** that should participate in the output. Clade is computed by
425
+ ** walking the graph formed by the flink table.
426
+ */
427
+ "clade(fid,fnid) AS (\n"
428
+ " SELECT blob.rid, %d FROM blob\n" /* %d is fnid */
429
+ " WHERE blob.uuid=(SELECT uuid FROM files_of_checkin(%Q)\n"
430
+ " WHERE filename=%Q)\n" /* %Q is the filename */
431
+ " UNION\n"
432
+ " SELECT flink.fid, flink.fnid\n"
433
+ " FROM clade, flink\n"
434
+ " WHERE clade.fid=flink.pfid AND clade.fnid=flink.pfnid\n"
435
+ ")\n",
436
+ fnid, fnid, zCI, zFilename
437
+ );
438
+ }else{
439
+ /* This is the case for all files with a given name. We will still
440
+ ** create a "clade(fid,fnid)" table that identifies all participates
441
+ ** in the output graph, so that subsequent queries can all be the same,
442
+ ** but in the case the clade table is much simplier, being just a
443
+ ** single direct query against the mlink table.
444
+ */
445
+ blob_append_sql(&sql,
446
+ "WITH clade(fid,fnid) AS (\n"
447
+ " SELECT DISTINCT fid, %d\n"
448
+ " FROM mlink\n"
449
+ " WHERE fnid=%d)",
450
+ fnid, fnid
451
+ );
452
+ }
367453
blob_append_sql(&sql,
368
- "SELECT"
369
- " datetime(min(event.mtime),toLocal())," /* Date of change */
370
- " coalesce(event.ecomment, event.comment)," /* Check-in comment */
371
- " coalesce(event.euser, event.user)," /* User who made chng */
372
- " mlink.pid," /* Parent file rid */
373
- " mlink.fid," /* File rid */
374
- " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file hash */
375
- " blob.uuid," /* Current file hash */
376
- " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in hash */
377
- " event.bgcolor," /* Background color */
378
- " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
379
- " AND tagxref.rid=mlink.mid)," /* Branchname */
380
- " mlink.mid," /* check-in ID */
381
- " mlink.pfnid," /* Previous filename */
382
- " blob.size" /* File size */
383
- " FROM mlink, event, blob"
384
- " WHERE mlink.fnid=%d"
385
- " AND event.objid=mlink.mid"
386
- " AND mlink.fid=blob.rid",
387
- TAG_BRANCH, fnid
454
+ "SELECT\n"
455
+ " datetime(min(event.mtime),toLocal()),\n" /* Date of change */
456
+ " coalesce(event.ecomment, event.comment),\n" /* Check-in comment */
457
+ " coalesce(event.euser, event.user),\n" /* User who made chng */
458
+ " mlink.pid,\n" /* Parent file rid */
459
+ " mlink.fid,\n" /* File rid */
460
+ " (SELECT uuid FROM blob WHERE rid=mlink.pid),\n" /* Parent file hash */
461
+ " blob.uuid,\n" /* Current file hash */
462
+ " (SELECT uuid FROM blob WHERE rid=mlink.mid),\n" /* Check-in hash */
463
+ " event.bgcolor,\n" /* Background color */
464
+ " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
465
+ " AND tagxref.rid=mlink.mid),\n" /* Branchname */
466
+ " mlink.mid,\n" /* check-in ID */
467
+ " mlink.pfnid,\n" /* Previous filename */
468
+ " blob.size,\n" /* File size */
469
+ " mlink.fnid\n" /* Current filename */
470
+ "FROM clade, mlink, event, blob\n"
471
+ "WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n"
472
+ " AND event.objid=mlink.mid\n"
473
+ " AND blob.rid=clade.fid\n",
474
+ TAG_BRANCH
388475
);
389476
if( (zA = P("a"))!=0 ){
390
- blob_append_sql(&sql, " AND event.mtime>=%.16g",
477
+ blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
391478
symbolic_name_to_mtime(zA,0));
392479
url_add_parameter(&url, "a", zA);
393480
}
394481
if( (zB = P("b"))!=0 ){
395
- blob_append_sql(&sql, " AND event.mtime<=%.16g",
482
+ blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
396483
symbolic_name_to_mtime(zB,0));
397484
url_add_parameter(&url, "b", zB);
398485
}
399486
if( ridFrom ){
400487
blob_append_sql(&sql,
401
- " AND mlink.mid IN (SELECT rid FROM ancestor)"
402
- " GROUP BY mlink.fid"
488
+ " AND mlink.mid IN (SELECT rid FROM ancestor)\n"
489
+ "GROUP BY mlink.fid\n"
403490
);
404491
}else{
405492
/* We only want each version of a file to appear on the graph once,
406493
** at its earliest appearance. All the other times that it gets merged
407494
** into this or that branch can be ignored. An exception is for when
@@ -409,22 +496,23 @@
409496
** is deleted in multiple places, we want to show each deletion, so
410497
** use a "fake fid" which is derived from the parent-fid for grouping.
411498
** The same fake-fid must be used on the graph.
412499
*/
413500
blob_append_sql(&sql,
414
- " GROUP BY"
415
- " CASE WHEN mlink.fid>0 THEN mlink.fid ELSE mlink.pid+1000000000 END"
501
+ "GROUP BY"
502
+ " CASE WHEN mlink.fid>0 THEN mlink.fid ELSE mlink.pid+1000000000 END\n"
416503
);
417504
}
418
- blob_append_sql(&sql, " ORDER BY event.mtime DESC /*sort*/");
505
+ blob_append_sql(&sql, "ORDER BY event.mtime DESC");
419506
if( (n = atoi(PD("n","0")))>0 ){
420507
blob_append_sql(&sql, " LIMIT %d", n);
421508
url_add_parameter(&url, "n", P("n"));
422509
}
510
+ blob_append_sql(&sql, " /*sort*/\n");
423511
db_prepare(&q, "%s", blob_sql_text(&sql));
424512
if( P("showsql")!=0 ){
425
- @ <p>SQL: %h(blob_str(&sql))</p>
513
+ @ <p>SQL: <blockquote><pre>%h(blob_str(&sql))</blockquote></pre>
426514
}
427515
zMark = P("m");
428516
if( zMark ){
429517
selRid = symbolic_name_to_rid(zMark, "*");
430518
}
@@ -452,10 +540,16 @@
452540
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridTo);
453541
zLink = href("%R/info/%!S", zUuid);
454542
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
455543
fossil_free(zUuid);
456544
}
545
+ }else if( ridCi ){
546
+ blob_appendf(&title, "History of the file that is called ");
547
+ hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
548
+ if( fShowId ) blob_appendf(&title, " (%d)", fnid);
549
+ blob_appendf(&title, " at checkin %z%h</a>",
550
+ href("%R/info?name=%t",zCI), zCI);
457551
}else{
458552
blob_appendf(&title, "History for ");
459553
hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
460554
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
461555
}
@@ -492,10 +586,11 @@
492586
const char *zBgClr = db_column_text(&q, 8);
493587
const char *zBr = db_column_text(&q, 9);
494588
int fmid = db_column_int(&q, 10);
495589
int pfnid = db_column_int(&q, 11);
496590
int szFile = db_column_int(&q, 12);
591
+ int fnid = db_column_int(&q, 13);
497592
int gidx;
498593
char zTime[10];
499594
int nParent = 0;
500595
int aParent[GR_MAX_RAIL];
501596
@@ -565,11 +660,12 @@
565660
cgi_printf("<span class='clutter' id='detail-%d'>",frid);
566661
}
567662
cgi_printf("<span class='timeline%sDetail'>", zStyle);
568663
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
569664
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
570
- @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
665
+ @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))\
666
+ @ [%S(zUuid)]</a>
571667
if( fShowId ){
572668
int srcId = delta_source_rid(frid);
573669
if( srcId>0 ){
574670
@ id:&nbsp;%d(frid)&larr;%d(srcId)
575671
}else{
@@ -626,11 +722,12 @@
626722
@ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
627723
if( fpid>0 ){
628724
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
629725
}
630726
if( fileedit_is_editable(zFilename) ){
631
- @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFilename,zCkin))[edit]</a>
727
+ @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFilename,zCkin))\
728
+ @ [edit]</a>
632729
}
633730
@ </span></span>
634731
}
635732
if( fDebug & FINFO_DEBUG_MLINK ){
636733
int ii;
@@ -640,11 +737,11 @@
640737
@ parents=%d(aParent[0])
641738
for(ii=1; ii<nParent; ii++){
642739
@ %d(aParent[ii])
643740
}
644741
}
645
- zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
742
+ zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFilename,zCkin);
646743
@ %z(zAncLink)[ancestry]</a>
647744
}
648745
tag_private_status(frid);
649746
/* End timelineDetail */
650747
if( tmFlags & TIMELINE_COMPACT ){
651748
--- src/finfo.c
+++ src/finfo.c
@@ -271,27 +271,42 @@
271 /* Values for the debug= query parameter to finfo */
272 #define FINFO_DEBUG_MLINK 0x01
273
274 /*
275 ** WEBPAGE: finfo
276 ** URL: /finfo?name=FILENAME
 
 
277 **
278 ** Show the change history for a single file.
 
 
 
 
 
 
 
279 **
280 ** Additional query parameters:
281 **
282 ** a=DATETIME Only show changes after DATETIME
283 ** b=DATETIME Only show changes before DATETIME
284 ** m=HASH Mark this particular file version
 
 
285 ** n=NUM Show the first NUM changes only
286 ** name=FILENAME (Required) name of file whose history to show
287 ** brbg Background color by branch name
288 ** ubg Background color by user name
289 ** from=HASH Ancestors of a particular check-in
 
290 ** to=HASH If both from= and to= are supplied, only show those
291 ** changes on the direct path between them.
 
292 ** showid Show RID values for debugging
 
 
293 **
294 ** DATETIME may be in any of usual formats, including "now",
295 ** "YYYY-MM-DDTHH:MM:SS.SSS", "YYYYMMDDHHMM", and others.
296 */
297 void finfo_page(void){
@@ -301,10 +316,12 @@
301 const char *zA;
302 const char *zB;
303 int n;
304 int ridFrom;
305 int ridTo = 0;
 
 
306 int fnid;
307 Blob title;
308 Blob sql;
309 HQuery url;
310 GraphContext *pGraph;
@@ -320,14 +337,17 @@
320 int selRid = 0; /* RID of the marked file version */
321
322 login_check_credentials();
323 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
324 fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
 
325 if( fnid==0 ){
326 style_header("No such file");
 
 
327 }else{
328 style_header("History for %s", zFilename);
329 }
330 login_anonymous_available();
331 tmFlags = timeline_ss_submenu();
332 if( tmFlags & TIMELINE_COLUMNAR ){
333 zStyle = "Columnar";
@@ -362,46 +382,113 @@
362 compute_direct_ancestors(ridFrom);
363 }
364 }
365 url_add_parameter(&url, "name", zFilename);
366 blob_zero(&sql);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367 blob_append_sql(&sql,
368 "SELECT"
369 " datetime(min(event.mtime),toLocal())," /* Date of change */
370 " coalesce(event.ecomment, event.comment)," /* Check-in comment */
371 " coalesce(event.euser, event.user)," /* User who made chng */
372 " mlink.pid," /* Parent file rid */
373 " mlink.fid," /* File rid */
374 " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file hash */
375 " blob.uuid," /* Current file hash */
376 " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in hash */
377 " event.bgcolor," /* Background color */
378 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
379 " AND tagxref.rid=mlink.mid)," /* Branchname */
380 " mlink.mid," /* check-in ID */
381 " mlink.pfnid," /* Previous filename */
382 " blob.size" /* File size */
383 " FROM mlink, event, blob"
384 " WHERE mlink.fnid=%d"
385 " AND event.objid=mlink.mid"
386 " AND mlink.fid=blob.rid",
387 TAG_BRANCH, fnid
 
388 );
389 if( (zA = P("a"))!=0 ){
390 blob_append_sql(&sql, " AND event.mtime>=%.16g",
391 symbolic_name_to_mtime(zA,0));
392 url_add_parameter(&url, "a", zA);
393 }
394 if( (zB = P("b"))!=0 ){
395 blob_append_sql(&sql, " AND event.mtime<=%.16g",
396 symbolic_name_to_mtime(zB,0));
397 url_add_parameter(&url, "b", zB);
398 }
399 if( ridFrom ){
400 blob_append_sql(&sql,
401 " AND mlink.mid IN (SELECT rid FROM ancestor)"
402 " GROUP BY mlink.fid"
403 );
404 }else{
405 /* We only want each version of a file to appear on the graph once,
406 ** at its earliest appearance. All the other times that it gets merged
407 ** into this or that branch can be ignored. An exception is for when
@@ -409,22 +496,23 @@
409 ** is deleted in multiple places, we want to show each deletion, so
410 ** use a "fake fid" which is derived from the parent-fid for grouping.
411 ** The same fake-fid must be used on the graph.
412 */
413 blob_append_sql(&sql,
414 " GROUP BY"
415 " CASE WHEN mlink.fid>0 THEN mlink.fid ELSE mlink.pid+1000000000 END"
416 );
417 }
418 blob_append_sql(&sql, " ORDER BY event.mtime DESC /*sort*/");
419 if( (n = atoi(PD("n","0")))>0 ){
420 blob_append_sql(&sql, " LIMIT %d", n);
421 url_add_parameter(&url, "n", P("n"));
422 }
 
423 db_prepare(&q, "%s", blob_sql_text(&sql));
424 if( P("showsql")!=0 ){
425 @ <p>SQL: %h(blob_str(&sql))</p>
426 }
427 zMark = P("m");
428 if( zMark ){
429 selRid = symbolic_name_to_rid(zMark, "*");
430 }
@@ -452,10 +540,16 @@
452 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridTo);
453 zLink = href("%R/info/%!S", zUuid);
454 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
455 fossil_free(zUuid);
456 }
 
 
 
 
 
 
457 }else{
458 blob_appendf(&title, "History for ");
459 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
460 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
461 }
@@ -492,10 +586,11 @@
492 const char *zBgClr = db_column_text(&q, 8);
493 const char *zBr = db_column_text(&q, 9);
494 int fmid = db_column_int(&q, 10);
495 int pfnid = db_column_int(&q, 11);
496 int szFile = db_column_int(&q, 12);
 
497 int gidx;
498 char zTime[10];
499 int nParent = 0;
500 int aParent[GR_MAX_RAIL];
501
@@ -565,11 +660,12 @@
565 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
566 }
567 cgi_printf("<span class='timeline%sDetail'>", zStyle);
568 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
569 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
570 @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
 
571 if( fShowId ){
572 int srcId = delta_source_rid(frid);
573 if( srcId>0 ){
574 @ id:&nbsp;%d(frid)&larr;%d(srcId)
575 }else{
@@ -626,11 +722,12 @@
626 @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
627 if( fpid>0 ){
628 @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
629 }
630 if( fileedit_is_editable(zFilename) ){
631 @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFilename,zCkin))[edit]</a>
 
632 }
633 @ </span></span>
634 }
635 if( fDebug & FINFO_DEBUG_MLINK ){
636 int ii;
@@ -640,11 +737,11 @@
640 @ parents=%d(aParent[0])
641 for(ii=1; ii<nParent; ii++){
642 @ %d(aParent[ii])
643 }
644 }
645 zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
646 @ %z(zAncLink)[ancestry]</a>
647 }
648 tag_private_status(frid);
649 /* End timelineDetail */
650 if( tmFlags & TIMELINE_COMPACT ){
651
--- src/finfo.c
+++ src/finfo.c
@@ -271,27 +271,42 @@
271 /* Values for the debug= query parameter to finfo */
272 #define FINFO_DEBUG_MLINK 0x01
273
274 /*
275 ** WEBPAGE: finfo
276 ** Usage:
277 ** * /finfo?name=FILENAME
278 ** * /finfo?name=FILENAME&ci=HASH
279 **
280 ** Show the change history for a single file. The name=FILENAME query
281 ** parameter gives the filename and is a required parameter. If the
282 ** ci=HASH parameter is also supplied, then the FILENAME,HASH combination
283 ** identifies a particular version of a file, and in that case all changes
284 ** to that one file version are tracked across both edits and renames.
285 ** If only the name=FILENAME parameter is supplied (if ci=HASH is omitted)
286 ** then the graph shows all changes to any file while it happened
287 ** to be called FILENAME and changes are not tracked across renames.
288 **
289 ** Additional query parameters:
290 **
291 ** a=DATETIME Only show changes after DATETIME
292 ** b=DATETIME Only show changes before DATETIME
293 ** ci=HASH identify a particular version of a file and then
294 ** track changes to that file across renames
295 ** m=HASH Mark this particular file version.
296 ** n=NUM Show the first NUM changes only
297 ** name=FILENAME (Required) name of file whose history to show
298 ** brbg Background color by branch name
299 ** ubg Background color by user name
300 ** from=HASH Ancestors only (not descendents) of the version of
301 ** the file in this particular check-in.
302 ** to=HASH If both from= and to= are supplied, only show those
303 ** changes on the direct path between the two given
304 ** checkins.
305 ** showid Show RID values for debugging
306 ** showsql Show the SQL query used to gather the data for
307 ** the graph
308 **
309 ** DATETIME may be in any of usual formats, including "now",
310 ** "YYYY-MM-DDTHH:MM:SS.SSS", "YYYYMMDDHHMM", and others.
311 */
312 void finfo_page(void){
@@ -301,10 +316,12 @@
316 const char *zA;
317 const char *zB;
318 int n;
319 int ridFrom;
320 int ridTo = 0;
321 int ridCi = 0;
322 const char *zCI = P("ci");
323 int fnid;
324 Blob title;
325 Blob sql;
326 HQuery url;
327 GraphContext *pGraph;
@@ -320,14 +337,17 @@
337 int selRid = 0; /* RID of the marked file version */
338
339 login_check_credentials();
340 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
341 fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
342 ridCi = zCI ? name_to_rid_www("ci") : 0;
343 if( fnid==0 ){
344 style_header("No such file");
345 }else if( ridCi==0 ){
346 style_header("All files named \"%s\"", zFilename);
347 }else{
348 style_header("History of %s of %s",zFilename, zCI);
349 }
350 login_anonymous_available();
351 tmFlags = timeline_ss_submenu();
352 if( tmFlags & TIMELINE_COLUMNAR ){
353 zStyle = "Columnar";
@@ -362,46 +382,113 @@
382 compute_direct_ancestors(ridFrom);
383 }
384 }
385 url_add_parameter(&url, "name", zFilename);
386 blob_zero(&sql);
387 if( ridCi ){
388 /* If we will be tracking changes across renames, some extra temp
389 ** tables (implemented as CTEs) are required */
390 blob_append_sql(&sql,
391 /* The fns(fnid) table holds the list of all filename-IDs that
392 ** might possibly exist in the output. This is an optimization
393 ** used to reduce the size and computation efforts for subsequent
394 ** CTEs.
395 */
396 "WITH RECURSIVE fns(fnid) AS (\n"
397 " SELECT %d\n" /* <---- fnid */
398 " UNION\n"
399 " SELECT pfnid FROM mlink, fns\n"
400 " WHERE mlink.fnid=fns.fnid\n"
401 " AND pfnid>0\n"
402 "),\n"
403
404 /* The flink(fid,fnid,pfid,pfnid) table indicates that there
405 ** is an edit and/or rename arc connecting two files (fid,fnid)
406 ** and (pfid,pfnid). This is similar to the built-in mlink
407 ** table except that flink() is bidirectional. Also the pfnid
408 ** column is always set even no rename occurs.
409 */
410 "flink(fid,fnid,pfid,pfnid) AS (\n"
411 " SELECT fid, fnid, pid,\n"
412 " CASE WHEN pfnid>0 THEN pfnid ELSE fnid END\n"
413 " FROM mlink\n"
414 " WHERE NOT isaux AND fid>0 AND pid>0 AND fnid IN fns\n"
415 " UNION\n"
416 " SELECT pid,\n"
417 " CASE WHEN pfnid>0 THEN pfnid ELSE fnid END,\n"
418 " fid, fnid\n"
419 " FROM mlink\n"
420 " WHERE NOT isaux AND pid>0 AND fid>0 AND fnid IN fns\n"
421 "),\n"
422
423 /* The clade(fid,fnid) table is the set of all (fid,fnid) pairs
424 ** that should participate in the output. Clade is computed by
425 ** walking the graph formed by the flink table.
426 */
427 "clade(fid,fnid) AS (\n"
428 " SELECT blob.rid, %d FROM blob\n" /* %d is fnid */
429 " WHERE blob.uuid=(SELECT uuid FROM files_of_checkin(%Q)\n"
430 " WHERE filename=%Q)\n" /* %Q is the filename */
431 " UNION\n"
432 " SELECT flink.fid, flink.fnid\n"
433 " FROM clade, flink\n"
434 " WHERE clade.fid=flink.pfid AND clade.fnid=flink.pfnid\n"
435 ")\n",
436 fnid, fnid, zCI, zFilename
437 );
438 }else{
439 /* This is the case for all files with a given name. We will still
440 ** create a "clade(fid,fnid)" table that identifies all participates
441 ** in the output graph, so that subsequent queries can all be the same,
442 ** but in the case the clade table is much simplier, being just a
443 ** single direct query against the mlink table.
444 */
445 blob_append_sql(&sql,
446 "WITH clade(fid,fnid) AS (\n"
447 " SELECT DISTINCT fid, %d\n"
448 " FROM mlink\n"
449 " WHERE fnid=%d)",
450 fnid, fnid
451 );
452 }
453 blob_append_sql(&sql,
454 "SELECT\n"
455 " datetime(min(event.mtime),toLocal()),\n" /* Date of change */
456 " coalesce(event.ecomment, event.comment),\n" /* Check-in comment */
457 " coalesce(event.euser, event.user),\n" /* User who made chng */
458 " mlink.pid,\n" /* Parent file rid */
459 " mlink.fid,\n" /* File rid */
460 " (SELECT uuid FROM blob WHERE rid=mlink.pid),\n" /* Parent file hash */
461 " blob.uuid,\n" /* Current file hash */
462 " (SELECT uuid FROM blob WHERE rid=mlink.mid),\n" /* Check-in hash */
463 " event.bgcolor,\n" /* Background color */
464 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
465 " AND tagxref.rid=mlink.mid),\n" /* Branchname */
466 " mlink.mid,\n" /* check-in ID */
467 " mlink.pfnid,\n" /* Previous filename */
468 " blob.size,\n" /* File size */
469 " mlink.fnid\n" /* Current filename */
470 "FROM clade, mlink, event, blob\n"
471 "WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n"
472 " AND event.objid=mlink.mid\n"
473 " AND blob.rid=clade.fid\n",
474 TAG_BRANCH
475 );
476 if( (zA = P("a"))!=0 ){
477 blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
478 symbolic_name_to_mtime(zA,0));
479 url_add_parameter(&url, "a", zA);
480 }
481 if( (zB = P("b"))!=0 ){
482 blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
483 symbolic_name_to_mtime(zB,0));
484 url_add_parameter(&url, "b", zB);
485 }
486 if( ridFrom ){
487 blob_append_sql(&sql,
488 " AND mlink.mid IN (SELECT rid FROM ancestor)\n"
489 "GROUP BY mlink.fid\n"
490 );
491 }else{
492 /* We only want each version of a file to appear on the graph once,
493 ** at its earliest appearance. All the other times that it gets merged
494 ** into this or that branch can be ignored. An exception is for when
@@ -409,22 +496,23 @@
496 ** is deleted in multiple places, we want to show each deletion, so
497 ** use a "fake fid" which is derived from the parent-fid for grouping.
498 ** The same fake-fid must be used on the graph.
499 */
500 blob_append_sql(&sql,
501 "GROUP BY"
502 " CASE WHEN mlink.fid>0 THEN mlink.fid ELSE mlink.pid+1000000000 END\n"
503 );
504 }
505 blob_append_sql(&sql, "ORDER BY event.mtime DESC");
506 if( (n = atoi(PD("n","0")))>0 ){
507 blob_append_sql(&sql, " LIMIT %d", n);
508 url_add_parameter(&url, "n", P("n"));
509 }
510 blob_append_sql(&sql, " /*sort*/\n");
511 db_prepare(&q, "%s", blob_sql_text(&sql));
512 if( P("showsql")!=0 ){
513 @ <p>SQL: <blockquote><pre>%h(blob_str(&sql))</blockquote></pre>
514 }
515 zMark = P("m");
516 if( zMark ){
517 selRid = symbolic_name_to_rid(zMark, "*");
518 }
@@ -452,10 +540,16 @@
540 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridTo);
541 zLink = href("%R/info/%!S", zUuid);
542 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
543 fossil_free(zUuid);
544 }
545 }else if( ridCi ){
546 blob_appendf(&title, "History of the file that is called ");
547 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
548 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
549 blob_appendf(&title, " at checkin %z%h</a>",
550 href("%R/info?name=%t",zCI), zCI);
551 }else{
552 blob_appendf(&title, "History for ");
553 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
554 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
555 }
@@ -492,10 +586,11 @@
586 const char *zBgClr = db_column_text(&q, 8);
587 const char *zBr = db_column_text(&q, 9);
588 int fmid = db_column_int(&q, 10);
589 int pfnid = db_column_int(&q, 11);
590 int szFile = db_column_int(&q, 12);
591 int fnid = db_column_int(&q, 13);
592 int gidx;
593 char zTime[10];
594 int nParent = 0;
595 int aParent[GR_MAX_RAIL];
596
@@ -565,11 +660,12 @@
660 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
661 }
662 cgi_printf("<span class='timeline%sDetail'>", zStyle);
663 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
664 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
665 @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))\
666 @ [%S(zUuid)]</a>
667 if( fShowId ){
668 int srcId = delta_source_rid(frid);
669 if( srcId>0 ){
670 @ id:&nbsp;%d(frid)&larr;%d(srcId)
671 }else{
@@ -626,11 +722,12 @@
722 @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
723 if( fpid>0 ){
724 @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
725 }
726 if( fileedit_is_editable(zFilename) ){
727 @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFilename,zCkin))\
728 @ [edit]</a>
729 }
730 @ </span></span>
731 }
732 if( fDebug & FINFO_DEBUG_MLINK ){
733 int ii;
@@ -640,11 +737,11 @@
737 @ parents=%d(aParent[0])
738 for(ii=1; ii<nParent; ii++){
739 @ %d(aParent[ii])
740 }
741 }
742 zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFilename,zCkin);
743 @ %z(zAncLink)[ancestry]</a>
744 }
745 tag_private_status(frid);
746 /* End timelineDetail */
747 if( tmFlags & TIMELINE_COMPACT ){
748
+21 -14
--- src/info.c
+++ src/info.c
@@ -368,10 +368,11 @@
368368
/*
369369
** Write a line of web-page output that shows changes that have occurred
370370
** to a file between two check-ins.
371371
*/
372372
static void append_file_change_line(
373
+ const char *zCkin, /* The checkin on which the change occurs */
373374
const char *zName, /* Name of the file that has changed */
374375
const char *zOld, /* blob.uuid before change. NULL for added files */
375376
const char *zNew, /* blob.uuid after change. NULL for deletes */
376377
const char *zOldName, /* Prior name. NULL if no name change. */
377378
u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */
@@ -401,19 +402,23 @@
401402
append_diff(zOld, zNew, diffFlags, pRe);
402403
}
403404
}else{
404405
if( zOld && zNew ){
405406
if( fossil_strcmp(zOld, zNew)!=0 ){
406
- @ Modified %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
407
+ @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
408
+ @ %h(zName)</a>
407409
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
408410
@ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
409411
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
410412
@ Name change
411
- @ from %z(href("%R/finfo?name=%T&m=%!S",zOldName,zOld))%h(zOldName)</a>
412
- @ to %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>.
413
+ @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\
414
+ @ %h(zOldName)</a>
415
+ @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
416
+ @ %h(zName)</a>.
413417
}else{
414
- @ %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a> became
418
+ @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
419
+ @ %h(zName)</a> became
415420
if( mperm==PERM_EXE ){
416421
@ executable with contents
417422
}else if( mperm==PERM_LNK ){
418423
@ a symlink with target
419424
}else{
@@ -420,15 +425,15 @@
420425
@ a regular file with contents
421426
}
422427
@ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
423428
}
424429
}else if( zOld ){
425
- @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
426
- @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
430
+ @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
431
+ @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
427432
}else{
428
- @ Added %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
429
- @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
433
+ @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
434
+ @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
430435
}
431436
if( diffFlags ){
432437
append_diff(zOld, zNew, diffFlags, pRe);
433438
}else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
434439
@ &nbsp;&nbsp;
@@ -929,11 +934,12 @@
929934
const char *zName = db_column_text(&q3,0);
930935
int mperm = db_column_int(&q3, 1);
931936
const char *zOld = db_column_text(&q3,2);
932937
const char *zNew = db_column_text(&q3,3);
933938
const char *zOldName = db_column_text(&q3, 4);
934
- append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
939
+ append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
940
+ diffFlags,pRe,mperm);
935941
}
936942
db_finalize(&q3);
937943
append_diff_javascript(diffType==2);
938944
cookie_render();
939945
style_footer();
@@ -1292,17 +1298,17 @@
12921298
}else{
12931299
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
12941300
}
12951301
if( cmp<0 ){
12961302
if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){
1297
- append_file_change_line(pFileFrom->zName,
1303
+ append_file_change_line(zFrom, pFileFrom->zName,
12981304
pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0);
12991305
}
13001306
pFileFrom = manifest_file_next(pFrom, 0);
13011307
}else if( cmp>0 ){
13021308
if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){
1303
- append_file_change_line(pFileTo->zName,
1309
+ append_file_change_line(zTo, pFileTo->zName,
13041310
0, pFileTo->zUuid, 0, diffFlags, pRe,
13051311
manifest_file_mperm(pFileTo));
13061312
}
13071313
pFileTo = manifest_file_next(pTo, 0);
13081314
}else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
@@ -1309,11 +1315,11 @@
13091315
pFileFrom = manifest_file_next(pFrom, 0);
13101316
pFileTo = manifest_file_next(pTo, 0);
13111317
}else{
13121318
if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0
13131319
|| sqlite3_strglob(zGlob, pFileTo->zName)==0) ){
1314
- append_file_change_line(pFileFrom->zName,
1320
+ append_file_change_line(zFrom, pFileFrom->zName,
13151321
pFileFrom->zUuid,
13161322
pFileTo->zUuid, 0, diffFlags, pRe,
13171323
manifest_file_mperm(pFileTo));
13181324
}
13191325
pFileFrom = manifest_file_next(pFrom, 0);
@@ -1419,11 +1425,12 @@
14191425
bNeedBase = 0;
14201426
style_set_current_page("doc/%S/%s",zVers,zName);
14211427
}
14221428
}
14231429
objType |= OBJTYPE_CONTENT;
1424
- @ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
1430
+ @ %z(href("%R/finfo?name=%T&ci=%!S&m=%!S",zName,zVers,zUuid))\
1431
+ @ %h(zName)</a>
14251432
tag_private_status(rid);
14261433
if( showDetail ){
14271434
@ <ul>
14281435
}
14291436
prevName = fossil_strdup(zName);
@@ -2311,11 +2318,11 @@
23112318
23122319
asText = P("txt")!=0;
23132320
if( isFile ){
23142321
if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
23152322
zCI = "tip";
2316
- @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2323
+ @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
23172324
@ from the %z(href("%R/info/tip"))latest check-in</a></h2>
23182325
}else{
23192326
const char *zPath;
23202327
Blob path;
23212328
blob_zero(&path);
23222329
--- src/info.c
+++ src/info.c
@@ -368,10 +368,11 @@
368 /*
369 ** Write a line of web-page output that shows changes that have occurred
370 ** to a file between two check-ins.
371 */
372 static void append_file_change_line(
 
373 const char *zName, /* Name of the file that has changed */
374 const char *zOld, /* blob.uuid before change. NULL for added files */
375 const char *zNew, /* blob.uuid after change. NULL for deletes */
376 const char *zOldName, /* Prior name. NULL if no name change. */
377 u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */
@@ -401,19 +402,23 @@
401 append_diff(zOld, zNew, diffFlags, pRe);
402 }
403 }else{
404 if( zOld && zNew ){
405 if( fossil_strcmp(zOld, zNew)!=0 ){
406 @ Modified %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
 
407 @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
408 @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
409 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
410 @ Name change
411 @ from %z(href("%R/finfo?name=%T&m=%!S",zOldName,zOld))%h(zOldName)</a>
412 @ to %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>.
 
 
413 }else{
414 @ %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a> became
 
415 if( mperm==PERM_EXE ){
416 @ executable with contents
417 }else if( mperm==PERM_LNK ){
418 @ a symlink with target
419 }else{
@@ -420,15 +425,15 @@
420 @ a regular file with contents
421 }
422 @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
423 }
424 }else if( zOld ){
425 @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
426 @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
427 }else{
428 @ Added %z(href("%R/finfo?name=%T&m=%!S",zName,zNew))%h(zName)</a>
429 @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
430 }
431 if( diffFlags ){
432 append_diff(zOld, zNew, diffFlags, pRe);
433 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
434 @ &nbsp;&nbsp;
@@ -929,11 +934,12 @@
929 const char *zName = db_column_text(&q3,0);
930 int mperm = db_column_int(&q3, 1);
931 const char *zOld = db_column_text(&q3,2);
932 const char *zNew = db_column_text(&q3,3);
933 const char *zOldName = db_column_text(&q3, 4);
934 append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
 
935 }
936 db_finalize(&q3);
937 append_diff_javascript(diffType==2);
938 cookie_render();
939 style_footer();
@@ -1292,17 +1298,17 @@
1292 }else{
1293 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
1294 }
1295 if( cmp<0 ){
1296 if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){
1297 append_file_change_line(pFileFrom->zName,
1298 pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0);
1299 }
1300 pFileFrom = manifest_file_next(pFrom, 0);
1301 }else if( cmp>0 ){
1302 if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){
1303 append_file_change_line(pFileTo->zName,
1304 0, pFileTo->zUuid, 0, diffFlags, pRe,
1305 manifest_file_mperm(pFileTo));
1306 }
1307 pFileTo = manifest_file_next(pTo, 0);
1308 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
@@ -1309,11 +1315,11 @@
1309 pFileFrom = manifest_file_next(pFrom, 0);
1310 pFileTo = manifest_file_next(pTo, 0);
1311 }else{
1312 if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0
1313 || sqlite3_strglob(zGlob, pFileTo->zName)==0) ){
1314 append_file_change_line(pFileFrom->zName,
1315 pFileFrom->zUuid,
1316 pFileTo->zUuid, 0, diffFlags, pRe,
1317 manifest_file_mperm(pFileTo));
1318 }
1319 pFileFrom = manifest_file_next(pFrom, 0);
@@ -1419,11 +1425,12 @@
1419 bNeedBase = 0;
1420 style_set_current_page("doc/%S/%s",zVers,zName);
1421 }
1422 }
1423 objType |= OBJTYPE_CONTENT;
1424 @ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
 
1425 tag_private_status(rid);
1426 if( showDetail ){
1427 @ <ul>
1428 }
1429 prevName = fossil_strdup(zName);
@@ -2311,11 +2318,11 @@
2311
2312 asText = P("txt")!=0;
2313 if( isFile ){
2314 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2315 zCI = "tip";
2316 @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2317 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2318 }else{
2319 const char *zPath;
2320 Blob path;
2321 blob_zero(&path);
2322
--- src/info.c
+++ src/info.c
@@ -368,10 +368,11 @@
368 /*
369 ** Write a line of web-page output that shows changes that have occurred
370 ** to a file between two check-ins.
371 */
372 static void append_file_change_line(
373 const char *zCkin, /* The checkin on which the change occurs */
374 const char *zName, /* Name of the file that has changed */
375 const char *zOld, /* blob.uuid before change. NULL for added files */
376 const char *zNew, /* blob.uuid after change. NULL for deletes */
377 const char *zOldName, /* Prior name. NULL if no name change. */
378 u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */
@@ -401,19 +402,23 @@
402 append_diff(zOld, zNew, diffFlags, pRe);
403 }
404 }else{
405 if( zOld && zNew ){
406 if( fossil_strcmp(zOld, zNew)!=0 ){
407 @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
408 @ %h(zName)</a>
409 @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
410 @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
411 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
412 @ Name change
413 @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\
414 @ %h(zOldName)</a>
415 @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
416 @ %h(zName)</a>.
417 }else{
418 @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
419 @ %h(zName)</a> became
420 if( mperm==PERM_EXE ){
421 @ executable with contents
422 }else if( mperm==PERM_LNK ){
423 @ a symlink with target
424 }else{
@@ -420,15 +425,15 @@
425 @ a regular file with contents
426 }
427 @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
428 }
429 }else if( zOld ){
430 @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\
431 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
432 }else{
433 @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
434 @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
435 }
436 if( diffFlags ){
437 append_diff(zOld, zNew, diffFlags, pRe);
438 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
439 @ &nbsp;&nbsp;
@@ -929,11 +934,12 @@
934 const char *zName = db_column_text(&q3,0);
935 int mperm = db_column_int(&q3, 1);
936 const char *zOld = db_column_text(&q3,2);
937 const char *zNew = db_column_text(&q3,3);
938 const char *zOldName = db_column_text(&q3, 4);
939 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
940 diffFlags,pRe,mperm);
941 }
942 db_finalize(&q3);
943 append_diff_javascript(diffType==2);
944 cookie_render();
945 style_footer();
@@ -1292,17 +1298,17 @@
1298 }else{
1299 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
1300 }
1301 if( cmp<0 ){
1302 if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){
1303 append_file_change_line(zFrom, pFileFrom->zName,
1304 pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0);
1305 }
1306 pFileFrom = manifest_file_next(pFrom, 0);
1307 }else if( cmp>0 ){
1308 if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){
1309 append_file_change_line(zTo, pFileTo->zName,
1310 0, pFileTo->zUuid, 0, diffFlags, pRe,
1311 manifest_file_mperm(pFileTo));
1312 }
1313 pFileTo = manifest_file_next(pTo, 0);
1314 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
@@ -1309,11 +1315,11 @@
1315 pFileFrom = manifest_file_next(pFrom, 0);
1316 pFileTo = manifest_file_next(pTo, 0);
1317 }else{
1318 if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0
1319 || sqlite3_strglob(zGlob, pFileTo->zName)==0) ){
1320 append_file_change_line(zFrom, pFileFrom->zName,
1321 pFileFrom->zUuid,
1322 pFileTo->zUuid, 0, diffFlags, pRe,
1323 manifest_file_mperm(pFileTo));
1324 }
1325 pFileFrom = manifest_file_next(pFrom, 0);
@@ -1419,11 +1425,12 @@
1425 bNeedBase = 0;
1426 style_set_current_page("doc/%S/%s",zVers,zName);
1427 }
1428 }
1429 objType |= OBJTYPE_CONTENT;
1430 @ %z(href("%R/finfo?name=%T&ci=%!S&m=%!S",zName,zVers,zUuid))\
1431 @ %h(zName)</a>
1432 tag_private_status(rid);
1433 if( showDetail ){
1434 @ <ul>
1435 }
1436 prevName = fossil_strdup(zName);
@@ -2311,11 +2318,11 @@
2318
2319 asText = P("txt")!=0;
2320 if( isFile ){
2321 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2322 zCI = "tip";
2323 @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
2324 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2325 }else{
2326 const char *zPath;
2327 Blob path;
2328 blob_zero(&path);
2329

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button