Fossil SCM
Add options to the 'tag' command to list tag values, as suggested by [forum:d4fb9400f9|forum post d4fb9400f9].
Commit
ea1b76bc9abdf00f46688ff72f6a973fd5e7b0dd45e5a005f8998c0b47767212
Parent
de8f8ceb9358486…
1 file changed
+53
-13
+53
-13
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -468,12 +468,17 @@ | ||
| 468 | 468 | ** (technote), and "tkt-" (ticket). The |
| 469 | 469 | ** prefix is stripped from the resulting |
| 470 | 470 | ** list unless --raw is provided. Ignored if |
| 471 | 471 | ** ARTIFACT-ID is provided. |
| 472 | 472 | ** --raw List raw names of tags |
| 473 | +** --sep SEP Separator when concatenating values | |
| 473 | 474 | ** --tagtype TYPE List only tags of type TYPE, which must |
| 474 | 475 | ** be one of: cancel, singleton, propagated |
| 476 | +** --values List tag values | |
| 477 | +** If --sep is supplied, list all values of a tag on | |
| 478 | +** the same line, separated by SEP; otherwise list | |
| 479 | +** each value on its own line. | |
| 475 | 480 | ** |
| 476 | 481 | ** The option --raw allows the manipulation of all types of tags |
| 477 | 482 | ** used for various internal purposes in fossil. It also shows |
| 478 | 483 | ** "cancel" tags for the "find" and "list" subcommands. You should |
| 479 | 484 | ** not use this option to make changes unless you are sure what |
| @@ -635,10 +640,13 @@ | ||
| 635 | 640 | const int fRaw = find_option("raw","",0)!=0; |
| 636 | 641 | const char *zTagType = find_option("tagtype","t",1); |
| 637 | 642 | const int fInverse = find_option("inverse","v",0)!=0; |
| 638 | 643 | const char *zTagPrefix = find_option("prefix","",1); |
| 639 | 644 | int nTagType = fRaw ? -1 : 0; |
| 645 | + int fValues = find_option("values","",0)!=0; | |
| 646 | + const char *zSep = find_option("sep","",1); | |
| 647 | + | |
| 640 | 648 | |
| 641 | 649 | if( zTagType!=0 ){ |
| 642 | 650 | int l = strlen(zTagType); |
| 643 | 651 | if( strncmp(zTagType,"cancel",l)==0 ){ |
| 644 | 652 | nTagType = 0; |
| @@ -650,29 +658,61 @@ | ||
| 650 | 658 | fossil_fatal("unrecognized tag type"); |
| 651 | 659 | } |
| 652 | 660 | } |
| 653 | 661 | if( g.argc==3 ){ |
| 654 | 662 | const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0; |
| 655 | - db_prepare(&q, | |
| 656 | - "SELECT tagname FROM tag" | |
| 657 | - " WHERE EXISTS(SELECT 1 FROM tagxref" | |
| 658 | - " WHERE tagid=tag.tagid" | |
| 659 | - " AND tagtype%s%d)" | |
| 660 | - " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' " | |
| 661 | - " END ORDER BY tagname COLLATE uintnocase", | |
| 662 | - zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, | |
| 663 | - nTagType, zTagPrefix, zTagPrefix | |
| 664 | - ); | |
| 663 | + if( !fValues ){ | |
| 664 | + db_prepare(&q, | |
| 665 | + "SELECT tagname FROM tag" | |
| 666 | + " WHERE EXISTS(SELECT 1 FROM tagxref" | |
| 667 | + " WHERE tagid=tag.tagid" | |
| 668 | + " AND tagtype%s%d)" | |
| 669 | + " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' " | |
| 670 | + " END ORDER BY tagname COLLATE uintnocase", | |
| 671 | + zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, | |
| 672 | + nTagType, zTagPrefix, zTagPrefix | |
| 673 | + ); | |
| 674 | + }else{ | |
| 675 | + if( zSep ){ | |
| 676 | + db_prepare(&q, | |
| 677 | + /* work around group_concat() with DISTINCT and custom separator */ | |
| 678 | + "SELECT tagname," | |
| 679 | + " rtrim(replace(group_concat(DISTINCT value||'@!' " | |
| 680 | + " ORDER BY value ASC), '@!,', %Q),'@!')" | |
| 681 | + " FROM tagxref, tag" | |
| 682 | + " WHERE tagxref.tagid=tag.tagid AND tagtype%s%d" | |
| 683 | + " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END" | |
| 684 | + " GROUP BY tagname" | |
| 685 | + " ORDER BY tagname COLLATE uintnocase", | |
| 686 | + ( zSep && strlen(zSep)>0 ) ? zSep : ",", | |
| 687 | + zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, | |
| 688 | + nTagType, zTagPrefix, zTagPrefix | |
| 689 | + ); | |
| 690 | + }else{ | |
| 691 | + db_prepare(&q, | |
| 692 | + "SELECT DISTINCT tagname, value" | |
| 693 | + " FROM tagxref, tag" | |
| 694 | + " WHERE tagxref.tagid=tag.tagid AND tagtype%s%d" | |
| 695 | + " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END" | |
| 696 | + " ORDER BY tagname, value COLLATE uintnocase", | |
| 697 | + zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, | |
| 698 | + nTagType, zTagPrefix, zTagPrefix | |
| 699 | + ); | |
| 700 | + } | |
| 701 | + } | |
| 665 | 702 | while( db_step(&q)==SQLITE_ROW ){ |
| 666 | 703 | const char *zName = db_column_text(&q, 0); |
| 704 | + const char *zValue = db_column_text(&q, 1); | |
| 705 | + int nWidth = fValues ? 20 : 0; | |
| 706 | + const char *zzValue = (fValues && zValue) ? mprintf(" %s", zValue) : ""; | |
| 667 | 707 | if( fRaw ){ |
| 668 | - fossil_print("%s\n", zName); | |
| 708 | + fossil_print("%-*s%s\n", nWidth, zName, zzValue); | |
| 669 | 709 | }else if( nTagPrefix>0 ){ |
| 670 | 710 | assert(db_column_bytes(&q,0)>=nTagPrefix); |
| 671 | - fossil_print("%s\n", &zName[nTagPrefix]); | |
| 711 | + fossil_print("%-*s%s\n", nWidth, &zName[nTagPrefix], zzValue); | |
| 672 | 712 | }else if( strncmp(zName, "sym-", 4)==0 ){ |
| 673 | - fossil_print("%s\n", &zName[4]); | |
| 713 | + fossil_print("%-*s%s\n", nWidth, &zName[4], zzValue); | |
| 674 | 714 | } |
| 675 | 715 | } |
| 676 | 716 | db_finalize(&q); |
| 677 | 717 | }else if( g.argc==4 ){ |
| 678 | 718 | char const *zObjId = g.argv[3]; |
| 679 | 719 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -468,12 +468,17 @@ | |
| 468 | ** (technote), and "tkt-" (ticket). The |
| 469 | ** prefix is stripped from the resulting |
| 470 | ** list unless --raw is provided. Ignored if |
| 471 | ** ARTIFACT-ID is provided. |
| 472 | ** --raw List raw names of tags |
| 473 | ** --tagtype TYPE List only tags of type TYPE, which must |
| 474 | ** be one of: cancel, singleton, propagated |
| 475 | ** |
| 476 | ** The option --raw allows the manipulation of all types of tags |
| 477 | ** used for various internal purposes in fossil. It also shows |
| 478 | ** "cancel" tags for the "find" and "list" subcommands. You should |
| 479 | ** not use this option to make changes unless you are sure what |
| @@ -635,10 +640,13 @@ | |
| 635 | const int fRaw = find_option("raw","",0)!=0; |
| 636 | const char *zTagType = find_option("tagtype","t",1); |
| 637 | const int fInverse = find_option("inverse","v",0)!=0; |
| 638 | const char *zTagPrefix = find_option("prefix","",1); |
| 639 | int nTagType = fRaw ? -1 : 0; |
| 640 | |
| 641 | if( zTagType!=0 ){ |
| 642 | int l = strlen(zTagType); |
| 643 | if( strncmp(zTagType,"cancel",l)==0 ){ |
| 644 | nTagType = 0; |
| @@ -650,29 +658,61 @@ | |
| 650 | fossil_fatal("unrecognized tag type"); |
| 651 | } |
| 652 | } |
| 653 | if( g.argc==3 ){ |
| 654 | const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0; |
| 655 | db_prepare(&q, |
| 656 | "SELECT tagname FROM tag" |
| 657 | " WHERE EXISTS(SELECT 1 FROM tagxref" |
| 658 | " WHERE tagid=tag.tagid" |
| 659 | " AND tagtype%s%d)" |
| 660 | " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' " |
| 661 | " END ORDER BY tagname COLLATE uintnocase", |
| 662 | zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, |
| 663 | nTagType, zTagPrefix, zTagPrefix |
| 664 | ); |
| 665 | while( db_step(&q)==SQLITE_ROW ){ |
| 666 | const char *zName = db_column_text(&q, 0); |
| 667 | if( fRaw ){ |
| 668 | fossil_print("%s\n", zName); |
| 669 | }else if( nTagPrefix>0 ){ |
| 670 | assert(db_column_bytes(&q,0)>=nTagPrefix); |
| 671 | fossil_print("%s\n", &zName[nTagPrefix]); |
| 672 | }else if( strncmp(zName, "sym-", 4)==0 ){ |
| 673 | fossil_print("%s\n", &zName[4]); |
| 674 | } |
| 675 | } |
| 676 | db_finalize(&q); |
| 677 | }else if( g.argc==4 ){ |
| 678 | char const *zObjId = g.argv[3]; |
| 679 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -468,12 +468,17 @@ | |
| 468 | ** (technote), and "tkt-" (ticket). The |
| 469 | ** prefix is stripped from the resulting |
| 470 | ** list unless --raw is provided. Ignored if |
| 471 | ** ARTIFACT-ID is provided. |
| 472 | ** --raw List raw names of tags |
| 473 | ** --sep SEP Separator when concatenating values |
| 474 | ** --tagtype TYPE List only tags of type TYPE, which must |
| 475 | ** be one of: cancel, singleton, propagated |
| 476 | ** --values List tag values |
| 477 | ** If --sep is supplied, list all values of a tag on |
| 478 | ** the same line, separated by SEP; otherwise list |
| 479 | ** each value on its own line. |
| 480 | ** |
| 481 | ** The option --raw allows the manipulation of all types of tags |
| 482 | ** used for various internal purposes in fossil. It also shows |
| 483 | ** "cancel" tags for the "find" and "list" subcommands. You should |
| 484 | ** not use this option to make changes unless you are sure what |
| @@ -635,10 +640,13 @@ | |
| 640 | const int fRaw = find_option("raw","",0)!=0; |
| 641 | const char *zTagType = find_option("tagtype","t",1); |
| 642 | const int fInverse = find_option("inverse","v",0)!=0; |
| 643 | const char *zTagPrefix = find_option("prefix","",1); |
| 644 | int nTagType = fRaw ? -1 : 0; |
| 645 | int fValues = find_option("values","",0)!=0; |
| 646 | const char *zSep = find_option("sep","",1); |
| 647 | |
| 648 | |
| 649 | if( zTagType!=0 ){ |
| 650 | int l = strlen(zTagType); |
| 651 | if( strncmp(zTagType,"cancel",l)==0 ){ |
| 652 | nTagType = 0; |
| @@ -650,29 +658,61 @@ | |
| 658 | fossil_fatal("unrecognized tag type"); |
| 659 | } |
| 660 | } |
| 661 | if( g.argc==3 ){ |
| 662 | const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0; |
| 663 | if( !fValues ){ |
| 664 | db_prepare(&q, |
| 665 | "SELECT tagname FROM tag" |
| 666 | " WHERE EXISTS(SELECT 1 FROM tagxref" |
| 667 | " WHERE tagid=tag.tagid" |
| 668 | " AND tagtype%s%d)" |
| 669 | " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' " |
| 670 | " END ORDER BY tagname COLLATE uintnocase", |
| 671 | zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, |
| 672 | nTagType, zTagPrefix, zTagPrefix |
| 673 | ); |
| 674 | }else{ |
| 675 | if( zSep ){ |
| 676 | db_prepare(&q, |
| 677 | /* work around group_concat() with DISTINCT and custom separator */ |
| 678 | "SELECT tagname," |
| 679 | " rtrim(replace(group_concat(DISTINCT value||'@!' " |
| 680 | " ORDER BY value ASC), '@!,', %Q),'@!')" |
| 681 | " FROM tagxref, tag" |
| 682 | " WHERE tagxref.tagid=tag.tagid AND tagtype%s%d" |
| 683 | " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END" |
| 684 | " GROUP BY tagname" |
| 685 | " ORDER BY tagname COLLATE uintnocase", |
| 686 | ( zSep && strlen(zSep)>0 ) ? zSep : ",", |
| 687 | zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, |
| 688 | nTagType, zTagPrefix, zTagPrefix |
| 689 | ); |
| 690 | }else{ |
| 691 | db_prepare(&q, |
| 692 | "SELECT DISTINCT tagname, value" |
| 693 | " FROM tagxref, tag" |
| 694 | " WHERE tagxref.tagid=tag.tagid AND tagtype%s%d" |
| 695 | " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END" |
| 696 | " ORDER BY tagname, value COLLATE uintnocase", |
| 697 | zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, |
| 698 | nTagType, zTagPrefix, zTagPrefix |
| 699 | ); |
| 700 | } |
| 701 | } |
| 702 | while( db_step(&q)==SQLITE_ROW ){ |
| 703 | const char *zName = db_column_text(&q, 0); |
| 704 | const char *zValue = db_column_text(&q, 1); |
| 705 | int nWidth = fValues ? 20 : 0; |
| 706 | const char *zzValue = (fValues && zValue) ? mprintf(" %s", zValue) : ""; |
| 707 | if( fRaw ){ |
| 708 | fossil_print("%-*s%s\n", nWidth, zName, zzValue); |
| 709 | }else if( nTagPrefix>0 ){ |
| 710 | assert(db_column_bytes(&q,0)>=nTagPrefix); |
| 711 | fossil_print("%-*s%s\n", nWidth, &zName[nTagPrefix], zzValue); |
| 712 | }else if( strncmp(zName, "sym-", 4)==0 ){ |
| 713 | fossil_print("%-*s%s\n", nWidth, &zName[4], zzValue); |
| 714 | } |
| 715 | } |
| 716 | db_finalize(&q); |
| 717 | }else if( g.argc==4 ){ |
| 718 | char const *zObjId = g.argv[3]; |
| 719 |