| | @@ -278,10 +278,11 @@ |
| 278 | 278 | #define BRL_ORDERBY_MTIME 0x004 /* Sort by MTIME. (otherwise sort by name)*/ |
| 279 | 279 | #define BRL_REVERSE 0x008 /* Reverse the sort order */ |
| 280 | 280 | #define BRL_PRIVATE 0x010 /* Show only private branches */ |
| 281 | 281 | #define BRL_MERGED 0x020 /* Show only merged branches */ |
| 282 | 282 | #define BRL_UNMERGED 0x040 /* Show only unmerged branches */ |
| 283 | +#define BRL_LIST_USERS 0x080 /* Populate list of users participating */ |
| 283 | 284 | |
| 284 | 285 | #endif /* INTERFACE */ |
| 285 | 286 | |
| 286 | 287 | /* |
| 287 | 288 | ** Prepare a query that will list branches. |
| | @@ -297,21 +298,38 @@ |
| 297 | 298 | */ |
| 298 | 299 | void branch_prepare_list_query( |
| 299 | 300 | Stmt *pQuery, |
| 300 | 301 | int brFlags, |
| 301 | 302 | const char *zBrNameGlob, |
| 302 | | - int nLimitMRU |
| 303 | + int nLimitMRU, |
| 304 | + const char *zUser |
| 303 | 305 | ){ |
| 304 | 306 | Blob sql; |
| 305 | 307 | blob_init(&sql, 0, 0); |
| 306 | 308 | brlist_create_temp_table(); |
| 307 | 309 | /* Ignore nLimitMRU if no chronological sort requested. */ |
| 308 | 310 | if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0; |
| 309 | 311 | /* Undocumented: invert negative values for nLimitMRU, so that command-line |
| 310 | 312 | ** arguments similar to `head -5' with "option numbers" are possible. */ |
| 311 | 313 | if( nLimitMRU<0 ) nLimitMRU = -nLimitMRU; |
| 312 | | - blob_append_sql(&sql,"SELECT name, isprivate, mergeto FROM ("); /* OUTER QUERY */ |
| 314 | + /* OUTER QUERY */ |
| 315 | + blob_append_sql(&sql,"SELECT name, isprivate, mergeto,"); |
| 316 | + if( brFlags & BRL_LIST_USERS ){ |
| 317 | + blob_append_sql(&sql, |
| 318 | + " (SELECT group_concat(user) FROM (" |
| 319 | + " SELECT DISTINCT * FROM (" |
| 320 | + " SELECT coalesce(euser,user) AS user" |
| 321 | + " FROM event" |
| 322 | + " WHERE type='ci' AND objid IN (" |
| 323 | + " SELECT rid FROM tagxref WHERE value=name)" |
| 324 | + " ORDER BY 1)))" |
| 325 | + ); |
| 326 | + }else{ |
| 327 | + blob_append_sql(&sql, " NULL"); |
| 328 | + } |
| 329 | + blob_append_sql(&sql," FROM ("); |
| 330 | + /* INNER QUERY */ |
| 313 | 331 | switch( brFlags & BRL_OPEN_CLOSED_MASK ){ |
| 314 | 332 | case BRL_CLOSED_ONLY: { |
| 315 | 333 | blob_append_sql(&sql, |
| 316 | 334 | "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE isclosed" |
| 317 | 335 | ); |
| | @@ -330,11 +348,16 @@ |
| 330 | 348 | break; |
| 331 | 349 | } |
| 332 | 350 | } |
| 333 | 351 | if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate"); |
| 334 | 352 | if( brFlags & BRL_MERGED ) blob_append_sql(&sql, " AND mergeto IS NOT NULL"); |
| 335 | | - if(zBrNameGlob) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob); |
| 353 | + if( zBrNameGlob ) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob); |
| 354 | + if( zUser && zUser[0] ) blob_append_sql(&sql, |
| 355 | + " AND EXISTS (SELECT 1 FROM event WHERE type='ci' AND (user=%Q OR euser=%Q)" |
| 356 | + " AND objid in (SELECT rid FROM tagxref WHERE value=tmp_brlist.name))", |
| 357 | + zUser, zUser |
| 358 | + ); |
| 336 | 359 | if( brFlags & BRL_ORDERBY_MTIME ){ |
| 337 | 360 | blob_append_sql(&sql, " ORDER BY -mtime"); |
| 338 | 361 | }else{ |
| 339 | 362 | blob_append_sql(&sql, " ORDER BY name COLLATE nocase"); |
| 340 | 363 | } |
| | @@ -614,17 +637,20 @@ |
| 614 | 637 | ** > fossil branch lsh ?OPTIONS? ?LIMIT? |
| 615 | 638 | ** |
| 616 | 639 | ** List all branches. |
| 617 | 640 | ** |
| 618 | 641 | ** Options: |
| 619 | | -** -a|--all List all branches. Default show only open branches |
| 620 | | -** -c|--closed List closed branches |
| 621 | | -** -m|--merged List branches merged into the current branch |
| 622 | | -** -M|--unmerged List branches not merged into the current branch |
| 623 | | -** -p List only private branches |
| 624 | | -** -r Reverse the sort order |
| 625 | | -** -t Show recently changed branches first |
| 642 | +** -a|--all List all branches. Default show only open branches |
| 643 | +** -c|--closed List closed branches |
| 644 | +** -m|--merged List branches merged into the current branch |
| 645 | +** -M|--unmerged List branches not merged into the current branch |
| 646 | +** -p List only private branches |
| 647 | +** -r Reverse the sort order |
| 648 | +** -t Show recently changed branches first |
| 649 | +** --self List only branches where you participate |
| 650 | +** --username USER List only branches where USER participate |
| 651 | +** --users N List up to N users partipiating |
| 626 | 652 | ** |
| 627 | 653 | ** The current branch is marked with an asterisk. Private branches are |
| 628 | 654 | ** marked with a hash sign. |
| 629 | 655 | ** |
| 630 | 656 | ** If GLOB is given, show only branches matching the pattern. |
| | @@ -689,26 +715,42 @@ |
| 689 | 715 | } |
| 690 | 716 | }else if( strncmp(zCmd,"list",n)==0 || |
| 691 | 717 | strncmp(zCmd, "ls", n)==0 || |
| 692 | 718 | strcmp(zCmd, "lsh")==0 ){ |
| 693 | 719 | Stmt q; |
| 720 | + Blob txt = empty_blob; |
| 694 | 721 | int vid; |
| 695 | 722 | char *zCurrent = 0; |
| 696 | 723 | const char *zBrNameGlob = 0; |
| 724 | + const char *zUser = find_option("username",0,1); |
| 725 | + const char *zUsersOpt = find_option("users",0,1); |
| 726 | + int nUsers = zUsersOpt ? atoi(zUsersOpt) : 0; |
| 697 | 727 | int nLimit = 0; |
| 698 | 728 | int brFlags = BRL_OPEN_ONLY; |
| 699 | 729 | if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH; |
| 700 | 730 | if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY; |
| 701 | 731 | if( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME; |
| 702 | 732 | if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE; |
| 703 | 733 | if( find_option("p",0,0)!=0 ) brFlags |= BRL_PRIVATE; |
| 704 | 734 | if( find_option("merged","m",0)!=0 ) brFlags |= BRL_MERGED; |
| 705 | 735 | if( find_option("unmerged","M",0)!=0 ) brFlags |= BRL_UNMERGED; |
| 736 | + if( find_option("self",0,0)!=0 ){ |
| 737 | + if( zUser ){ |
| 738 | + fossil_fatal("flags --username and --self are mutually exclusive"); |
| 739 | + } |
| 740 | + user_select(); |
| 741 | + zUser = login_name(); |
| 742 | + } |
| 743 | + verify_all_options(); |
| 706 | 744 | |
| 707 | 745 | if ( (brFlags & BRL_MERGED) && (brFlags & BRL_UNMERGED) ){ |
| 708 | 746 | fossil_fatal("flags --merged and --unmerged are mutually exclusive"); |
| 709 | 747 | } |
| 748 | + if( zUsersOpt ){ |
| 749 | + if( nUsers <= 0) fossil_fatal("With --users, N must be positive"); |
| 750 | + brFlags |= BRL_LIST_USERS; |
| 751 | + } |
| 710 | 752 | if( strcmp(zCmd, "lsh")==0 ){ |
| 711 | 753 | nLimit = 5; |
| 712 | 754 | if( g.argc>4 || (g.argc==4 && (nLimit = atoi(g.argv[3]))==0) ){ |
| 713 | 755 | fossil_fatal("the lsh subcommand allows one optional numeric argument"); |
| 714 | 756 | } |
| | @@ -720,26 +762,48 @@ |
| 720 | 762 | if( g.localOpen ){ |
| 721 | 763 | vid = db_lget_int("checkout", 0); |
| 722 | 764 | zCurrent = db_text(0, "SELECT value FROM tagxref" |
| 723 | 765 | " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); |
| 724 | 766 | } |
| 725 | | - branch_prepare_list_query(&q, brFlags, zBrNameGlob, nLimit); |
| 767 | + branch_prepare_list_query(&q, brFlags, zBrNameGlob, nLimit, zUser); |
| 768 | + blob_init(&txt, 0, 0); |
| 726 | 769 | while( db_step(&q)==SQLITE_ROW ){ |
| 727 | 770 | const char *zBr = db_column_text(&q, 0); |
| 728 | 771 | int isPriv = db_column_int(&q, 1)==1; |
| 729 | 772 | const char *zMergeTo = db_column_text(&q, 2); |
| 730 | 773 | int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; |
| 774 | + const char *zUsers = db_column_text(&q, 3); |
| 731 | 775 | if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){ |
| 732 | 776 | continue; |
| 733 | 777 | } |
| 734 | 778 | if( (brFlags & BRL_UNMERGED) && (fossil_strcmp(zCurrent,zMergeTo)==0 |
| 735 | 779 | || isCur) ){ |
| 736 | 780 | continue; |
| 737 | 781 | } |
| 738 | | - fossil_print("%s%s%s\n", |
| 782 | + blob_appendf(&txt, "%s%s%s", |
| 739 | 783 | ( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ), |
| 740 | 784 | (isCur ? "* " : " "), zBr); |
| 785 | + if( nUsers ){ |
| 786 | + char c; |
| 787 | + const char *cp; |
| 788 | + const char *pComma = 0; |
| 789 | + int commas = 0; |
| 790 | + for( cp = zUsers; ( c = *cp ) != 0; cp++ ){ |
| 791 | + if( c == ',' ){ |
| 792 | + commas++; |
| 793 | + if( commas == nUsers ) pComma = cp; |
| 794 | + } |
| 795 | + } |
| 796 | + if( pComma ){ |
| 797 | + blob_appendf(&txt, " (%.*s,... %i more)", |
| 798 | + pComma - zUsers, zUsers, commas + 1 - nUsers); |
| 799 | + }else{ |
| 800 | + blob_appendf(&txt, " (%s)", zUsers); |
| 801 | + } |
| 802 | + } |
| 803 | + fossil_print("%s\n", blob_str(&txt)); |
| 804 | + blob_reset(&txt); |
| 741 | 805 | } |
| 742 | 806 | db_finalize(&q); |
| 743 | 807 | }else if( strncmp(zCmd,"new",n)==0 ){ |
| 744 | 808 | branch_new(); |
| 745 | 809 | }else if( strncmp(zCmd,"close",5)==0 ){ |
| | @@ -922,11 +986,11 @@ |
| 922 | 986 | @ reopened).</li> |
| 923 | 987 | @ </ol> |
| 924 | 988 | style_sidebox_end(); |
| 925 | 989 | #endif |
| 926 | 990 | |
| 927 | | - branch_prepare_list_query(&q, brFlags, 0, 0); |
| 991 | + branch_prepare_list_query(&q, brFlags, 0, 0, 0); |
| 928 | 992 | cnt = 0; |
| 929 | 993 | while( db_step(&q)==SQLITE_ROW ){ |
| 930 | 994 | const char *zBr = db_column_text(&q, 0); |
| 931 | 995 | if( cnt==0 ){ |
| 932 | 996 | if( colorTest ){ |
| 933 | 997 | |