| | @@ -384,10 +384,30 @@ |
| 384 | 384 | || strncmp(zTag,"tkt-",4)==0 |
| 385 | 385 | || strncmp(zTag,"event-",6)==0)){ |
| 386 | 386 | fossil_fatal("Invalid prefix for tag name: %s", zTag); |
| 387 | 387 | } |
| 388 | 388 | } |
| 389 | + |
| 390 | +/* |
| 391 | +** Internal helper for the tag command. Fetches rid_root_parent(rid) |
| 392 | +** and if it differs from the input, assigns *zSym to the root object's |
| 393 | +** UUID (intentionally leaking it). |
| 394 | +*/ |
| 395 | +static int tag_cmd_root(int rid, char const **zSym, int bVerbose){ |
| 396 | + const int origRid = rid; |
| 397 | + rid = rid_root_parent(rid); |
| 398 | + assert( rid>0 ); |
| 399 | + if( origRid!=rid ){ |
| 400 | + char const *zOrig = *zSym; |
| 401 | + *zSym = rid_to_uuid(rid)/*intentional leak*/; |
| 402 | + if( bVerbose ){ |
| 403 | + fossil_print("Redirecting tag from %!S to root ancestor %!S.\n", |
| 404 | + zOrig, *zSym); |
| 405 | + } |
| 406 | + } |
| 407 | + return rid; |
| 408 | +} |
| 389 | 409 | |
| 390 | 410 | /* |
| 391 | 411 | ** COMMAND: tag |
| 392 | 412 | ** |
| 393 | 413 | ** Usage: %fossil tag SUBCOMMAND ... |
| | @@ -409,19 +429,30 @@ |
| 409 | 429 | ** actually insert it into the database |
| 410 | 430 | ** --propagate Propagating tag |
| 411 | 431 | ** --raw Raw tag name. Ignored for |
| 412 | 432 | ** non-CHECK-IN artifacts. |
| 413 | 433 | ** --user-override USER Name USER when adding the tag |
| 434 | +** --root If ARTIFACT-ID refers to an artifact |
| 435 | +** with a P-card, it gets translated to the |
| 436 | +** oldest parent of that artifact and |
| 437 | +** --propagate is assumed. This is |
| 438 | +** most useful with forum posts, wiki |
| 439 | +** pages, and tech notes, where tags are |
| 440 | +** most easliy managed via their initial |
| 441 | +** version. |
| 414 | 442 | ** |
| 415 | 443 | ** The --date-override and --user-override options support |
| 416 | 444 | ** importing history from other SCM systems. DATETIME has |
| 417 | 445 | ** the form 'YYYY-MM-DD HH:MM:SS'. |
| 418 | 446 | ** |
| 419 | 447 | ** Note that fossil uses some tag prefixes internally and this |
| 420 | 448 | ** command will reject tags with these prefixes to avoid |
| 421 | 449 | ** causing problems or confusion: "wiki-", "tkt-", "event-". |
| 422 | 450 | ** |
| 451 | +** When tagging forum posts, the ARTIFACT-ID is translated to |
| 452 | +** the initial version of that post and --propagate is implied. |
| 453 | +** |
| 423 | 454 | ** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID |
| 424 | 455 | ** |
| 425 | 456 | ** Remove the tag TAGNAME from the artifact referenced by |
| 426 | 457 | ** ARTIFACT-ID, and also remove the propagation of the tag to |
| 427 | 458 | ** any descendants. Use the -n|--dry-run option to see |
| | @@ -433,10 +464,11 @@ |
| 433 | 464 | ** -n|--dry-run Display the control artifact, but do |
| 434 | 465 | ** not insert it into the database |
| 435 | 466 | ** --raw Raw tag name. Ignored for |
| 436 | 467 | ** non-CHECK-IN artifacts. |
| 437 | 468 | ** --user-override USER Name USER when deleting the tag |
| 469 | +** --root As described for 'add'. |
| 438 | 470 | ** |
| 439 | 471 | ** > fossil tag find ?OPTIONS? TAGNAME |
| 440 | 472 | ** |
| 441 | 473 | ** List all objects that use TAGNAME. |
| 442 | 474 | ** |
| | @@ -452,12 +484,11 @@ |
| 452 | 484 | ** if --raw is used. |
| 453 | 485 | ** |
| 454 | 486 | ** > fossil tag list|ls ?OPTIONS? ?ARTIFACT-ID? |
| 455 | 487 | ** |
| 456 | 488 | ** List all tags or, if ARTIFACT-ID is supplied, all tags and |
| 457 | | -** their values for that artifact. The tagtype option accepts |
| 458 | | -** one of: propagated, singleton, cancel. For historical |
| 489 | +** their values for that artifact. For historical |
| 459 | 490 | ** scripting compatibility, the internal tag types "wiki-", |
| 460 | 491 | ** "tkt-", and "event-" (technote) are elided by default |
| 461 | 492 | ** unless the --raw or --prefix options are used. |
| 462 | 493 | ** |
| 463 | 494 | ** Options: |
| | @@ -475,10 +506,12 @@ |
| 475 | 506 | ** be one of: cancel, singleton, propagated |
| 476 | 507 | ** --values List tag values |
| 477 | 508 | ** If --sep is supplied, list all values of a tag on |
| 478 | 509 | ** the same line, separated by SEP; otherwise list |
| 479 | 510 | ** each value on its own line. |
| 511 | +** --root As described for 'add' and only if ARTIFACT-ID is |
| 512 | +** provided. |
| 480 | 513 | ** |
| 481 | 514 | ** The option --raw allows the manipulation of all types of tags |
| 482 | 515 | ** used for various internal purposes in fossil. It also shows |
| 483 | 516 | ** "cancel" tags for the "find" and "list" subcommands. You should |
| 484 | 517 | ** not use this option to make changes unless you are sure what |
| | @@ -512,34 +545,38 @@ |
| 512 | 545 | |
| 513 | 546 | if( strncmp(g.argv[2],"add",n)==0 ){ |
| 514 | 547 | char *zValue; |
| 515 | 548 | int dryRun = 0; |
| 516 | 549 | int fRaw = find_option("raw","",0)!=0; |
| 517 | | - const char *zPrefix = ""; |
| 518 | 550 | int fPropagate = find_option("propagate","",0)!=0; |
| 551 | + int fRoot = find_option("root","",0)!=0; |
| 552 | + int objType; |
| 553 | + int rid; |
| 554 | + const char *zPrefix = ""; |
| 519 | 555 | const char *zDateOvrd = find_option("date-override",0,1); |
| 520 | 556 | const char *zUserOvrd = find_option("user-override",0,1); |
| 521 | 557 | const char *zTag; |
| 522 | 558 | const char *zObjId; |
| 523 | | - int objType; |
| 524 | 559 | if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; |
| 525 | 560 | if( g.argc!=5 && g.argc!=6 ){ |
| 526 | 561 | usage("add ?options? TAGNAME ARTIFACT-ID ?VALUE?"); |
| 527 | 562 | } |
| 528 | 563 | zTag = g.argv[3]; |
| 529 | 564 | tag_cmd_tagname_check(zTag); |
| 530 | 565 | zObjId = g.argv[4]; |
| 531 | 566 | zValue = g.argc==6 ? g.argv[5] : 0; |
| 532 | | - objType = whatis_rid_type(symbolic_name_to_rid(zObjId, 0)); |
| 533 | | - switch(objType){ |
| 534 | | - case 0: |
| 535 | | - fossil_fatal("Cannot resolve artifact ID: %s", zObjId); |
| 536 | | - break; |
| 537 | | - case CFTYPE_MANIFEST: |
| 538 | | - zPrefix = fRaw ? "" : "sym-"; |
| 539 | | - break; |
| 540 | | - default: break; |
| 567 | + rid = symbolic_name_to_rid(zObjId, 0); |
| 568 | + if( rid<=0 ){ |
| 569 | + fossil_fatal("Cannot resolve artifact ID: %s", zObjId); |
| 570 | + } |
| 571 | + if( fRoot ){ |
| 572 | + rid = tag_cmd_root(rid, &zObjId, dryRun); |
| 573 | + fPropagate = 1; |
| 574 | + } |
| 575 | + objType = whatis_rid_type(rid); |
| 576 | + if( CFTYPE_MANIFEST==objType ){ |
| 577 | + zPrefix = fRaw ? "" : "sym-"; |
| 541 | 578 | } |
| 542 | 579 | db_begin_transaction(); |
| 543 | 580 | tag_add_artifact(zPrefix, zTag, zObjId, zValue, |
| 544 | 581 | 1+fPropagate+dryRun,zDateOvrd,zUserOvrd); |
| 545 | 582 | db_end_transaction(0); |
| | @@ -551,32 +588,35 @@ |
| 551 | 588 | }else |
| 552 | 589 | |
| 553 | 590 | if( strncmp(g.argv[2],"cancel",n)==0 ){ |
| 554 | 591 | int dryRun = 0; |
| 555 | 592 | int fRaw = find_option("raw","",0)!=0; |
| 593 | + int fRoot = find_option("root","",0)!=0; |
| 594 | + int objType; |
| 595 | + int rid; |
| 556 | 596 | const char *zPrefix = ""; |
| 557 | 597 | const char *zDateOvrd = find_option("date-override",0,1); |
| 558 | 598 | const char *zUserOvrd = find_option("user-override",0,1); |
| 559 | 599 | const char *zTag; |
| 560 | 600 | const char *zObjId; |
| 561 | | - int objType; |
| 562 | 601 | if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; |
| 563 | 602 | if( g.argc!=5 ){ |
| 564 | 603 | usage("cancel ?options? TAGNAME ARTIFACT-ID"); |
| 565 | 604 | } |
| 566 | 605 | zTag = g.argv[3]; |
| 567 | 606 | tag_cmd_tagname_check(zTag); |
| 568 | 607 | zObjId = g.argv[4]; |
| 569 | | - objType = whatis_rid_type(symbolic_name_to_rid(zObjId, 0)); |
| 570 | | - switch(objType){ |
| 571 | | - case 0: |
| 572 | | - fossil_fatal("Cannot resolve artifact ID: %s", zObjId); |
| 573 | | - break; |
| 574 | | - case CFTYPE_MANIFEST: |
| 575 | | - zPrefix = fRaw ? "" : "sym-"; |
| 576 | | - break; |
| 577 | | - default: break; |
| 608 | + rid = symbolic_name_to_rid(zObjId, 0); |
| 609 | + if( rid<=0 ){ |
| 610 | + fossil_fatal("Cannot resolve artifact ID: %s", zObjId); |
| 611 | + } |
| 612 | + if( fRoot ){ |
| 613 | + rid = tag_cmd_root(rid, &zObjId, dryRun); |
| 614 | + } |
| 615 | + objType = whatis_rid_type(rid); |
| 616 | + if( CFTYPE_MANIFEST==objType ){ |
| 617 | + zPrefix = fRaw ? "" : "sym-"; |
| 578 | 618 | } |
| 579 | 619 | db_begin_transaction(); |
| 580 | 620 | tag_add_artifact(zPrefix, zTag, zObjId, 0, dryRun, |
| 581 | 621 | zDateOvrd, zUserOvrd); |
| 582 | 622 | db_end_transaction(0); |
| | @@ -639,10 +679,11 @@ |
| 639 | 679 | const char *zTagType = find_option("tagtype","t",1); |
| 640 | 680 | const int fInverse = find_option("inverse","v",0)!=0; |
| 641 | 681 | const char *zTagPrefix = find_option("prefix","",1); |
| 642 | 682 | int nTagType = fRaw ? -1 : 0; |
| 643 | 683 | int fValues = find_option("values","",0)!=0; |
| 684 | + int fRoot = find_option("root","",0)!=0; |
| 644 | 685 | const char *zSep = find_option("sep","",1); |
| 645 | 686 | |
| 646 | 687 | |
| 647 | 688 | if( zTagType!=0 ){ |
| 648 | 689 | int l = strlen(zTagType); |
| | @@ -712,15 +753,19 @@ |
| 712 | 753 | } |
| 713 | 754 | } |
| 714 | 755 | db_finalize(&q); |
| 715 | 756 | }else if( g.argc==4 ){ |
| 716 | 757 | char const *zObjId = g.argv[3]; |
| 717 | | - const int rid = name_to_rid(zObjId); |
| 718 | | - const int objType = whatis_rid_type(rid); |
| 758 | + int rid = name_to_rid(zObjId); |
| 759 | + int objType; |
| 719 | 760 | int nTagOffset = 0; |
| 720 | 761 | |
| 762 | + if( fRoot ){ |
| 763 | + rid = tag_cmd_root(rid, &zObjId, 0); |
| 764 | + } |
| 721 | 765 | zTagPrefix = 0; |
| 766 | + objType = rid>=0 ? whatis_rid_type(rid) : 0; |
| 722 | 767 | if(objType<=0){ |
| 723 | 768 | fossil_fatal("Cannot resolve artifact ID: %s", zObjId); |
| 724 | 769 | }else if(fRaw==0){ |
| 725 | 770 | /* Figure out the tag prefix to strip */ |
| 726 | 771 | switch(objType){ |
| 727 | 772 | |