Fossil SCM
Enhance the change descriptions in the "Changes" accordion panel title to provide information on the number of added and deleted files and similar.
Commit
94e94bc3a61385937580731bbd96460c17c2826a89aaf15c2f356e7ffad4f057
Parent
4151ae97229fe82…
1 file changed
+83
-23
+83
-23
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -371,10 +371,21 @@ | ||
| 371 | 371 | pCfg->zLeftHash = 0; |
| 372 | 372 | blob_reset(&from); |
| 373 | 373 | blob_reset(&to); |
| 374 | 374 | } |
| 375 | 375 | |
| 376 | +/* | |
| 377 | +** The append_file_change_line() routine updates an array of counts | |
| 378 | +** for various kinds of changes. | |
| 379 | +*/ | |
| 380 | +#define FCHNG_NEW 0 | |
| 381 | +#define FCHNG_DELETE 1 | |
| 382 | +#define FCHNG_RENAME 2 | |
| 383 | +#define FCHNG_MODE 3 | |
| 384 | +#define FCHNG_EDIT 4 | |
| 385 | +#define N_FCHNG 5 | |
| 386 | + | |
| 376 | 387 | /* |
| 377 | 388 | ** Write a line of web-page output that shows changes that have occurred |
| 378 | 389 | ** to a file between two check-ins. |
| 379 | 390 | */ |
| 380 | 391 | static void append_file_change_line( |
| @@ -382,32 +393,38 @@ | ||
| 382 | 393 | const char *zName, /* Name of the file that has changed */ |
| 383 | 394 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 384 | 395 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 385 | 396 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 386 | 397 | DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ |
| 387 | - int mperm /* executable or symlink permission for zNew */ | |
| 398 | + int mperm, /* executable or symlink permission for zNew */ | |
| 399 | + int *aChng /* Change count array */ | |
| 388 | 400 | ){ |
| 389 | 401 | @ <div class='file-change-line'><span> |
| 390 | 402 | /* Maintenance reminder: the extra level of SPAN is for |
| 391 | 403 | ** arranging new elements via JS. */ |
| 392 | 404 | if( !g.perm.Hyperlink ){ |
| 393 | 405 | if( zNew==0 ){ |
| 394 | 406 | @ Deleted %h(zName). |
| 407 | + aChng[FCHNG_DELETE]++; | |
| 395 | 408 | }else if( zOld==0 ){ |
| 396 | 409 | @ Added %h(zName). |
| 410 | + aChng[FCHNG_NEW]++; | |
| 397 | 411 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 398 | 412 | @ Name change from %h(zOldName) to %h(zName). |
| 413 | + aChng[FCHNG_RENAME]++; | |
| 399 | 414 | }else if( fossil_strcmp(zNew, zOld)==0 ){ |
| 400 | 415 | if( mperm==PERM_EXE ){ |
| 401 | 416 | @ %h(zName) became executable. |
| 402 | 417 | }else if( mperm==PERM_LNK ){ |
| 403 | 418 | @ %h(zName) became a symlink. |
| 404 | 419 | }else{ |
| 405 | 420 | @ %h(zName) became a regular file. |
| 406 | 421 | } |
| 422 | + aChng[FCHNG_MODE]++; | |
| 407 | 423 | }else{ |
| 408 | 424 | @ Changes to %h(zName). |
| 425 | + aChng[FCHNG_EDIT]++; | |
| 409 | 426 | } |
| 410 | 427 | @ </span></div> |
| 411 | 428 | if( pCfg ){ |
| 412 | 429 | append_diff(zOld, zNew, pCfg); |
| 413 | 430 | } |
| @@ -422,22 +439,25 @@ | ||
| 422 | 439 | @ %h(zOldName)</a> |
| 423 | 440 | @ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> |
| 424 | 441 | @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 425 | 442 | @ %h(zName)</a> |
| 426 | 443 | @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 444 | + aChng[FCHNG_RENAME]++; | |
| 427 | 445 | }else{ |
| 428 | 446 | @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 429 | 447 | @ %h(zName)</a> |
| 430 | 448 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> |
| 431 | 449 | @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 432 | 450 | } |
| 451 | + aChng[FCHNG_EDIT]++; | |
| 433 | 452 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 434 | 453 | @ Name change |
| 435 | 454 | @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\ |
| 436 | 455 | @ %h(zOldName)</a> |
| 437 | 456 | @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 438 | 457 | @ %h(zName)</a>. |
| 458 | + aChng[FCHNG_RENAME]++; | |
| 439 | 459 | }else{ |
| 440 | 460 | @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 441 | 461 | @ %h(zName)</a> became |
| 442 | 462 | if( mperm==PERM_EXE ){ |
| 443 | 463 | @ executable with contents |
| @@ -445,17 +465,20 @@ | ||
| 445 | 465 | @ a symlink with target |
| 446 | 466 | }else{ |
| 447 | 467 | @ a regular file with contents |
| 448 | 468 | } |
| 449 | 469 | @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 470 | + aChng[FCHNG_MODE]++; | |
| 450 | 471 | } |
| 451 | 472 | }else if( zOld ){ |
| 452 | 473 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zOld,zCkin2))\ |
| 453 | 474 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 475 | + aChng[FCHNG_DELETE]++; | |
| 454 | 476 | }else{ |
| 455 | 477 | @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 456 | 478 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 479 | + aChng[FCHNG_NEW]++; | |
| 457 | 480 | } |
| 458 | 481 | if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 459 | 482 | if( pCfg ){ |
| 460 | 483 | @ </span></div> |
| 461 | 484 | append_diff(zOld, zNew, pCfg); |
| @@ -624,10 +647,58 @@ | ||
| 624 | 647 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 625 | 648 | &xtra); |
| 626 | 649 | db_finalize(&q); |
| 627 | 650 | style_finish_page(); |
| 628 | 651 | } |
| 652 | + | |
| 653 | +/* | |
| 654 | +** Generate JS code that will adjust the change counts in the title | |
| 655 | +** of the "changes_section" panel where "changes_section" is a JS id. | |
| 656 | +** | |
| 657 | +** The change counts are taken from g.diffCnt[] for edits, and from | |
| 658 | +** the aChng[] parameter for additions, deletions, and modifications. | |
| 659 | +** The aChng parameter may be NULL, in which case those factors are | |
| 660 | +** ignored. | |
| 661 | +*/ | |
| 662 | +static void adjust_changes_section_title( | |
| 663 | + int iLine, /* Souce code line number of caller */ | |
| 664 | + int *aChng /* Change categories */ | |
| 665 | +){ | |
| 666 | + sqlite3_str *pTxt = sqlite3_str_new(0); | |
| 667 | + if( g.diffCnt[0]>0 ){ | |
| 668 | + sqlite3_str_appendf(pTxt, "%d file%s edited [+%d -%d lines]", | |
| 669 | + g.diffCnt[0], g.diffCnt[0]==1 ? "" : "s", | |
| 670 | + g.diffCnt[1], g.diffCnt[2]); | |
| 671 | + } | |
| 672 | + if( aChng ){ | |
| 673 | + static const int aiType[] = | |
| 674 | + { FCHNG_NEW, FCHNG_DELETE, FCHNG_RENAME, FCHNG_MODE }; | |
| 675 | + static const char *azType[] = | |
| 676 | + { "added", "deleted", "renamed", "mode change"}; | |
| 677 | + int i; | |
| 678 | + for(i=0; i<4; i++){ | |
| 679 | + int j = aiType[i]; | |
| 680 | + if( aChng[j] ){ | |
| 681 | + const char *zFiles = ""; | |
| 682 | + if( sqlite3_str_length(pTxt)>0 ){ | |
| 683 | + sqlite3_str_append(pTxt,", ", 2); | |
| 684 | + }else{ | |
| 685 | + zFiles = i<3 && aChng[j]>1 ? " files" : " file"; | |
| 686 | + } | |
| 687 | + sqlite3_str_appendf(pTxt,"%d%s %s", aChng[j], zFiles, azType[i]); | |
| 688 | + if( i==3 && aChng[j]>1 ) sqlite3_str_appendf(pTxt, "s"); | |
| 689 | + } | |
| 690 | + } | |
| 691 | + } | |
| 692 | + if( sqlite3_str_length(pTxt)>0 ){ | |
| 693 | + @ <script nonce='%h(style_nonce())'>;/* info.c:%d(iLine) */ | |
| 694 | + @ document.getElementById('changes_section').textContent = \ | |
| 695 | + @ 'Changes (%h(sqlite3_str_value(pTxt)))' | |
| 696 | + @ </script> | |
| 697 | + } | |
| 698 | + sqlite3_str_free(pTxt); | |
| 699 | +} | |
| 629 | 700 | |
| 630 | 701 | /* |
| 631 | 702 | ** Render a web-page diff of the changes in the working check-out |
| 632 | 703 | */ |
| 633 | 704 | static void ckout_normal_diff(int vid){ |
| @@ -734,16 +805,11 @@ | ||
| 734 | 805 | blob_reset(&old); |
| 735 | 806 | blob_reset(&new); |
| 736 | 807 | } |
| 737 | 808 | } |
| 738 | 809 | db_finalize(&q); |
| 739 | - @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */ | |
| 740 | - @ document.getElementById('changes_section').textContent = 'Changes ' + | |
| 741 | - @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' + | |
| 742 | - @ '+%d(g.diffCnt[1]) ' + | |
| 743 | - @ '−%d(g.diffCnt[2]))' | |
| 744 | - @ </script> | |
| 810 | + adjust_changes_section_title(__LINE__, 0); | |
| 745 | 811 | append_diff_javascript(diffType); |
| 746 | 812 | } |
| 747 | 813 | |
| 748 | 814 | /* |
| 749 | 815 | ** Render a web-page diff of the changes in the working check-out to |
| @@ -827,16 +893,11 @@ | ||
| 827 | 893 | } |
| 828 | 894 | fossil_free(zLhs); |
| 829 | 895 | fossil_free(zRhs); |
| 830 | 896 | } |
| 831 | 897 | db_finalize(&q); |
| 832 | - @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */ | |
| 833 | - @ document.getElementById('changes_section').textContent = 'Changes ' + | |
| 834 | - @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' + | |
| 835 | - @ '+%d(g.diffCnt[1]) ' + | |
| 836 | - @ '−%d(g.diffCnt[2]))' | |
| 837 | - @ </script> | |
| 898 | + adjust_changes_section_title(__LINE__, 0); | |
| 838 | 899 | append_diff_javascript(diffType); |
| 839 | 900 | } |
| 840 | 901 | |
| 841 | 902 | /* |
| 842 | 903 | ** WEBPAGE: ckout |
| @@ -935,11 +996,13 @@ | ||
| 935 | 996 | ReCompiled *pRe = 0; /* regex */ |
| 936 | 997 | const char *zW; /* URL param for ignoring whitespace */ |
| 937 | 998 | const char *zPage = "vinfo"; /* Page that shows diffs */ |
| 938 | 999 | const char *zBrName; /* Branch name */ |
| 939 | 1000 | DiffConfig DCfg,*pCfg; /* Type of diff */ |
| 1001 | + int aChng[N_FCHNG]; /* Counts of change types */ | |
| 940 | 1002 | |
| 1003 | + memset(aChng, 0, sizeof(aChng)); | |
| 941 | 1004 | login_check_credentials(); |
| 942 | 1005 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 943 | 1006 | style_set_current_feature("vinfo"); |
| 944 | 1007 | zName = P("name"); |
| 945 | 1008 | rid = name_to_rid_www("name"); |
| @@ -1253,21 +1316,16 @@ | ||
| 1253 | 1316 | int mperm = db_column_int(&q3, 1); |
| 1254 | 1317 | const char *zOld = db_column_text(&q3,2); |
| 1255 | 1318 | const char *zNew = db_column_text(&q3,3); |
| 1256 | 1319 | const char *zOldName = db_column_text(&q3, 4); |
| 1257 | 1320 | append_file_change_line(zUuid, zName, zOld, zNew, zOldName, |
| 1258 | - pCfg,mperm); | |
| 1321 | + pCfg,mperm,aChng); | |
| 1259 | 1322 | } |
| 1260 | 1323 | db_finalize(&q3); |
| 1261 | 1324 | @ </div> |
| 1262 | 1325 | if( diffType!=0 ){ |
| 1263 | - @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */ | |
| 1264 | - @ document.getElementById('changes_section').textContent = 'Changes ' + | |
| 1265 | - @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' + | |
| 1266 | - @ '+%d(g.diffCnt[1]) ' + | |
| 1267 | - @ '−%d(g.diffCnt[2]))' | |
| 1268 | - @ </script> | |
| 1326 | + adjust_changes_section_title(__LINE__, aChng); | |
| 1269 | 1327 | } |
| 1270 | 1328 | append_diff_javascript(diffType); |
| 1271 | 1329 | style_finish_page(); |
| 1272 | 1330 | } |
| 1273 | 1331 | |
| @@ -1449,11 +1507,13 @@ | ||
| 1449 | 1507 | DiffConfig DCfg, *pCfg = 0; |
| 1450 | 1508 | int graphFlags = 0; |
| 1451 | 1509 | Blob qp; /* non-glob= query parameters for generated links */ |
| 1452 | 1510 | Blob qpGlob; /* glob= query parameter for generated links */ |
| 1453 | 1511 | int bInvert = PB("inv"); |
| 1512 | + int aChng[N_FCHNG]; /* Count of versiou change types */ | |
| 1454 | 1513 | |
| 1514 | + memset(aChng, 0, sizeof(aChng)); | |
| 1455 | 1515 | login_check_credentials(); |
| 1456 | 1516 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1457 | 1517 | if( robot_restrict("diff") ) return; |
| 1458 | 1518 | login_anonymous_available(); |
| 1459 | 1519 | fossil_nice_default(); |
| @@ -1594,18 +1654,18 @@ | ||
| 1594 | 1654 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 1595 | 1655 | } |
| 1596 | 1656 | if( cmp<0 ){ |
| 1597 | 1657 | if( !pGlob || glob_match(pGlob, pFileFrom->zName) ){ |
| 1598 | 1658 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1599 | - pFileFrom->zUuid, 0, 0, pCfg, 0); | |
| 1659 | + pFileFrom->zUuid, 0, 0, pCfg, 0,aChng); | |
| 1600 | 1660 | } |
| 1601 | 1661 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1602 | 1662 | }else if( cmp>0 ){ |
| 1603 | 1663 | if( !pGlob || glob_match(pGlob, pFileTo->zName) ){ |
| 1604 | 1664 | append_file_change_line(zTo, pFileTo->zName, |
| 1605 | 1665 | 0, pFileTo->zUuid, 0, pCfg, |
| 1606 | - manifest_file_mperm(pFileTo)); | |
| 1666 | + manifest_file_mperm(pFileTo),aChng); | |
| 1607 | 1667 | } |
| 1608 | 1668 | pFileTo = manifest_file_next(pTo, 0); |
| 1609 | 1669 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1610 | 1670 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1611 | 1671 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1613,11 +1673,11 @@ | ||
| 1613 | 1673 | if(!pGlob || (glob_match(pGlob, pFileFrom->zName) |
| 1614 | 1674 | || glob_match(pGlob, pFileTo->zName)) ){ |
| 1615 | 1675 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1616 | 1676 | pFileFrom->zUuid, |
| 1617 | 1677 | pFileTo->zUuid, 0, pCfg, |
| 1618 | - manifest_file_mperm(pFileTo)); | |
| 1678 | + manifest_file_mperm(pFileTo),aChng); | |
| 1619 | 1679 | } |
| 1620 | 1680 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1621 | 1681 | pFileTo = manifest_file_next(pTo, 0); |
| 1622 | 1682 | } |
| 1623 | 1683 | } |
| 1624 | 1684 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -371,10 +371,21 @@ | |
| 371 | pCfg->zLeftHash = 0; |
| 372 | blob_reset(&from); |
| 373 | blob_reset(&to); |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | ** Write a line of web-page output that shows changes that have occurred |
| 378 | ** to a file between two check-ins. |
| 379 | */ |
| 380 | static void append_file_change_line( |
| @@ -382,32 +393,38 @@ | |
| 382 | const char *zName, /* Name of the file that has changed */ |
| 383 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 384 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 385 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 386 | DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ |
| 387 | int mperm /* executable or symlink permission for zNew */ |
| 388 | ){ |
| 389 | @ <div class='file-change-line'><span> |
| 390 | /* Maintenance reminder: the extra level of SPAN is for |
| 391 | ** arranging new elements via JS. */ |
| 392 | if( !g.perm.Hyperlink ){ |
| 393 | if( zNew==0 ){ |
| 394 | @ Deleted %h(zName). |
| 395 | }else if( zOld==0 ){ |
| 396 | @ Added %h(zName). |
| 397 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 398 | @ Name change from %h(zOldName) to %h(zName). |
| 399 | }else if( fossil_strcmp(zNew, zOld)==0 ){ |
| 400 | if( mperm==PERM_EXE ){ |
| 401 | @ %h(zName) became executable. |
| 402 | }else if( mperm==PERM_LNK ){ |
| 403 | @ %h(zName) became a symlink. |
| 404 | }else{ |
| 405 | @ %h(zName) became a regular file. |
| 406 | } |
| 407 | }else{ |
| 408 | @ Changes to %h(zName). |
| 409 | } |
| 410 | @ </span></div> |
| 411 | if( pCfg ){ |
| 412 | append_diff(zOld, zNew, pCfg); |
| 413 | } |
| @@ -422,22 +439,25 @@ | |
| 422 | @ %h(zOldName)</a> |
| 423 | @ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> |
| 424 | @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 425 | @ %h(zName)</a> |
| 426 | @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 427 | }else{ |
| 428 | @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 429 | @ %h(zName)</a> |
| 430 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> |
| 431 | @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 432 | } |
| 433 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 434 | @ Name change |
| 435 | @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\ |
| 436 | @ %h(zOldName)</a> |
| 437 | @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 438 | @ %h(zName)</a>. |
| 439 | }else{ |
| 440 | @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 441 | @ %h(zName)</a> became |
| 442 | if( mperm==PERM_EXE ){ |
| 443 | @ executable with contents |
| @@ -445,17 +465,20 @@ | |
| 445 | @ a symlink with target |
| 446 | }else{ |
| 447 | @ a regular file with contents |
| 448 | } |
| 449 | @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 450 | } |
| 451 | }else if( zOld ){ |
| 452 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zOld,zCkin2))\ |
| 453 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 454 | }else{ |
| 455 | @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 456 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 457 | } |
| 458 | if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 459 | if( pCfg ){ |
| 460 | @ </span></div> |
| 461 | append_diff(zOld, zNew, pCfg); |
| @@ -624,10 +647,58 @@ | |
| 624 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 625 | &xtra); |
| 626 | db_finalize(&q); |
| 627 | style_finish_page(); |
| 628 | } |
| 629 | |
| 630 | /* |
| 631 | ** Render a web-page diff of the changes in the working check-out |
| 632 | */ |
| 633 | static void ckout_normal_diff(int vid){ |
| @@ -734,16 +805,11 @@ | |
| 734 | blob_reset(&old); |
| 735 | blob_reset(&new); |
| 736 | } |
| 737 | } |
| 738 | db_finalize(&q); |
| 739 | @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */ |
| 740 | @ document.getElementById('changes_section').textContent = 'Changes ' + |
| 741 | @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' + |
| 742 | @ '+%d(g.diffCnt[1]) ' + |
| 743 | @ '−%d(g.diffCnt[2]))' |
| 744 | @ </script> |
| 745 | append_diff_javascript(diffType); |
| 746 | } |
| 747 | |
| 748 | /* |
| 749 | ** Render a web-page diff of the changes in the working check-out to |
| @@ -827,16 +893,11 @@ | |
| 827 | } |
| 828 | fossil_free(zLhs); |
| 829 | fossil_free(zRhs); |
| 830 | } |
| 831 | db_finalize(&q); |
| 832 | @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */ |
| 833 | @ document.getElementById('changes_section').textContent = 'Changes ' + |
| 834 | @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' + |
| 835 | @ '+%d(g.diffCnt[1]) ' + |
| 836 | @ '−%d(g.diffCnt[2]))' |
| 837 | @ </script> |
| 838 | append_diff_javascript(diffType); |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | ** WEBPAGE: ckout |
| @@ -935,11 +996,13 @@ | |
| 935 | ReCompiled *pRe = 0; /* regex */ |
| 936 | const char *zW; /* URL param for ignoring whitespace */ |
| 937 | const char *zPage = "vinfo"; /* Page that shows diffs */ |
| 938 | const char *zBrName; /* Branch name */ |
| 939 | DiffConfig DCfg,*pCfg; /* Type of diff */ |
| 940 | |
| 941 | login_check_credentials(); |
| 942 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 943 | style_set_current_feature("vinfo"); |
| 944 | zName = P("name"); |
| 945 | rid = name_to_rid_www("name"); |
| @@ -1253,21 +1316,16 @@ | |
| 1253 | int mperm = db_column_int(&q3, 1); |
| 1254 | const char *zOld = db_column_text(&q3,2); |
| 1255 | const char *zNew = db_column_text(&q3,3); |
| 1256 | const char *zOldName = db_column_text(&q3, 4); |
| 1257 | append_file_change_line(zUuid, zName, zOld, zNew, zOldName, |
| 1258 | pCfg,mperm); |
| 1259 | } |
| 1260 | db_finalize(&q3); |
| 1261 | @ </div> |
| 1262 | if( diffType!=0 ){ |
| 1263 | @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */ |
| 1264 | @ document.getElementById('changes_section').textContent = 'Changes ' + |
| 1265 | @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' + |
| 1266 | @ '+%d(g.diffCnt[1]) ' + |
| 1267 | @ '−%d(g.diffCnt[2]))' |
| 1268 | @ </script> |
| 1269 | } |
| 1270 | append_diff_javascript(diffType); |
| 1271 | style_finish_page(); |
| 1272 | } |
| 1273 | |
| @@ -1449,11 +1507,13 @@ | |
| 1449 | DiffConfig DCfg, *pCfg = 0; |
| 1450 | int graphFlags = 0; |
| 1451 | Blob qp; /* non-glob= query parameters for generated links */ |
| 1452 | Blob qpGlob; /* glob= query parameter for generated links */ |
| 1453 | int bInvert = PB("inv"); |
| 1454 | |
| 1455 | login_check_credentials(); |
| 1456 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1457 | if( robot_restrict("diff") ) return; |
| 1458 | login_anonymous_available(); |
| 1459 | fossil_nice_default(); |
| @@ -1594,18 +1654,18 @@ | |
| 1594 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 1595 | } |
| 1596 | if( cmp<0 ){ |
| 1597 | if( !pGlob || glob_match(pGlob, pFileFrom->zName) ){ |
| 1598 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1599 | pFileFrom->zUuid, 0, 0, pCfg, 0); |
| 1600 | } |
| 1601 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1602 | }else if( cmp>0 ){ |
| 1603 | if( !pGlob || glob_match(pGlob, pFileTo->zName) ){ |
| 1604 | append_file_change_line(zTo, pFileTo->zName, |
| 1605 | 0, pFileTo->zUuid, 0, pCfg, |
| 1606 | manifest_file_mperm(pFileTo)); |
| 1607 | } |
| 1608 | pFileTo = manifest_file_next(pTo, 0); |
| 1609 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1610 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1611 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1613,11 +1673,11 @@ | |
| 1613 | if(!pGlob || (glob_match(pGlob, pFileFrom->zName) |
| 1614 | || glob_match(pGlob, pFileTo->zName)) ){ |
| 1615 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1616 | pFileFrom->zUuid, |
| 1617 | pFileTo->zUuid, 0, pCfg, |
| 1618 | manifest_file_mperm(pFileTo)); |
| 1619 | } |
| 1620 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1621 | pFileTo = manifest_file_next(pTo, 0); |
| 1622 | } |
| 1623 | } |
| 1624 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -371,10 +371,21 @@ | |
| 371 | pCfg->zLeftHash = 0; |
| 372 | blob_reset(&from); |
| 373 | blob_reset(&to); |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | ** The append_file_change_line() routine updates an array of counts |
| 378 | ** for various kinds of changes. |
| 379 | */ |
| 380 | #define FCHNG_NEW 0 |
| 381 | #define FCHNG_DELETE 1 |
| 382 | #define FCHNG_RENAME 2 |
| 383 | #define FCHNG_MODE 3 |
| 384 | #define FCHNG_EDIT 4 |
| 385 | #define N_FCHNG 5 |
| 386 | |
| 387 | /* |
| 388 | ** Write a line of web-page output that shows changes that have occurred |
| 389 | ** to a file between two check-ins. |
| 390 | */ |
| 391 | static void append_file_change_line( |
| @@ -382,32 +393,38 @@ | |
| 393 | const char *zName, /* Name of the file that has changed */ |
| 394 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 395 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 396 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 397 | DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ |
| 398 | int mperm, /* executable or symlink permission for zNew */ |
| 399 | int *aChng /* Change count array */ |
| 400 | ){ |
| 401 | @ <div class='file-change-line'><span> |
| 402 | /* Maintenance reminder: the extra level of SPAN is for |
| 403 | ** arranging new elements via JS. */ |
| 404 | if( !g.perm.Hyperlink ){ |
| 405 | if( zNew==0 ){ |
| 406 | @ Deleted %h(zName). |
| 407 | aChng[FCHNG_DELETE]++; |
| 408 | }else if( zOld==0 ){ |
| 409 | @ Added %h(zName). |
| 410 | aChng[FCHNG_NEW]++; |
| 411 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 412 | @ Name change from %h(zOldName) to %h(zName). |
| 413 | aChng[FCHNG_RENAME]++; |
| 414 | }else if( fossil_strcmp(zNew, zOld)==0 ){ |
| 415 | if( mperm==PERM_EXE ){ |
| 416 | @ %h(zName) became executable. |
| 417 | }else if( mperm==PERM_LNK ){ |
| 418 | @ %h(zName) became a symlink. |
| 419 | }else{ |
| 420 | @ %h(zName) became a regular file. |
| 421 | } |
| 422 | aChng[FCHNG_MODE]++; |
| 423 | }else{ |
| 424 | @ Changes to %h(zName). |
| 425 | aChng[FCHNG_EDIT]++; |
| 426 | } |
| 427 | @ </span></div> |
| 428 | if( pCfg ){ |
| 429 | append_diff(zOld, zNew, pCfg); |
| 430 | } |
| @@ -422,22 +439,25 @@ | |
| 439 | @ %h(zOldName)</a> |
| 440 | @ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> |
| 441 | @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 442 | @ %h(zName)</a> |
| 443 | @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 444 | aChng[FCHNG_RENAME]++; |
| 445 | }else{ |
| 446 | @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 447 | @ %h(zName)</a> |
| 448 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> |
| 449 | @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 450 | } |
| 451 | aChng[FCHNG_EDIT]++; |
| 452 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 453 | @ Name change |
| 454 | @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\ |
| 455 | @ %h(zOldName)</a> |
| 456 | @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 457 | @ %h(zName)</a>. |
| 458 | aChng[FCHNG_RENAME]++; |
| 459 | }else{ |
| 460 | @ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 461 | @ %h(zName)</a> became |
| 462 | if( mperm==PERM_EXE ){ |
| 463 | @ executable with contents |
| @@ -445,17 +465,20 @@ | |
| 465 | @ a symlink with target |
| 466 | }else{ |
| 467 | @ a regular file with contents |
| 468 | } |
| 469 | @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 470 | aChng[FCHNG_MODE]++; |
| 471 | } |
| 472 | }else if( zOld ){ |
| 473 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zOld,zCkin2))\ |
| 474 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 475 | aChng[FCHNG_DELETE]++; |
| 476 | }else{ |
| 477 | @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\ |
| 478 | @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. |
| 479 | aChng[FCHNG_NEW]++; |
| 480 | } |
| 481 | if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 482 | if( pCfg ){ |
| 483 | @ </span></div> |
| 484 | append_diff(zOld, zNew, pCfg); |
| @@ -624,10 +647,58 @@ | |
| 647 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 648 | &xtra); |
| 649 | db_finalize(&q); |
| 650 | style_finish_page(); |
| 651 | } |
| 652 | |
| 653 | /* |
| 654 | ** Generate JS code that will adjust the change counts in the title |
| 655 | ** of the "changes_section" panel where "changes_section" is a JS id. |
| 656 | ** |
| 657 | ** The change counts are taken from g.diffCnt[] for edits, and from |
| 658 | ** the aChng[] parameter for additions, deletions, and modifications. |
| 659 | ** The aChng parameter may be NULL, in which case those factors are |
| 660 | ** ignored. |
| 661 | */ |
| 662 | static void adjust_changes_section_title( |
| 663 | int iLine, /* Souce code line number of caller */ |
| 664 | int *aChng /* Change categories */ |
| 665 | ){ |
| 666 | sqlite3_str *pTxt = sqlite3_str_new(0); |
| 667 | if( g.diffCnt[0]>0 ){ |
| 668 | sqlite3_str_appendf(pTxt, "%d file%s edited [+%d -%d lines]", |
| 669 | g.diffCnt[0], g.diffCnt[0]==1 ? "" : "s", |
| 670 | g.diffCnt[1], g.diffCnt[2]); |
| 671 | } |
| 672 | if( aChng ){ |
| 673 | static const int aiType[] = |
| 674 | { FCHNG_NEW, FCHNG_DELETE, FCHNG_RENAME, FCHNG_MODE }; |
| 675 | static const char *azType[] = |
| 676 | { "added", "deleted", "renamed", "mode change"}; |
| 677 | int i; |
| 678 | for(i=0; i<4; i++){ |
| 679 | int j = aiType[i]; |
| 680 | if( aChng[j] ){ |
| 681 | const char *zFiles = ""; |
| 682 | if( sqlite3_str_length(pTxt)>0 ){ |
| 683 | sqlite3_str_append(pTxt,", ", 2); |
| 684 | }else{ |
| 685 | zFiles = i<3 && aChng[j]>1 ? " files" : " file"; |
| 686 | } |
| 687 | sqlite3_str_appendf(pTxt,"%d%s %s", aChng[j], zFiles, azType[i]); |
| 688 | if( i==3 && aChng[j]>1 ) sqlite3_str_appendf(pTxt, "s"); |
| 689 | } |
| 690 | } |
| 691 | } |
| 692 | if( sqlite3_str_length(pTxt)>0 ){ |
| 693 | @ <script nonce='%h(style_nonce())'>;/* info.c:%d(iLine) */ |
| 694 | @ document.getElementById('changes_section').textContent = \ |
| 695 | @ 'Changes (%h(sqlite3_str_value(pTxt)))' |
| 696 | @ </script> |
| 697 | } |
| 698 | sqlite3_str_free(pTxt); |
| 699 | } |
| 700 | |
| 701 | /* |
| 702 | ** Render a web-page diff of the changes in the working check-out |
| 703 | */ |
| 704 | static void ckout_normal_diff(int vid){ |
| @@ -734,16 +805,11 @@ | |
| 805 | blob_reset(&old); |
| 806 | blob_reset(&new); |
| 807 | } |
| 808 | } |
| 809 | db_finalize(&q); |
| 810 | adjust_changes_section_title(__LINE__, 0); |
| 811 | append_diff_javascript(diffType); |
| 812 | } |
| 813 | |
| 814 | /* |
| 815 | ** Render a web-page diff of the changes in the working check-out to |
| @@ -827,16 +893,11 @@ | |
| 893 | } |
| 894 | fossil_free(zLhs); |
| 895 | fossil_free(zRhs); |
| 896 | } |
| 897 | db_finalize(&q); |
| 898 | adjust_changes_section_title(__LINE__, 0); |
| 899 | append_diff_javascript(diffType); |
| 900 | } |
| 901 | |
| 902 | /* |
| 903 | ** WEBPAGE: ckout |
| @@ -935,11 +996,13 @@ | |
| 996 | ReCompiled *pRe = 0; /* regex */ |
| 997 | const char *zW; /* URL param for ignoring whitespace */ |
| 998 | const char *zPage = "vinfo"; /* Page that shows diffs */ |
| 999 | const char *zBrName; /* Branch name */ |
| 1000 | DiffConfig DCfg,*pCfg; /* Type of diff */ |
| 1001 | int aChng[N_FCHNG]; /* Counts of change types */ |
| 1002 | |
| 1003 | memset(aChng, 0, sizeof(aChng)); |
| 1004 | login_check_credentials(); |
| 1005 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1006 | style_set_current_feature("vinfo"); |
| 1007 | zName = P("name"); |
| 1008 | rid = name_to_rid_www("name"); |
| @@ -1253,21 +1316,16 @@ | |
| 1316 | int mperm = db_column_int(&q3, 1); |
| 1317 | const char *zOld = db_column_text(&q3,2); |
| 1318 | const char *zNew = db_column_text(&q3,3); |
| 1319 | const char *zOldName = db_column_text(&q3, 4); |
| 1320 | append_file_change_line(zUuid, zName, zOld, zNew, zOldName, |
| 1321 | pCfg,mperm,aChng); |
| 1322 | } |
| 1323 | db_finalize(&q3); |
| 1324 | @ </div> |
| 1325 | if( diffType!=0 ){ |
| 1326 | adjust_changes_section_title(__LINE__, aChng); |
| 1327 | } |
| 1328 | append_diff_javascript(diffType); |
| 1329 | style_finish_page(); |
| 1330 | } |
| 1331 | |
| @@ -1449,11 +1507,13 @@ | |
| 1507 | DiffConfig DCfg, *pCfg = 0; |
| 1508 | int graphFlags = 0; |
| 1509 | Blob qp; /* non-glob= query parameters for generated links */ |
| 1510 | Blob qpGlob; /* glob= query parameter for generated links */ |
| 1511 | int bInvert = PB("inv"); |
| 1512 | int aChng[N_FCHNG]; /* Count of versiou change types */ |
| 1513 | |
| 1514 | memset(aChng, 0, sizeof(aChng)); |
| 1515 | login_check_credentials(); |
| 1516 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1517 | if( robot_restrict("diff") ) return; |
| 1518 | login_anonymous_available(); |
| 1519 | fossil_nice_default(); |
| @@ -1594,18 +1654,18 @@ | |
| 1654 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 1655 | } |
| 1656 | if( cmp<0 ){ |
| 1657 | if( !pGlob || glob_match(pGlob, pFileFrom->zName) ){ |
| 1658 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1659 | pFileFrom->zUuid, 0, 0, pCfg, 0,aChng); |
| 1660 | } |
| 1661 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1662 | }else if( cmp>0 ){ |
| 1663 | if( !pGlob || glob_match(pGlob, pFileTo->zName) ){ |
| 1664 | append_file_change_line(zTo, pFileTo->zName, |
| 1665 | 0, pFileTo->zUuid, 0, pCfg, |
| 1666 | manifest_file_mperm(pFileTo),aChng); |
| 1667 | } |
| 1668 | pFileTo = manifest_file_next(pTo, 0); |
| 1669 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 1670 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1671 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1613,11 +1673,11 @@ | |
| 1673 | if(!pGlob || (glob_match(pGlob, pFileFrom->zName) |
| 1674 | || glob_match(pGlob, pFileTo->zName)) ){ |
| 1675 | append_file_change_line(zFrom, pFileFrom->zName, |
| 1676 | pFileFrom->zUuid, |
| 1677 | pFileTo->zUuid, 0, pCfg, |
| 1678 | manifest_file_mperm(pFileTo),aChng); |
| 1679 | } |
| 1680 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1681 | pFileTo = manifest_file_next(pTo, 0); |
| 1682 | } |
| 1683 | } |
| 1684 |