Fossil SCM

Updated tag list subcommand to be able to deal with propagated tags on wiki, forum post, and technote artifacts, with an eye towards not breaking any historical scripting uses.

stephan 2021-06-04 11:39 trunk
Commit c797abe4c377d14f3c642d038d6e8f7f1cec914aac012d03966c22ab541c3474
+5 -1
--- src/name.c
+++ src/name.c
@@ -744,14 +744,18 @@
744744
** Given an RID of a structural artifact, which is assumed to be
745745
** valid, this function returns one of the CFTYPE_xxx values
746746
** describing the record type, or 0 if the RID does not refer to an
747747
** artifact record (as determined by reading the event table).
748748
**
749
-** Note that this function neve returns CFTYPE_ATTACHMENT or
749
+** Note that this function never returns CFTYPE_ATTACHMENT or
750750
** CFTYPE_CLUSTER because those are not used in the event table. Thus
751751
** it cannot be used to distinguish those artifact types from
752752
** non-artifact file content.
753
+**
754
+** Potential TODO: if the rid is not found in the timeline, try to
755
+** match it to a client file and return a hypothetical new
756
+** CFTYPE_OPAQUE or CFTYPE_NONARTIFACT if a match is found.
753757
*/
754758
int whatis_rid_type(int rid){
755759
Stmt q = empty_Stmt;
756760
int type = 0;
757761
/* Check for entries on the timeline that reference this object */
758762
--- src/name.c
+++ src/name.c
@@ -744,14 +744,18 @@
744 ** Given an RID of a structural artifact, which is assumed to be
745 ** valid, this function returns one of the CFTYPE_xxx values
746 ** describing the record type, or 0 if the RID does not refer to an
747 ** artifact record (as determined by reading the event table).
748 **
749 ** Note that this function neve returns CFTYPE_ATTACHMENT or
750 ** CFTYPE_CLUSTER because those are not used in the event table. Thus
751 ** it cannot be used to distinguish those artifact types from
752 ** non-artifact file content.
 
 
 
 
753 */
754 int whatis_rid_type(int rid){
755 Stmt q = empty_Stmt;
756 int type = 0;
757 /* Check for entries on the timeline that reference this object */
758
--- src/name.c
+++ src/name.c
@@ -744,14 +744,18 @@
744 ** Given an RID of a structural artifact, which is assumed to be
745 ** valid, this function returns one of the CFTYPE_xxx values
746 ** describing the record type, or 0 if the RID does not refer to an
747 ** artifact record (as determined by reading the event table).
748 **
749 ** Note that this function never returns CFTYPE_ATTACHMENT or
750 ** CFTYPE_CLUSTER because those are not used in the event table. Thus
751 ** it cannot be used to distinguish those artifact types from
752 ** non-artifact file content.
753 **
754 ** Potential TODO: if the rid is not found in the timeline, try to
755 ** match it to a client file and return a hypothetical new
756 ** CFTYPE_OPAQUE or CFTYPE_NONARTIFACT if a match is found.
757 */
758 int whatis_rid_type(int rid){
759 Stmt q = empty_Stmt;
760 int type = 0;
761 /* Check for entries on the timeline that reference this object */
762
+55 -13
--- src/tag.c
+++ src/tag.c
@@ -370,10 +370,15 @@
370370
** If zTag is NULL or valid for use as a tag for the `tag add` and
371371
** `tag cancel` commands, returns without side effects, else emits a
372372
** fatal error message. We reject certain prefixes to avoid that
373373
** clients cause undue grief by improperly tagging artifacts as being,
374374
** e.g., wiki pages or tickets.
375
+**
376
+** Note that we intentionally allow the "sym-" prefix, partly for
377
+** historical compatibility and partly because it can be applied
378
+** properly, whereas the other reserved name types have special
379
+** meanings for fossil and cannot be sensibly manually manipulated.
375380
*/
376381
static void tag_cmd_tagname_check(const char *zTag){
377382
if(zTag && *zTag &&
378383
(strncmp(zTag,"wiki-",5)==0
379384
|| strncmp(zTag,"tkt-",4)==0
@@ -393,11 +398,11 @@
393398
**
394399
** Add a new tag or property to an artifact referenced by
395400
** ARTIFACT-ID. For checkins, the tag will be usable instead
396401
** of a CHECK-IN in commands such as update and merge. If the
397402
** --propagate flag is present and ARTIFACT-ID refers to a
398
-** wiki page, forum post, tech-note, or check-in, the tag
403
+** wiki page, forum post, technote, or check-in, the tag
399404
** propagates to all descendants of that artifact.
400405
**
401406
** Options:
402407
** --raw Raw tag name. Ignored for
403408
** non-CHECK-IN artifacts.
@@ -440,20 +445,31 @@
440445
** Options:
441446
** --raw Raw tag name.
442447
** -t|--type TYPE One of "ci", or "e".
443448
** -n|--limit N Limit to N results.
444449
**
445
-** > fossil tag list|ls ?OPTIONS? ?CHECK-IN?
450
+** > fossil tag list|ls ?OPTIONS? ?ARTIFACT-ID?
446451
**
447
-** List all tags, or if CHECK-IN is supplied, list
448
-** all tags and their values for CHECK-IN. The tagtype option
449
-** takes one of: propagated, singleton, cancel.
452
+** List all tags or, if ARTIFACT-ID is supplied, all tags and
453
+** their values for that artifact. The tagtype option accepts
454
+** one of: propagated, singleton, cancel. For historical
455
+** scripting compatibility, the internal tag types "wiki-",
456
+** "tkt-", and "event-" (technote) are elided by default
457
+** unless the --raw or --prefix options are used.
450458
**
451459
** Options:
452
-** --raw List tags raw names of tags
453
-** --tagtype TYPE List only tags of type TYPE
460
+** --raw List raw names of tags
461
+** --tagtype TYPE List only tags of type TYPE, which must
462
+** be one of: cancel, singleton, propagated
454463
** -v|--inverse Inverse the meaning of --tagtype TYPE.
464
+** --prefix List only tags with the given prefix.
465
+** Fossil-internal prefixes include "sym-"
466
+** (branch name), "wiki-", "event-"
467
+** (technote), and "tkt-" (ticket). The
468
+** prefix is stripped from the resulting
469
+** list unless --raw is provided. Ignored if
470
+** ARTIFACT-ID is provided.
455471
**
456472
** The option --raw allows the manipulation of all types of tags
457473
** used for various internal purposes in fossil. It also shows
458474
** "cancel" tags for the "find" and "list" subcommands. You should
459475
** not use this option to make changes unless you are sure what
@@ -610,11 +626,13 @@
610626
if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
611627
Stmt q;
612628
const int fRaw = find_option("raw","",0)!=0;
613629
const char *zTagType = find_option("tagtype","t",1);
614630
const int fInverse = find_option("inverse","v",0)!=0;
631
+ const char *zTagPrefix = find_option("prefix","",1);
615632
int nTagType = fRaw ? -1 : 0;
633
+
616634
if( zTagType!=0 ){
617635
int l = strlen(zTagType);
618636
if( strncmp(zTagType,"cancel",l)==0 ){
619637
nTagType = 0;
620638
}else if( strncmp(zTagType,"singleton",l)==0 ){
@@ -624,30 +642,55 @@
624642
}else{
625643
fossil_fatal("unrecognized tag type");
626644
}
627645
}
628646
if( g.argc==3 ){
647
+ const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0;
629648
db_prepare(&q,
630649
"SELECT tagname FROM tag"
631650
" WHERE EXISTS(SELECT 1 FROM tagxref"
632651
" WHERE tagid=tag.tagid"
633652
" AND tagtype%s%d)"
634
- " ORDER BY tagname",
653
+ " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' "
654
+ " END ORDER BY tagname",
635655
zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
636
- nTagType
656
+ nTagType, zTagPrefix, zTagPrefix
637657
);
638658
while( db_step(&q)==SQLITE_ROW ){
639659
const char *zName = db_column_text(&q, 0);
640660
if( fRaw ){
641661
fossil_print("%s\n", zName);
662
+ }else if( nTagPrefix>0 ){
663
+ assert(db_column_bytes(&q,0)>=nTagPrefix);
664
+ fossil_print("%s\n", &zName[nTagPrefix]);
642665
}else if( strncmp(zName, "sym-", 4)==0 ){
643666
fossil_print("%s\n", &zName[4]);
644667
}
645668
}
646669
db_finalize(&q);
647670
}else if( g.argc==4 ){
648
- int rid = name_to_rid(g.argv[3]);
671
+ char const *zObjId = g.argv[3];
672
+ const int rid = name_to_rid(zObjId);
673
+ const int objType = whatis_rid_type(rid);
674
+ int nTagOffset = 0;
675
+
676
+ zTagPrefix = 0;
677
+ if(objType<=0){
678
+ fossil_fatal("Cannot resolve artifact ID: %s", zObjId);
679
+ }else if(fRaw==0){
680
+ /* Figure out the tag prefix to strip */
681
+ switch(objType){
682
+ case CFTYPE_MANIFEST: zTagPrefix = "sym-"; break;
683
+ case CFTYPE_WIKI: zTagPrefix = "wiki-"; break;
684
+ case CFTYPE_TICKET: zTagPrefix = "tkt-"; break;
685
+ case CFTYPE_EVENT: zTagPrefix = "event-"; break;
686
+ default: break;
687
+ }
688
+ if(zTagPrefix!=0){
689
+ nTagOffset = (int)strlen(zTagPrefix);
690
+ }
691
+ }
649692
db_prepare(&q,
650693
"SELECT tagname, value FROM tagxref, tag"
651694
" WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
652695
" AND tagtype%s%d"
653696
" ORDER BY tagname",
@@ -656,13 +699,12 @@
656699
nTagType
657700
);
658701
while( db_step(&q)==SQLITE_ROW ){
659702
const char *zName = db_column_text(&q, 0);
660703
const char *zValue = db_column_text(&q, 1);
661
- if( fRaw==0 ){
662
- if( strncmp(zName, "sym-", 4)!=0 ) continue;
663
- zName += 4;
704
+ if( zTagPrefix && strncmp(zName, zTagPrefix, nTagOffset)==0 ){
705
+ zName += nTagOffset;
664706
}
665707
if( zValue && zValue[0] ){
666708
fossil_print("%s=%s\n", zName, zValue);
667709
}else{
668710
fossil_print("%s\n", zName);
669711
--- src/tag.c
+++ src/tag.c
@@ -370,10 +370,15 @@
370 ** If zTag is NULL or valid for use as a tag for the `tag add` and
371 ** `tag cancel` commands, returns without side effects, else emits a
372 ** fatal error message. We reject certain prefixes to avoid that
373 ** clients cause undue grief by improperly tagging artifacts as being,
374 ** e.g., wiki pages or tickets.
 
 
 
 
 
375 */
376 static void tag_cmd_tagname_check(const char *zTag){
377 if(zTag && *zTag &&
378 (strncmp(zTag,"wiki-",5)==0
379 || strncmp(zTag,"tkt-",4)==0
@@ -393,11 +398,11 @@
393 **
394 ** Add a new tag or property to an artifact referenced by
395 ** ARTIFACT-ID. For checkins, the tag will be usable instead
396 ** of a CHECK-IN in commands such as update and merge. If the
397 ** --propagate flag is present and ARTIFACT-ID refers to a
398 ** wiki page, forum post, tech-note, or check-in, the tag
399 ** propagates to all descendants of that artifact.
400 **
401 ** Options:
402 ** --raw Raw tag name. Ignored for
403 ** non-CHECK-IN artifacts.
@@ -440,20 +445,31 @@
440 ** Options:
441 ** --raw Raw tag name.
442 ** -t|--type TYPE One of "ci", or "e".
443 ** -n|--limit N Limit to N results.
444 **
445 ** > fossil tag list|ls ?OPTIONS? ?CHECK-IN?
446 **
447 ** List all tags, or if CHECK-IN is supplied, list
448 ** all tags and their values for CHECK-IN. The tagtype option
449 ** takes one of: propagated, singleton, cancel.
 
 
 
450 **
451 ** Options:
452 ** --raw List tags raw names of tags
453 ** --tagtype TYPE List only tags of type TYPE
 
454 ** -v|--inverse Inverse the meaning of --tagtype TYPE.
 
 
 
 
 
 
 
455 **
456 ** The option --raw allows the manipulation of all types of tags
457 ** used for various internal purposes in fossil. It also shows
458 ** "cancel" tags for the "find" and "list" subcommands. You should
459 ** not use this option to make changes unless you are sure what
@@ -610,11 +626,13 @@
610 if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
611 Stmt q;
612 const int fRaw = find_option("raw","",0)!=0;
613 const char *zTagType = find_option("tagtype","t",1);
614 const int fInverse = find_option("inverse","v",0)!=0;
 
615 int nTagType = fRaw ? -1 : 0;
 
616 if( zTagType!=0 ){
617 int l = strlen(zTagType);
618 if( strncmp(zTagType,"cancel",l)==0 ){
619 nTagType = 0;
620 }else if( strncmp(zTagType,"singleton",l)==0 ){
@@ -624,30 +642,55 @@
624 }else{
625 fossil_fatal("unrecognized tag type");
626 }
627 }
628 if( g.argc==3 ){
 
629 db_prepare(&q,
630 "SELECT tagname FROM tag"
631 " WHERE EXISTS(SELECT 1 FROM tagxref"
632 " WHERE tagid=tag.tagid"
633 " AND tagtype%s%d)"
634 " ORDER BY tagname",
 
635 zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
636 nTagType
637 );
638 while( db_step(&q)==SQLITE_ROW ){
639 const char *zName = db_column_text(&q, 0);
640 if( fRaw ){
641 fossil_print("%s\n", zName);
 
 
 
642 }else if( strncmp(zName, "sym-", 4)==0 ){
643 fossil_print("%s\n", &zName[4]);
644 }
645 }
646 db_finalize(&q);
647 }else if( g.argc==4 ){
648 int rid = name_to_rid(g.argv[3]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649 db_prepare(&q,
650 "SELECT tagname, value FROM tagxref, tag"
651 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
652 " AND tagtype%s%d"
653 " ORDER BY tagname",
@@ -656,13 +699,12 @@
656 nTagType
657 );
658 while( db_step(&q)==SQLITE_ROW ){
659 const char *zName = db_column_text(&q, 0);
660 const char *zValue = db_column_text(&q, 1);
661 if( fRaw==0 ){
662 if( strncmp(zName, "sym-", 4)!=0 ) continue;
663 zName += 4;
664 }
665 if( zValue && zValue[0] ){
666 fossil_print("%s=%s\n", zName, zValue);
667 }else{
668 fossil_print("%s\n", zName);
669
--- src/tag.c
+++ src/tag.c
@@ -370,10 +370,15 @@
370 ** If zTag is NULL or valid for use as a tag for the `tag add` and
371 ** `tag cancel` commands, returns without side effects, else emits a
372 ** fatal error message. We reject certain prefixes to avoid that
373 ** clients cause undue grief by improperly tagging artifacts as being,
374 ** e.g., wiki pages or tickets.
375 **
376 ** Note that we intentionally allow the "sym-" prefix, partly for
377 ** historical compatibility and partly because it can be applied
378 ** properly, whereas the other reserved name types have special
379 ** meanings for fossil and cannot be sensibly manually manipulated.
380 */
381 static void tag_cmd_tagname_check(const char *zTag){
382 if(zTag && *zTag &&
383 (strncmp(zTag,"wiki-",5)==0
384 || strncmp(zTag,"tkt-",4)==0
@@ -393,11 +398,11 @@
398 **
399 ** Add a new tag or property to an artifact referenced by
400 ** ARTIFACT-ID. For checkins, the tag will be usable instead
401 ** of a CHECK-IN in commands such as update and merge. If the
402 ** --propagate flag is present and ARTIFACT-ID refers to a
403 ** wiki page, forum post, technote, or check-in, the tag
404 ** propagates to all descendants of that artifact.
405 **
406 ** Options:
407 ** --raw Raw tag name. Ignored for
408 ** non-CHECK-IN artifacts.
@@ -440,20 +445,31 @@
445 ** Options:
446 ** --raw Raw tag name.
447 ** -t|--type TYPE One of "ci", or "e".
448 ** -n|--limit N Limit to N results.
449 **
450 ** > fossil tag list|ls ?OPTIONS? ?ARTIFACT-ID?
451 **
452 ** List all tags or, if ARTIFACT-ID is supplied, all tags and
453 ** their values for that artifact. The tagtype option accepts
454 ** one of: propagated, singleton, cancel. For historical
455 ** scripting compatibility, the internal tag types "wiki-",
456 ** "tkt-", and "event-" (technote) are elided by default
457 ** unless the --raw or --prefix options are used.
458 **
459 ** Options:
460 ** --raw List raw names of tags
461 ** --tagtype TYPE List only tags of type TYPE, which must
462 ** be one of: cancel, singleton, propagated
463 ** -v|--inverse Inverse the meaning of --tagtype TYPE.
464 ** --prefix List only tags with the given prefix.
465 ** Fossil-internal prefixes include "sym-"
466 ** (branch name), "wiki-", "event-"
467 ** (technote), and "tkt-" (ticket). The
468 ** prefix is stripped from the resulting
469 ** list unless --raw is provided. Ignored if
470 ** ARTIFACT-ID is provided.
471 **
472 ** The option --raw allows the manipulation of all types of tags
473 ** used for various internal purposes in fossil. It also shows
474 ** "cancel" tags for the "find" and "list" subcommands. You should
475 ** not use this option to make changes unless you are sure what
@@ -610,11 +626,13 @@
626 if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
627 Stmt q;
628 const int fRaw = find_option("raw","",0)!=0;
629 const char *zTagType = find_option("tagtype","t",1);
630 const int fInverse = find_option("inverse","v",0)!=0;
631 const char *zTagPrefix = find_option("prefix","",1);
632 int nTagType = fRaw ? -1 : 0;
633
634 if( zTagType!=0 ){
635 int l = strlen(zTagType);
636 if( strncmp(zTagType,"cancel",l)==0 ){
637 nTagType = 0;
638 }else if( strncmp(zTagType,"singleton",l)==0 ){
@@ -624,30 +642,55 @@
642 }else{
643 fossil_fatal("unrecognized tag type");
644 }
645 }
646 if( g.argc==3 ){
647 const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0;
648 db_prepare(&q,
649 "SELECT tagname FROM tag"
650 " WHERE EXISTS(SELECT 1 FROM tagxref"
651 " WHERE tagid=tag.tagid"
652 " AND tagtype%s%d)"
653 " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' "
654 " END ORDER BY tagname",
655 zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
656 nTagType, zTagPrefix, zTagPrefix
657 );
658 while( db_step(&q)==SQLITE_ROW ){
659 const char *zName = db_column_text(&q, 0);
660 if( fRaw ){
661 fossil_print("%s\n", zName);
662 }else if( nTagPrefix>0 ){
663 assert(db_column_bytes(&q,0)>=nTagPrefix);
664 fossil_print("%s\n", &zName[nTagPrefix]);
665 }else if( strncmp(zName, "sym-", 4)==0 ){
666 fossil_print("%s\n", &zName[4]);
667 }
668 }
669 db_finalize(&q);
670 }else if( g.argc==4 ){
671 char const *zObjId = g.argv[3];
672 const int rid = name_to_rid(zObjId);
673 const int objType = whatis_rid_type(rid);
674 int nTagOffset = 0;
675
676 zTagPrefix = 0;
677 if(objType<=0){
678 fossil_fatal("Cannot resolve artifact ID: %s", zObjId);
679 }else if(fRaw==0){
680 /* Figure out the tag prefix to strip */
681 switch(objType){
682 case CFTYPE_MANIFEST: zTagPrefix = "sym-"; break;
683 case CFTYPE_WIKI: zTagPrefix = "wiki-"; break;
684 case CFTYPE_TICKET: zTagPrefix = "tkt-"; break;
685 case CFTYPE_EVENT: zTagPrefix = "event-"; break;
686 default: break;
687 }
688 if(zTagPrefix!=0){
689 nTagOffset = (int)strlen(zTagPrefix);
690 }
691 }
692 db_prepare(&q,
693 "SELECT tagname, value FROM tagxref, tag"
694 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
695 " AND tagtype%s%d"
696 " ORDER BY tagname",
@@ -656,13 +699,12 @@
699 nTagType
700 );
701 while( db_step(&q)==SQLITE_ROW ){
702 const char *zName = db_column_text(&q, 0);
703 const char *zValue = db_column_text(&q, 1);
704 if( zTagPrefix && strncmp(zName, zTagPrefix, nTagOffset)==0 ){
705 zName += nTagOffset;
 
706 }
707 if( zValue && zValue[0] ){
708 fossil_print("%s=%s\n", zName, zValue);
709 }else{
710 fossil_print("%s\n", zName);
711
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
1414
to be inside the working directory without requiring the --force flag.
1515
* The [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/wikinew|/wikinew]
1616
pages now default to markdown format.
1717
* The [/help?cmd=/login|/login] page now links to a user's forum post
1818
timeline if the repository has forum posts.
19
+ * Tags may now be propagated for forum posts, wiki pages, and technotes.
20
+ The [/help?cmd=tag|tag command] can now manipulate and list such tags.
1921
2022
<a name='v2_15'></a>
2123
<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2>
2224
* <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
2325
the patch is recommended.</b><p>
2426
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
14 to be inside the working directory without requiring the --force flag.
15 * The [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/wikinew|/wikinew]
16 pages now default to markdown format.
17 * The [/help?cmd=/login|/login] page now links to a user's forum post
18 timeline if the repository has forum posts.
 
 
19
20 <a name='v2_15'></a>
21 <h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2>
22 * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
23 the patch is recommended.</b><p>
24
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
14 to be inside the working directory without requiring the --force flag.
15 * The [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/wikinew|/wikinew]
16 pages now default to markdown format.
17 * The [/help?cmd=/login|/login] page now links to a user's forum post
18 timeline if the repository has forum posts.
19 * Tags may now be propagated for forum posts, wiki pages, and technotes.
20 The [/help?cmd=tag|tag command] can now manipulate and list such tags.
21
22 <a name='v2_15'></a>
23 <h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2>
24 * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
25 the patch is recommended.</b><p>
26

Keyboard Shortcuts

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