Fossil SCM
Enhance the "vdiff" web method so that it shows the differences between to arbitrary check-ins identified by the "from" and "to" query parameters.
Commit
1d713f3f4d2292573363cf3b731ee3517a76fb5c
Parent
296b90a25bfbd3f…
1 file changed
+160
-88
+160
-88
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -238,10 +238,51 @@ | ||
| 238 | 238 | blob_reset(&from); |
| 239 | 239 | blob_reset(&to); |
| 240 | 240 | blob_reset(&out); |
| 241 | 241 | } |
| 242 | 242 | |
| 243 | +/* | |
| 244 | +** Write a line of web-page output that shows changes that have occurred | |
| 245 | +** to a file between two check-ins. | |
| 246 | +*/ | |
| 247 | +static void append_file_change_line( | |
| 248 | + const char *zName, /* Name of the file that has changed */ | |
| 249 | + const char *zOld, /* blob.uuid before change. NULL for added files */ | |
| 250 | + const char *zNew, /* blob.uuid after change. NULL for deletes */ | |
| 251 | + int showDiff /* Show edit diffs if true */ | |
| 252 | +){ | |
| 253 | + if( !g.okHistory ){ | |
| 254 | + if( zNew==0 ){ | |
| 255 | + @ <p>Deleted %h(zName)</p> | |
| 256 | + }else if( zOld==0 ){ | |
| 257 | + @ <p>Added %h(zName)</p> | |
| 258 | + }else{ | |
| 259 | + @ <p>Changes to %h(zName)</p> | |
| 260 | + } | |
| 261 | + }else if( zOld && zNew ){ | |
| 262 | + @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 263 | + @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> | |
| 264 | + @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> | |
| 265 | + if( !showDiff ){ | |
| 266 | + @ | |
| 267 | + @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> | |
| 268 | + }else{ | |
| 269 | + int rid1 = uuid_to_rid(zOld, 0); | |
| 270 | + int rid2 = uuid_to_rid(zNew, 0); | |
| 271 | + @ <blockquote><pre> | |
| 272 | + append_diff(rid1, rid2); | |
| 273 | + @ </pre></blockquote> | |
| 274 | + } | |
| 275 | + }else if( zOld ){ | |
| 276 | + @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 277 | + @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p> | |
| 278 | + }else{ | |
| 279 | + @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 280 | + @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p> | |
| 281 | + } | |
| 282 | +} | |
| 283 | + | |
| 243 | 284 | |
| 244 | 285 | /* |
| 245 | 286 | ** WEBPAGE: vinfo |
| 246 | 287 | ** WEBPAGE: ci |
| 247 | 288 | ** URL: /ci?name=RID|ARTIFACTID |
| @@ -391,52 +432,23 @@ | ||
| 391 | 432 | }else{ |
| 392 | 433 | @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[show diffs]</a><br/> |
| 393 | 434 | } |
| 394 | 435 | } |
| 395 | 436 | db_prepare(&q, |
| 396 | - "SELECT pid, fid, name," | |
| 437 | + "SELECT name," | |
| 397 | 438 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 398 | 439 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)" |
| 399 | 440 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 400 | 441 | " WHERE mlink.mid=%d" |
| 401 | 442 | " ORDER BY name", |
| 402 | 443 | rid |
| 403 | 444 | ); |
| 404 | 445 | while( db_step(&q)==SQLITE_ROW ){ |
| 405 | - int pid = db_column_int(&q,0); | |
| 406 | - int fid = db_column_int(&q,1); | |
| 407 | - const char *zName = db_column_text(&q,2); | |
| 408 | - const char *zOld = db_column_text(&q,3); | |
| 409 | - const char *zNew = db_column_text(&q,4); | |
| 410 | - if( !g.okHistory ){ | |
| 411 | - if( zNew==0 ){ | |
| 412 | - @ <p>Deleted %h(zName)</p> | |
| 413 | - continue; | |
| 414 | - }else{ | |
| 415 | - @ <p>Changes to %h(zName)</p> | |
| 416 | - } | |
| 417 | - }else if( zOld && zNew ){ | |
| 418 | - @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 419 | - @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> | |
| 420 | - @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> | |
| 421 | - if( !showDiff ){ | |
| 422 | - @ | |
| 423 | - @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> | |
| 424 | - } | |
| 425 | - }else if( zOld ){ | |
| 426 | - @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 427 | - @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p> | |
| 428 | - continue; | |
| 429 | - }else{ | |
| 430 | - @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 431 | - @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p> | |
| 432 | - } | |
| 433 | - if( showDiff ){ | |
| 434 | - @ <blockquote><pre> | |
| 435 | - append_diff(pid, fid); | |
| 436 | - @ </pre></blockquote> | |
| 437 | - } | |
| 446 | + const char *zName = db_column_text(&q,0); | |
| 447 | + const char *zOld = db_column_text(&q,1); | |
| 448 | + const char *zNew = db_column_text(&q,2); | |
| 449 | + append_file_change_line(zName, zOld, zNew, showDiff); | |
| 438 | 450 | } |
| 439 | 451 | db_finalize(&q); |
| 440 | 452 | style_footer(); |
| 441 | 453 | } |
| 442 | 454 | |
| @@ -520,76 +532,135 @@ | ||
| 520 | 532 | } |
| 521 | 533 | manifest_clear(&m); |
| 522 | 534 | } |
| 523 | 535 | style_footer(); |
| 524 | 536 | } |
| 537 | + | |
| 538 | +/* | |
| 539 | +** Show a webpage error message | |
| 540 | +*/ | |
| 541 | +void webpage_error(const char *zFormat, ...){ | |
| 542 | + va_list ap; | |
| 543 | + const char *z; | |
| 544 | + va_start(ap, zFormat); | |
| 545 | + z = vmprintf(zFormat, ap); | |
| 546 | + va_end(ap); | |
| 547 | + style_header("URL Error"); | |
| 548 | + @ <h1>Error</h1> | |
| 549 | + @ <p>%h(z)</p> | |
| 550 | + style_footer(); | |
| 551 | +} | |
| 552 | + | |
| 553 | +/* | |
| 554 | +** Find an checkin based on query parameter zParam and parse its | |
| 555 | +** manifest. Return the number of errors. | |
| 556 | +*/ | |
| 557 | +static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){ | |
| 558 | + int rid; | |
| 559 | + Blob content; | |
| 560 | + | |
| 561 | + *pRid = rid = name_to_rid_www(zParam); | |
| 562 | + if( rid==0 ){ | |
| 563 | + webpage_error("Missing \"%s\" query parameter.", zParam); | |
| 564 | + return 1; | |
| 565 | + } | |
| 566 | + if( !is_a_version(rid) ){ | |
| 567 | + webpage_error("Artifact %s is not a checkin.", P(zParam)); | |
| 568 | + return 1; | |
| 569 | + } | |
| 570 | + content_get(rid, &content); | |
| 571 | + manifest_parse(pM, &content); | |
| 572 | + return 0; | |
| 573 | +} | |
| 574 | + | |
| 575 | +/* | |
| 576 | +** Output a description of a check-in | |
| 577 | +*/ | |
| 578 | +void checkin_description(int rid){ | |
| 579 | + Stmt q; | |
| 580 | + db_prepare(&q, | |
| 581 | + "SELECT datetime(mtime), coalesce(euser,user)," | |
| 582 | + " coalesce(ecomment,comment), uuid" | |
| 583 | + " FROM event, blob" | |
| 584 | + " WHERE event.objid=%d AND type='ci'" | |
| 585 | + " AND blob.rid=%d", | |
| 586 | + rid, rid | |
| 587 | + ); | |
| 588 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 589 | + const char *zDate = db_column_text(&q, 0); | |
| 590 | + const char *zUser = db_column_text(&q, 1); | |
| 591 | + const char *zCom = db_column_text(&q, 2); | |
| 592 | + const char *zUuid = db_column_text(&q, 3); | |
| 593 | + @ Check-in | |
| 594 | + hyperlink_to_uuid(zUuid); | |
| 595 | + @ - %w(zCom) by | |
| 596 | + hyperlink_to_user(zUser,zDate," on"); | |
| 597 | + hyperlink_to_date(zDate, "."); | |
| 598 | + } | |
| 599 | + db_finalize(&q); | |
| 600 | +} | |
| 601 | + | |
| 525 | 602 | |
| 526 | 603 | /* |
| 527 | 604 | ** WEBPAGE: vdiff |
| 528 | -** URL: /vdiff?name=RID | |
| 605 | +** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN | |
| 529 | 606 | ** |
| 530 | -** Show all differences for a particular check-in. | |
| 607 | +** Show all differences between two checkins. | |
| 531 | 608 | */ |
| 532 | 609 | void vdiff_page(void){ |
| 533 | - int rid; | |
| 534 | - Stmt q; | |
| 535 | - char *zUuid; | |
| 610 | + int ridFrom, ridTo; | |
| 611 | + int showDetail = 0; | |
| 612 | + int iFrom, iTo; | |
| 613 | + Manifest mFrom, mTo; | |
| 536 | 614 | |
| 537 | 615 | login_check_credentials(); |
| 538 | 616 | if( !g.okRead ){ login_needed(); return; } |
| 539 | 617 | login_anonymous_available(); |
| 540 | 618 | |
| 541 | - rid = name_to_rid_www("name"); | |
| 542 | - if( rid==0 ){ | |
| 543 | - fossil_redirect_home(); | |
| 544 | - } | |
| 545 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 546 | - style_header("Check-in [%.10s]", zUuid); | |
| 547 | - db_prepare(&q, | |
| 548 | - "SELECT datetime(mtime), " | |
| 549 | - " coalesce(event.ecomment,event.comment)," | |
| 550 | - " coalesce(event.euser,event.user)" | |
| 551 | - " FROM event WHERE type='ci' AND objid=%d", | |
| 552 | - rid | |
| 553 | - ); | |
| 554 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 555 | - const char *zDate = db_column_text(&q, 0); | |
| 556 | - const char *zUser = db_column_text(&q, 2); | |
| 557 | - const char *zComment = db_column_text(&q, 1); | |
| 558 | - @ <h2>Check-in %s(zUuid)</h2> | |
| 559 | - @ <p>Made by | |
| 560 | - hyperlink_to_user(zUser,zDate," on"); | |
| 561 | - hyperlink_to_date(zDate, ":"); | |
| 562 | - @ %w(zComment). | |
| 563 | - if( g.okHistory ){ | |
| 564 | - @ <a href="%s(g.zBaseURL)/ci/%s(zUuid)">[details]</a> | |
| 565 | - } | |
| 566 | - @ </p><hr> | |
| 567 | - } | |
| 568 | - db_finalize(&q); | |
| 569 | - db_prepare(&q, | |
| 570 | - "SELECT pid, fid, name" | |
| 571 | - " FROM mlink, filename" | |
| 572 | - " WHERE mlink.mid=%d" | |
| 573 | - " AND filename.fnid=mlink.fnid" | |
| 574 | - " ORDER BY name", | |
| 575 | - rid | |
| 576 | - ); | |
| 577 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 578 | - int pid = db_column_int(&q,0); | |
| 579 | - int fid = db_column_int(&q,1); | |
| 580 | - const char *zName = db_column_text(&q,2); | |
| 581 | - if( g.okHistory ){ | |
| 582 | - @ <p><a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></p> | |
| 619 | + if( vdiff_parse_manifest("from", &ridFrom, &mFrom) ) return; | |
| 620 | + if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return; | |
| 621 | + showDetail = atoi(PD("detail","0")); | |
| 622 | + style_header("Check-in Differences"); | |
| 623 | + @ <h2>Difference From:</h2><blockquote> | |
| 624 | + checkin_description(ridFrom); | |
| 625 | + @ </blockquote><h2>To:</h2><blockquote> | |
| 626 | + checkin_description(ridTo); | |
| 627 | + @ </blockquote><hr><p> | |
| 628 | + | |
| 629 | + iFrom = iTo = 0; | |
| 630 | + while( iFrom<mFrom.nFile && iTo<mTo.nFile ){ | |
| 631 | + int cmp; | |
| 632 | + if( iFrom>=mFrom.nFile ){ | |
| 633 | + cmp = +1; | |
| 634 | + }else if( iTo>=mTo.nFile ){ | |
| 635 | + cmp = -1; | |
| 636 | + }else{ | |
| 637 | + cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName); | |
| 638 | + } | |
| 639 | + if( cmp<0 ){ | |
| 640 | + append_file_change_line(mFrom.aFile[iFrom].zName, | |
| 641 | + mFrom.aFile[iFrom].zUuid, 0, 0); | |
| 642 | + iFrom++; | |
| 643 | + }else if( cmp>0 ){ | |
| 644 | + append_file_change_line(mTo.aFile[iTo].zName, | |
| 645 | + 0, mTo.aFile[iTo].zUuid, 0); | |
| 646 | + iTo++; | |
| 647 | + }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){ | |
| 648 | + /* No changes */ | |
| 649 | + iFrom++; | |
| 650 | + iTo++; | |
| 583 | 651 | }else{ |
| 584 | - @ <p>%h(zName)</p> | |
| 652 | + append_file_change_line(mFrom.aFile[iFrom].zName, | |
| 653 | + mFrom.aFile[iFrom].zUuid, | |
| 654 | + mTo.aFile[iTo].zUuid, showDetail); | |
| 655 | + iFrom++; | |
| 656 | + iTo++; | |
| 585 | 657 | } |
| 586 | - @ <blockquote><pre> | |
| 587 | - append_diff(pid, fid); | |
| 588 | - @ </pre></blockquote> | |
| 589 | 658 | } |
| 590 | - db_finalize(&q); | |
| 659 | + manifest_clear(&mFrom); | |
| 660 | + manifest_clear(&mTo); | |
| 661 | + | |
| 591 | 662 | style_footer(); |
| 592 | 663 | } |
| 593 | 664 | |
| 594 | 665 | /* |
| 595 | 666 | ** Write a description of an object to the www reply. |
| @@ -783,16 +854,17 @@ | ||
| 783 | 854 | ** |
| 784 | 855 | ** Two arguments, v1 and v2, are integers. Show the difference between |
| 785 | 856 | ** the two records. |
| 786 | 857 | */ |
| 787 | 858 | void diff_page(void){ |
| 788 | - int v1 = name_to_rid(P("v1")); | |
| 789 | - int v2 = name_to_rid(P("v2")); | |
| 859 | + int v1, v2; | |
| 790 | 860 | Blob c1, c2, diff; |
| 791 | 861 | |
| 792 | 862 | login_check_credentials(); |
| 793 | 863 | if( !g.okRead ){ login_needed(); return; } |
| 864 | + v1 = name_to_rid_www("v1"); | |
| 865 | + v2 = name_to_rid_www("v2"); | |
| 794 | 866 | if( v1==0 || v2==0 ) fossil_redirect_home(); |
| 795 | 867 | style_header("Diff"); |
| 796 | 868 | @ <h2>Differences From:</h2> |
| 797 | 869 | @ <blockquote> |
| 798 | 870 | object_description(v1, 1, 0); |
| 799 | 871 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -238,10 +238,51 @@ | |
| 238 | blob_reset(&from); |
| 239 | blob_reset(&to); |
| 240 | blob_reset(&out); |
| 241 | } |
| 242 | |
| 243 | |
| 244 | /* |
| 245 | ** WEBPAGE: vinfo |
| 246 | ** WEBPAGE: ci |
| 247 | ** URL: /ci?name=RID|ARTIFACTID |
| @@ -391,52 +432,23 @@ | |
| 391 | }else{ |
| 392 | @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[show diffs]</a><br/> |
| 393 | } |
| 394 | } |
| 395 | db_prepare(&q, |
| 396 | "SELECT pid, fid, name," |
| 397 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 398 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)" |
| 399 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 400 | " WHERE mlink.mid=%d" |
| 401 | " ORDER BY name", |
| 402 | rid |
| 403 | ); |
| 404 | while( db_step(&q)==SQLITE_ROW ){ |
| 405 | int pid = db_column_int(&q,0); |
| 406 | int fid = db_column_int(&q,1); |
| 407 | const char *zName = db_column_text(&q,2); |
| 408 | const char *zOld = db_column_text(&q,3); |
| 409 | const char *zNew = db_column_text(&q,4); |
| 410 | if( !g.okHistory ){ |
| 411 | if( zNew==0 ){ |
| 412 | @ <p>Deleted %h(zName)</p> |
| 413 | continue; |
| 414 | }else{ |
| 415 | @ <p>Changes to %h(zName)</p> |
| 416 | } |
| 417 | }else if( zOld && zNew ){ |
| 418 | @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 419 | @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> |
| 420 | @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> |
| 421 | if( !showDiff ){ |
| 422 | @ |
| 423 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> |
| 424 | } |
| 425 | }else if( zOld ){ |
| 426 | @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 427 | @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p> |
| 428 | continue; |
| 429 | }else{ |
| 430 | @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 431 | @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p> |
| 432 | } |
| 433 | if( showDiff ){ |
| 434 | @ <blockquote><pre> |
| 435 | append_diff(pid, fid); |
| 436 | @ </pre></blockquote> |
| 437 | } |
| 438 | } |
| 439 | db_finalize(&q); |
| 440 | style_footer(); |
| 441 | } |
| 442 | |
| @@ -520,76 +532,135 @@ | |
| 520 | } |
| 521 | manifest_clear(&m); |
| 522 | } |
| 523 | style_footer(); |
| 524 | } |
| 525 | |
| 526 | /* |
| 527 | ** WEBPAGE: vdiff |
| 528 | ** URL: /vdiff?name=RID |
| 529 | ** |
| 530 | ** Show all differences for a particular check-in. |
| 531 | */ |
| 532 | void vdiff_page(void){ |
| 533 | int rid; |
| 534 | Stmt q; |
| 535 | char *zUuid; |
| 536 | |
| 537 | login_check_credentials(); |
| 538 | if( !g.okRead ){ login_needed(); return; } |
| 539 | login_anonymous_available(); |
| 540 | |
| 541 | rid = name_to_rid_www("name"); |
| 542 | if( rid==0 ){ |
| 543 | fossil_redirect_home(); |
| 544 | } |
| 545 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 546 | style_header("Check-in [%.10s]", zUuid); |
| 547 | db_prepare(&q, |
| 548 | "SELECT datetime(mtime), " |
| 549 | " coalesce(event.ecomment,event.comment)," |
| 550 | " coalesce(event.euser,event.user)" |
| 551 | " FROM event WHERE type='ci' AND objid=%d", |
| 552 | rid |
| 553 | ); |
| 554 | while( db_step(&q)==SQLITE_ROW ){ |
| 555 | const char *zDate = db_column_text(&q, 0); |
| 556 | const char *zUser = db_column_text(&q, 2); |
| 557 | const char *zComment = db_column_text(&q, 1); |
| 558 | @ <h2>Check-in %s(zUuid)</h2> |
| 559 | @ <p>Made by |
| 560 | hyperlink_to_user(zUser,zDate," on"); |
| 561 | hyperlink_to_date(zDate, ":"); |
| 562 | @ %w(zComment). |
| 563 | if( g.okHistory ){ |
| 564 | @ <a href="%s(g.zBaseURL)/ci/%s(zUuid)">[details]</a> |
| 565 | } |
| 566 | @ </p><hr> |
| 567 | } |
| 568 | db_finalize(&q); |
| 569 | db_prepare(&q, |
| 570 | "SELECT pid, fid, name" |
| 571 | " FROM mlink, filename" |
| 572 | " WHERE mlink.mid=%d" |
| 573 | " AND filename.fnid=mlink.fnid" |
| 574 | " ORDER BY name", |
| 575 | rid |
| 576 | ); |
| 577 | while( db_step(&q)==SQLITE_ROW ){ |
| 578 | int pid = db_column_int(&q,0); |
| 579 | int fid = db_column_int(&q,1); |
| 580 | const char *zName = db_column_text(&q,2); |
| 581 | if( g.okHistory ){ |
| 582 | @ <p><a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></p> |
| 583 | }else{ |
| 584 | @ <p>%h(zName)</p> |
| 585 | } |
| 586 | @ <blockquote><pre> |
| 587 | append_diff(pid, fid); |
| 588 | @ </pre></blockquote> |
| 589 | } |
| 590 | db_finalize(&q); |
| 591 | style_footer(); |
| 592 | } |
| 593 | |
| 594 | /* |
| 595 | ** Write a description of an object to the www reply. |
| @@ -783,16 +854,17 @@ | |
| 783 | ** |
| 784 | ** Two arguments, v1 and v2, are integers. Show the difference between |
| 785 | ** the two records. |
| 786 | */ |
| 787 | void diff_page(void){ |
| 788 | int v1 = name_to_rid(P("v1")); |
| 789 | int v2 = name_to_rid(P("v2")); |
| 790 | Blob c1, c2, diff; |
| 791 | |
| 792 | login_check_credentials(); |
| 793 | if( !g.okRead ){ login_needed(); return; } |
| 794 | if( v1==0 || v2==0 ) fossil_redirect_home(); |
| 795 | style_header("Diff"); |
| 796 | @ <h2>Differences From:</h2> |
| 797 | @ <blockquote> |
| 798 | object_description(v1, 1, 0); |
| 799 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -238,10 +238,51 @@ | |
| 238 | blob_reset(&from); |
| 239 | blob_reset(&to); |
| 240 | blob_reset(&out); |
| 241 | } |
| 242 | |
| 243 | /* |
| 244 | ** Write a line of web-page output that shows changes that have occurred |
| 245 | ** to a file between two check-ins. |
| 246 | */ |
| 247 | static void append_file_change_line( |
| 248 | const char *zName, /* Name of the file that has changed */ |
| 249 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 250 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 251 | int showDiff /* Show edit diffs if true */ |
| 252 | ){ |
| 253 | if( !g.okHistory ){ |
| 254 | if( zNew==0 ){ |
| 255 | @ <p>Deleted %h(zName)</p> |
| 256 | }else if( zOld==0 ){ |
| 257 | @ <p>Added %h(zName)</p> |
| 258 | }else{ |
| 259 | @ <p>Changes to %h(zName)</p> |
| 260 | } |
| 261 | }else if( zOld && zNew ){ |
| 262 | @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 263 | @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> |
| 264 | @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> |
| 265 | if( !showDiff ){ |
| 266 | @ |
| 267 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> |
| 268 | }else{ |
| 269 | int rid1 = uuid_to_rid(zOld, 0); |
| 270 | int rid2 = uuid_to_rid(zNew, 0); |
| 271 | @ <blockquote><pre> |
| 272 | append_diff(rid1, rid2); |
| 273 | @ </pre></blockquote> |
| 274 | } |
| 275 | }else if( zOld ){ |
| 276 | @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 277 | @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p> |
| 278 | }else{ |
| 279 | @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 280 | @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p> |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | |
| 285 | /* |
| 286 | ** WEBPAGE: vinfo |
| 287 | ** WEBPAGE: ci |
| 288 | ** URL: /ci?name=RID|ARTIFACTID |
| @@ -391,52 +432,23 @@ | |
| 432 | }else{ |
| 433 | @ <a href="%s(g.zBaseURL)/vinfo/%T(zName)">[show diffs]</a><br/> |
| 434 | } |
| 435 | } |
| 436 | db_prepare(&q, |
| 437 | "SELECT name," |
| 438 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 439 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)" |
| 440 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 441 | " WHERE mlink.mid=%d" |
| 442 | " ORDER BY name", |
| 443 | rid |
| 444 | ); |
| 445 | while( db_step(&q)==SQLITE_ROW ){ |
| 446 | const char *zName = db_column_text(&q,0); |
| 447 | const char *zOld = db_column_text(&q,1); |
| 448 | const char *zNew = db_column_text(&q,2); |
| 449 | append_file_change_line(zName, zOld, zNew, showDiff); |
| 450 | } |
| 451 | db_finalize(&q); |
| 452 | style_footer(); |
| 453 | } |
| 454 | |
| @@ -520,76 +532,135 @@ | |
| 532 | } |
| 533 | manifest_clear(&m); |
| 534 | } |
| 535 | style_footer(); |
| 536 | } |
| 537 | |
| 538 | /* |
| 539 | ** Show a webpage error message |
| 540 | */ |
| 541 | void webpage_error(const char *zFormat, ...){ |
| 542 | va_list ap; |
| 543 | const char *z; |
| 544 | va_start(ap, zFormat); |
| 545 | z = vmprintf(zFormat, ap); |
| 546 | va_end(ap); |
| 547 | style_header("URL Error"); |
| 548 | @ <h1>Error</h1> |
| 549 | @ <p>%h(z)</p> |
| 550 | style_footer(); |
| 551 | } |
| 552 | |
| 553 | /* |
| 554 | ** Find an checkin based on query parameter zParam and parse its |
| 555 | ** manifest. Return the number of errors. |
| 556 | */ |
| 557 | static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){ |
| 558 | int rid; |
| 559 | Blob content; |
| 560 | |
| 561 | *pRid = rid = name_to_rid_www(zParam); |
| 562 | if( rid==0 ){ |
| 563 | webpage_error("Missing \"%s\" query parameter.", zParam); |
| 564 | return 1; |
| 565 | } |
| 566 | if( !is_a_version(rid) ){ |
| 567 | webpage_error("Artifact %s is not a checkin.", P(zParam)); |
| 568 | return 1; |
| 569 | } |
| 570 | content_get(rid, &content); |
| 571 | manifest_parse(pM, &content); |
| 572 | return 0; |
| 573 | } |
| 574 | |
| 575 | /* |
| 576 | ** Output a description of a check-in |
| 577 | */ |
| 578 | void checkin_description(int rid){ |
| 579 | Stmt q; |
| 580 | db_prepare(&q, |
| 581 | "SELECT datetime(mtime), coalesce(euser,user)," |
| 582 | " coalesce(ecomment,comment), uuid" |
| 583 | " FROM event, blob" |
| 584 | " WHERE event.objid=%d AND type='ci'" |
| 585 | " AND blob.rid=%d", |
| 586 | rid, rid |
| 587 | ); |
| 588 | while( db_step(&q)==SQLITE_ROW ){ |
| 589 | const char *zDate = db_column_text(&q, 0); |
| 590 | const char *zUser = db_column_text(&q, 1); |
| 591 | const char *zCom = db_column_text(&q, 2); |
| 592 | const char *zUuid = db_column_text(&q, 3); |
| 593 | @ Check-in |
| 594 | hyperlink_to_uuid(zUuid); |
| 595 | @ - %w(zCom) by |
| 596 | hyperlink_to_user(zUser,zDate," on"); |
| 597 | hyperlink_to_date(zDate, "."); |
| 598 | } |
| 599 | db_finalize(&q); |
| 600 | } |
| 601 | |
| 602 | |
| 603 | /* |
| 604 | ** WEBPAGE: vdiff |
| 605 | ** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN |
| 606 | ** |
| 607 | ** Show all differences between two checkins. |
| 608 | */ |
| 609 | void vdiff_page(void){ |
| 610 | int ridFrom, ridTo; |
| 611 | int showDetail = 0; |
| 612 | int iFrom, iTo; |
| 613 | Manifest mFrom, mTo; |
| 614 | |
| 615 | login_check_credentials(); |
| 616 | if( !g.okRead ){ login_needed(); return; } |
| 617 | login_anonymous_available(); |
| 618 | |
| 619 | if( vdiff_parse_manifest("from", &ridFrom, &mFrom) ) return; |
| 620 | if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return; |
| 621 | showDetail = atoi(PD("detail","0")); |
| 622 | style_header("Check-in Differences"); |
| 623 | @ <h2>Difference From:</h2><blockquote> |
| 624 | checkin_description(ridFrom); |
| 625 | @ </blockquote><h2>To:</h2><blockquote> |
| 626 | checkin_description(ridTo); |
| 627 | @ </blockquote><hr><p> |
| 628 | |
| 629 | iFrom = iTo = 0; |
| 630 | while( iFrom<mFrom.nFile && iTo<mTo.nFile ){ |
| 631 | int cmp; |
| 632 | if( iFrom>=mFrom.nFile ){ |
| 633 | cmp = +1; |
| 634 | }else if( iTo>=mTo.nFile ){ |
| 635 | cmp = -1; |
| 636 | }else{ |
| 637 | cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName); |
| 638 | } |
| 639 | if( cmp<0 ){ |
| 640 | append_file_change_line(mFrom.aFile[iFrom].zName, |
| 641 | mFrom.aFile[iFrom].zUuid, 0, 0); |
| 642 | iFrom++; |
| 643 | }else if( cmp>0 ){ |
| 644 | append_file_change_line(mTo.aFile[iTo].zName, |
| 645 | 0, mTo.aFile[iTo].zUuid, 0); |
| 646 | iTo++; |
| 647 | }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){ |
| 648 | /* No changes */ |
| 649 | iFrom++; |
| 650 | iTo++; |
| 651 | }else{ |
| 652 | append_file_change_line(mFrom.aFile[iFrom].zName, |
| 653 | mFrom.aFile[iFrom].zUuid, |
| 654 | mTo.aFile[iTo].zUuid, showDetail); |
| 655 | iFrom++; |
| 656 | iTo++; |
| 657 | } |
| 658 | } |
| 659 | manifest_clear(&mFrom); |
| 660 | manifest_clear(&mTo); |
| 661 | |
| 662 | style_footer(); |
| 663 | } |
| 664 | |
| 665 | /* |
| 666 | ** Write a description of an object to the www reply. |
| @@ -783,16 +854,17 @@ | |
| 854 | ** |
| 855 | ** Two arguments, v1 and v2, are integers. Show the difference between |
| 856 | ** the two records. |
| 857 | */ |
| 858 | void diff_page(void){ |
| 859 | int v1, v2; |
| 860 | Blob c1, c2, diff; |
| 861 | |
| 862 | login_check_credentials(); |
| 863 | if( !g.okRead ){ login_needed(); return; } |
| 864 | v1 = name_to_rid_www("v1"); |
| 865 | v2 = name_to_rid_www("v2"); |
| 866 | if( v1==0 || v2==0 ) fossil_redirect_home(); |
| 867 | style_header("Diff"); |
| 868 | @ <h2>Differences From:</h2> |
| 869 | @ <blockquote> |
| 870 | object_description(v1, 1, 0); |
| 871 |