Fossil SCM

Add new branch subcommands: close, reopen, hide, unhide.

stephan 2021-07-25 02:59 trunk merge
Commit f1fb1239be0b0a10abfa6647285e0c968a171b980b08e07d78317617f44ebd22
+246 -7
--- src/branch.c
+++ src/branch.c
@@ -344,22 +344,241 @@
344344
" AND ox.rid=ix.rid)",
345345
TAG_BRANCH, zBrName, TAG_CLOSED
346346
);
347347
}
348348
349
+/*
350
+** Internal helper for branch_cmd_close() and friends. Adds a row to
351
+** the to the brcmdtag TEMP table, initializing that table if needed,
352
+** holding a pending tag for the given blob.rid (which is assumed to
353
+** be valid). zTag must be a fully-formed tag name, including the
354
+** (+,-,*) prefix character.
355
+**
356
+*/
357
+static void branch_cmd_tag_add(int rid, const char *zTag){
358
+ static int once = 0;
359
+ assert(zTag && ('+'==zTag[0] || '-'==zTag[0] || '*'==zTag[0]));
360
+ if(0==once++){
361
+ db_multi_exec("CREATE TEMP TABLE brcmdtag("
362
+ "rid INTEGER UNIQUE ON CONFLICT IGNORE,"
363
+ "tag TEXT NOT NULL"
364
+ ")");
365
+ }
366
+ db_multi_exec("INSERT INTO brcmdtag(rid,tag) VALUES(%d,%Q)",
367
+ rid, zTag);
368
+}
369
+
370
+/*
371
+** Internal helper for branch_cmd_close() and friends. Creates and
372
+** saves a control artifact of tag changes stored via
373
+** branch_cmd_tag_add(). Fails fatally on error, returns 0 if it saves
374
+** an artifact, and a negative value if it does not save anything
375
+** because no tags were queued up. A positive return value is reserved
376
+** for potential future semantics.
377
+**
378
+** This function asserts that a transaction is underway and it ends
379
+** the transaction, committing or rolling back, as appropriate.
380
+*/
381
+static int branch_cmd_tag_finalize(int fDryRun /* roll back if true */,
382
+ int fVerbose /* output extra info */,
383
+ const char *zDateOvrd /* --date-override */,
384
+ const char *zUserOvrd /* --user-override */){
385
+ int nTags = 0;
386
+ Stmt q = empty_Stmt;
387
+ Blob manifest = empty_blob;
388
+ int doRollback = fDryRun!=0;
389
+
390
+ assert(db_transaction_nesting_depth() > 0);
391
+ if(!db_table_exists("temp","brcmdtag")){
392
+ fossil_warning("No tags added - nothing to do.");
393
+ db_end_transaction(1);
394
+ return -1;
395
+ }
396
+ db_prepare(&q, "SELECT b.uuid, t.tag "
397
+ "FROM blob b, brcmdtag t "
398
+ "WHERE b.rid=t.rid "
399
+ "ORDER BY t.tag, b.uuid");
400
+ blob_appendf(&manifest, "D %z\n",
401
+ date_in_standard_format( zDateOvrd ? zDateOvrd : "now"));
402
+ while(SQLITE_ROW==db_step(&q)){
403
+ const char * zHash = db_column_text(&q, 0);
404
+ const char * zTag = db_column_text(&q, 1);
405
+ blob_appendf(&manifest, "T %s %s\n", zTag, zHash);
406
+ ++nTags;
407
+ }
408
+ if(!nTags){
409
+ fossil_warning("No tags added - nothing to do.");
410
+ db_end_transaction(1);
411
+ blob_reset(&manifest);
412
+ return -1;
413
+ }
414
+ user_select();
415
+ blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name());
416
+ { /* Z-card and save artifact */
417
+ int newRid;
418
+ Blob cksum = empty_blob;
419
+ md5sum_blob(&manifest, &cksum);
420
+ blob_appendf(&manifest, "Z %b\n", &cksum);
421
+ blob_reset(&cksum);
422
+ if(fDryRun && fVerbose){
423
+ fossil_print("Dry-run mode: will roll back new artifact:\n%b",
424
+ &manifest);
425
+ /* Run through the saving steps, though, noting that doing so
426
+ ** will clear out &manifest, which is why we output it here
427
+ ** instead of after saving. */
428
+ }
429
+ newRid = content_put(&manifest);
430
+ if(0==newRid){
431
+ fossil_fatal("Problem saving new artifact: %s\n%b",
432
+ g.zErrMsg, &manifest);
433
+ }else if(manifest_crosslink(newRid, &manifest, 0)==0){
434
+ fossil_fatal("Crosslinking error: %s", g.zErrMsg);
435
+ }
436
+ fossil_print("Saved new control artifact %z (RID %d).\n",
437
+ rid_to_uuid(newRid), newRid);
438
+ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", newRid);
439
+ if(fDryRun){
440
+ fossil_print("Dry-run mode: rolling back new artifact.\n");
441
+ assert(0!=doRollback);
442
+ }
443
+ }
444
+ db_multi_exec("DROP TABLE brcmdtag");
445
+ blob_reset(&manifest);
446
+ db_end_transaction(doRollback);
447
+ return 0;
448
+}
449
+
450
+/*
451
+** Internal helper for branch_cmd_close() and friends. zName is a
452
+** symbolic checkin name. Returns the blob.rid of the checkin or fails
453
+** fatally if the name does not resolve unambiguously. If zUuid is
454
+** not NULL, *zUuid is set to the resolved blob.uuid and must be freed
455
+** by the caller via fossil_free().
456
+*/
457
+static int branch_resolve_name(char const *zName, char **zUuid){
458
+ const int rid = name_to_uuid2(zName, "ci", zUuid);
459
+ if(0==rid){
460
+ fossil_fatal("Cannot resolve name: %s", zName);
461
+ }else if(rid<0){
462
+ fossil_fatal("Ambiguous name: %s", zName);
463
+ }
464
+ return rid;
465
+}
466
+
467
+/*
468
+** Implementation of (branch hide/unhide) subcommands. nStartAtArg is
469
+** the g.argv index to start reading branch/checkin names. fHide is
470
+** true for hiding, false for unhiding. Fails fatally on error.
471
+*/
472
+static void branch_cmd_hide(int nStartAtArg, int fHide){
473
+ int argPos = nStartAtArg; /* g.argv pos with first branch/ci name */
474
+ char * zUuid = 0; /* Resolved branch UUID. */
475
+ const int fVerbose = find_option("verbose","v",0)!=0;
476
+ const int fDryRun = find_option("dry-run","n",0)!=0;
477
+ const char *zDateOvrd = find_option("date-override",0,1);
478
+ const char *zUserOvrd = find_option("user-override",0,1);
479
+
480
+ verify_all_options();
481
+ db_begin_transaction();
482
+ for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
483
+ const char * zName = g.argv[argPos];
484
+ const int rid = branch_resolve_name(zName, &zUuid);
485
+ const int isHidden = rid_has_tag(rid, TAG_HIDDEN);
486
+ /* Potential TODO: check for existing 'hidden' flag and skip this
487
+ ** entry if it already has (if fHide) or does not have (if !fHide)
488
+ ** that tag. FWIW, /ci_edit does not do so. */
489
+ if(fHide && isHidden){
490
+ fossil_warning("Skipping hidden checkin %s: %s.", zName, zUuid);
491
+ continue;
492
+ }else if(!fHide && !isHidden){
493
+ fossil_warning("Skipping non-hidden checkin %s: %s.", zName, zUuid);
494
+ continue;
495
+ }
496
+ branch_cmd_tag_add(rid, fHide ? "*hidden" : "-hidden");
497
+ if(fVerbose!=0){
498
+ fossil_print("%s checkin [%s] %s\n",
499
+ fHide ? "Hiding" : "Unhiding",
500
+ zName, zUuid);
501
+ }
502
+ }
503
+ branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
504
+}
505
+
506
+/*
507
+** Implementation of (branch close|reopen) subcommands. nStartAtArg is
508
+** the g.argv index to start reading branch/checkin names. The given
509
+** checkins are closed if fClose is true, else their "closed" tag (if
510
+** any) is cancelled. Fails fatally on error.
511
+*/
512
+static void branch_cmd_close(int nStartAtArg, int fClose){
513
+ int argPos = nStartAtArg; /* g.argv pos with first branch name */
514
+ char * zUuid = 0; /* Resolved branch UUID. */
515
+ const int fVerbose = find_option("verbose","v",0)!=0;
516
+ const int fDryRun = find_option("dry-run","n",0)!=0;
517
+ const char *zDateOvrd = find_option("date-override",0,1);
518
+ const char *zUserOvrd = find_option("user-override",0,1);
519
+
520
+ verify_all_options();
521
+ db_begin_transaction();
522
+ for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
523
+ const char * zName = g.argv[argPos];
524
+ const int rid = branch_resolve_name(zName, &zUuid);
525
+ const int isClosed = leaf_is_closed(rid);
526
+ if(!is_a_leaf(rid)){
527
+ /* This behaviour is different from /ci_edit closing, where
528
+ ** is_a_leaf() adds a "+" tag and !is_a_leaf() adds a "*"
529
+ ** tag. We might want to change this to match for consistency's
530
+ ** sake, but it currently seems unnecessary to close/re-open a
531
+ ** non-leaf. */
532
+ fossil_warning("Skipping non-leaf [%s] %s", zName, zUuid);
533
+ continue;
534
+ }else if(fClose && isClosed){
535
+ fossil_warning("Skipping closed leaf [%s] %s", zName, zUuid);
536
+ continue;
537
+ }else if(!fClose && !isClosed){
538
+ fossil_warning("Skipping non-closed leaf [%s] %s", zName, zUuid);
539
+ continue;
540
+ }
541
+ branch_cmd_tag_add(rid, fClose ? "+closed" : "-closed");
542
+ if(fVerbose!=0){
543
+ fossil_print("%s branch [%s] %s\n",
544
+ fClose ? "Closing" : "Re-opening",
545
+ zName, zUuid);
546
+ }
547
+ }
548
+ branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
549
+}
349550
350551
/*
351552
** COMMAND: branch
352553
**
353554
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
354555
**
355556
** Run various subcommands to manage branches of the open repository or
356557
** of the repository identified by the -R or --repository option.
357558
**
559
+** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
560
+**
561
+** Adds or cancels the "closed" tag to one or more branches.
562
+** It accepts arbitrary unambiguous symbolic names but
563
+** will only resolve checkin names and skips any which resolve
564
+** to non-leaf checkins. Options:
565
+** -n|--dry-run do not commit changes and dump artifact
566
+** to stdout
567
+** -v|--verbose output more information
568
+** --date-override DATE DATE to use instead of 'now'
569
+** --user-override USER USER to use instead of the current default
570
+**
358571
** > fossil branch current
359572
**
360573
** Print the name of the branch for the current check-out
574
+**
575
+** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
576
+**
577
+** Adds or cancels the "hidden" tag for the specified branches or
578
+** or checkin IDs. Accepts the same options as the close
579
+** subcommand.
361580
**
362581
** > fossil branch info BRANCH-NAME
363582
**
364583
** Print information about a branch
365584
**
@@ -377,17 +596,17 @@
377596
**
378597
** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
379598
**
380599
** Create a new branch BRANCH-NAME off of check-in BASIS.
381600
** Supported options for this subcommand include:
382
-** --private branch is private (i.e., remains local)
383
-** --bgcolor COLOR use COLOR instead of automatic background
601
+** --private branch is private (i.e., remains local)
602
+** --bgcolor COLOR use COLOR instead of automatic background
384603
** ("auto" lets Fossil choose it automatically,
385
-** even for private branches)
386
-** --nosign do not sign contents on this branch
387
-** --date-override DATE DATE to use instead of 'now'
388
-** --user-override USER USER to use instead of the current default
604
+** even for private branches)
605
+** --nosign do not sign contents on this branch
606
+** --date-override DATE DATE to use instead of 'now'
607
+** --user-override USER USER to use instead of the current default
389608
**
390609
** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
391610
** year-month-day form, it may be truncated, the "T" may be
392611
** replaced by a space, and it may also name a timezone offset
393612
** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
@@ -452,13 +671,33 @@
452671
fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
453672
}
454673
db_finalize(&q);
455674
}else if( strncmp(zCmd,"new",n)==0 ){
456675
branch_new();
676
+ }else if( strncmp(zCmd,"close",5)==0 ){
677
+ if(g.argc<4){
678
+ usage("branch close branch-name(s)...");
679
+ }
680
+ branch_cmd_close(3, 1);
681
+ }else if( strncmp(zCmd,"reopen",6)==0 ){
682
+ if(g.argc<4){
683
+ usage("branch reopen branch-name(s)...");
684
+ }
685
+ branch_cmd_close(3, 0);
686
+ }else if( strncmp(zCmd,"hide",4)==0 ){
687
+ if(g.argc<4){
688
+ usage("branch hide branch-name(s)...");
689
+ }
690
+ branch_cmd_hide(3,1);
691
+ }else if( strncmp(zCmd,"unhide",6)==0 ){
692
+ if(g.argc<4){
693
+ usage("branch unhide branch-name(s)...");
694
+ }
695
+ branch_cmd_hide(3,0);
457696
}else{
458697
fossil_fatal("branch subcommand should be one of: "
459
- "current info list ls new");
698
+ "close current hide info list ls new reopen unhide");
460699
}
461700
}
462701
463702
/*
464703
** This is the new-style branch-list page that shows the branch names
465704
--- src/branch.c
+++ src/branch.c
@@ -344,22 +344,241 @@
344 " AND ox.rid=ix.rid)",
345 TAG_BRANCH, zBrName, TAG_CLOSED
346 );
347 }
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
350 /*
351 ** COMMAND: branch
352 **
353 ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
354 **
355 ** Run various subcommands to manage branches of the open repository or
356 ** of the repository identified by the -R or --repository option.
357 **
 
 
 
 
 
 
 
 
 
 
 
 
358 ** > fossil branch current
359 **
360 ** Print the name of the branch for the current check-out
 
 
 
 
 
 
361 **
362 ** > fossil branch info BRANCH-NAME
363 **
364 ** Print information about a branch
365 **
@@ -377,17 +596,17 @@
377 **
378 ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
379 **
380 ** Create a new branch BRANCH-NAME off of check-in BASIS.
381 ** Supported options for this subcommand include:
382 ** --private branch is private (i.e., remains local)
383 ** --bgcolor COLOR use COLOR instead of automatic background
384 ** ("auto" lets Fossil choose it automatically,
385 ** even for private branches)
386 ** --nosign do not sign contents on this branch
387 ** --date-override DATE DATE to use instead of 'now'
388 ** --user-override USER USER to use instead of the current default
389 **
390 ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
391 ** year-month-day form, it may be truncated, the "T" may be
392 ** replaced by a space, and it may also name a timezone offset
393 ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
@@ -452,13 +671,33 @@
452 fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
453 }
454 db_finalize(&q);
455 }else if( strncmp(zCmd,"new",n)==0 ){
456 branch_new();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457 }else{
458 fossil_fatal("branch subcommand should be one of: "
459 "current info list ls new");
460 }
461 }
462
463 /*
464 ** This is the new-style branch-list page that shows the branch names
465
--- src/branch.c
+++ src/branch.c
@@ -344,22 +344,241 @@
344 " AND ox.rid=ix.rid)",
345 TAG_BRANCH, zBrName, TAG_CLOSED
346 );
347 }
348
349 /*
350 ** Internal helper for branch_cmd_close() and friends. Adds a row to
351 ** the to the brcmdtag TEMP table, initializing that table if needed,
352 ** holding a pending tag for the given blob.rid (which is assumed to
353 ** be valid). zTag must be a fully-formed tag name, including the
354 ** (+,-,*) prefix character.
355 **
356 */
357 static void branch_cmd_tag_add(int rid, const char *zTag){
358 static int once = 0;
359 assert(zTag && ('+'==zTag[0] || '-'==zTag[0] || '*'==zTag[0]));
360 if(0==once++){
361 db_multi_exec("CREATE TEMP TABLE brcmdtag("
362 "rid INTEGER UNIQUE ON CONFLICT IGNORE,"
363 "tag TEXT NOT NULL"
364 ")");
365 }
366 db_multi_exec("INSERT INTO brcmdtag(rid,tag) VALUES(%d,%Q)",
367 rid, zTag);
368 }
369
370 /*
371 ** Internal helper for branch_cmd_close() and friends. Creates and
372 ** saves a control artifact of tag changes stored via
373 ** branch_cmd_tag_add(). Fails fatally on error, returns 0 if it saves
374 ** an artifact, and a negative value if it does not save anything
375 ** because no tags were queued up. A positive return value is reserved
376 ** for potential future semantics.
377 **
378 ** This function asserts that a transaction is underway and it ends
379 ** the transaction, committing or rolling back, as appropriate.
380 */
381 static int branch_cmd_tag_finalize(int fDryRun /* roll back if true */,
382 int fVerbose /* output extra info */,
383 const char *zDateOvrd /* --date-override */,
384 const char *zUserOvrd /* --user-override */){
385 int nTags = 0;
386 Stmt q = empty_Stmt;
387 Blob manifest = empty_blob;
388 int doRollback = fDryRun!=0;
389
390 assert(db_transaction_nesting_depth() > 0);
391 if(!db_table_exists("temp","brcmdtag")){
392 fossil_warning("No tags added - nothing to do.");
393 db_end_transaction(1);
394 return -1;
395 }
396 db_prepare(&q, "SELECT b.uuid, t.tag "
397 "FROM blob b, brcmdtag t "
398 "WHERE b.rid=t.rid "
399 "ORDER BY t.tag, b.uuid");
400 blob_appendf(&manifest, "D %z\n",
401 date_in_standard_format( zDateOvrd ? zDateOvrd : "now"));
402 while(SQLITE_ROW==db_step(&q)){
403 const char * zHash = db_column_text(&q, 0);
404 const char * zTag = db_column_text(&q, 1);
405 blob_appendf(&manifest, "T %s %s\n", zTag, zHash);
406 ++nTags;
407 }
408 if(!nTags){
409 fossil_warning("No tags added - nothing to do.");
410 db_end_transaction(1);
411 blob_reset(&manifest);
412 return -1;
413 }
414 user_select();
415 blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name());
416 { /* Z-card and save artifact */
417 int newRid;
418 Blob cksum = empty_blob;
419 md5sum_blob(&manifest, &cksum);
420 blob_appendf(&manifest, "Z %b\n", &cksum);
421 blob_reset(&cksum);
422 if(fDryRun && fVerbose){
423 fossil_print("Dry-run mode: will roll back new artifact:\n%b",
424 &manifest);
425 /* Run through the saving steps, though, noting that doing so
426 ** will clear out &manifest, which is why we output it here
427 ** instead of after saving. */
428 }
429 newRid = content_put(&manifest);
430 if(0==newRid){
431 fossil_fatal("Problem saving new artifact: %s\n%b",
432 g.zErrMsg, &manifest);
433 }else if(manifest_crosslink(newRid, &manifest, 0)==0){
434 fossil_fatal("Crosslinking error: %s", g.zErrMsg);
435 }
436 fossil_print("Saved new control artifact %z (RID %d).\n",
437 rid_to_uuid(newRid), newRid);
438 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", newRid);
439 if(fDryRun){
440 fossil_print("Dry-run mode: rolling back new artifact.\n");
441 assert(0!=doRollback);
442 }
443 }
444 db_multi_exec("DROP TABLE brcmdtag");
445 blob_reset(&manifest);
446 db_end_transaction(doRollback);
447 return 0;
448 }
449
450 /*
451 ** Internal helper for branch_cmd_close() and friends. zName is a
452 ** symbolic checkin name. Returns the blob.rid of the checkin or fails
453 ** fatally if the name does not resolve unambiguously. If zUuid is
454 ** not NULL, *zUuid is set to the resolved blob.uuid and must be freed
455 ** by the caller via fossil_free().
456 */
457 static int branch_resolve_name(char const *zName, char **zUuid){
458 const int rid = name_to_uuid2(zName, "ci", zUuid);
459 if(0==rid){
460 fossil_fatal("Cannot resolve name: %s", zName);
461 }else if(rid<0){
462 fossil_fatal("Ambiguous name: %s", zName);
463 }
464 return rid;
465 }
466
467 /*
468 ** Implementation of (branch hide/unhide) subcommands. nStartAtArg is
469 ** the g.argv index to start reading branch/checkin names. fHide is
470 ** true for hiding, false for unhiding. Fails fatally on error.
471 */
472 static void branch_cmd_hide(int nStartAtArg, int fHide){
473 int argPos = nStartAtArg; /* g.argv pos with first branch/ci name */
474 char * zUuid = 0; /* Resolved branch UUID. */
475 const int fVerbose = find_option("verbose","v",0)!=0;
476 const int fDryRun = find_option("dry-run","n",0)!=0;
477 const char *zDateOvrd = find_option("date-override",0,1);
478 const char *zUserOvrd = find_option("user-override",0,1);
479
480 verify_all_options();
481 db_begin_transaction();
482 for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
483 const char * zName = g.argv[argPos];
484 const int rid = branch_resolve_name(zName, &zUuid);
485 const int isHidden = rid_has_tag(rid, TAG_HIDDEN);
486 /* Potential TODO: check for existing 'hidden' flag and skip this
487 ** entry if it already has (if fHide) or does not have (if !fHide)
488 ** that tag. FWIW, /ci_edit does not do so. */
489 if(fHide && isHidden){
490 fossil_warning("Skipping hidden checkin %s: %s.", zName, zUuid);
491 continue;
492 }else if(!fHide && !isHidden){
493 fossil_warning("Skipping non-hidden checkin %s: %s.", zName, zUuid);
494 continue;
495 }
496 branch_cmd_tag_add(rid, fHide ? "*hidden" : "-hidden");
497 if(fVerbose!=0){
498 fossil_print("%s checkin [%s] %s\n",
499 fHide ? "Hiding" : "Unhiding",
500 zName, zUuid);
501 }
502 }
503 branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
504 }
505
506 /*
507 ** Implementation of (branch close|reopen) subcommands. nStartAtArg is
508 ** the g.argv index to start reading branch/checkin names. The given
509 ** checkins are closed if fClose is true, else their "closed" tag (if
510 ** any) is cancelled. Fails fatally on error.
511 */
512 static void branch_cmd_close(int nStartAtArg, int fClose){
513 int argPos = nStartAtArg; /* g.argv pos with first branch name */
514 char * zUuid = 0; /* Resolved branch UUID. */
515 const int fVerbose = find_option("verbose","v",0)!=0;
516 const int fDryRun = find_option("dry-run","n",0)!=0;
517 const char *zDateOvrd = find_option("date-override",0,1);
518 const char *zUserOvrd = find_option("user-override",0,1);
519
520 verify_all_options();
521 db_begin_transaction();
522 for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
523 const char * zName = g.argv[argPos];
524 const int rid = branch_resolve_name(zName, &zUuid);
525 const int isClosed = leaf_is_closed(rid);
526 if(!is_a_leaf(rid)){
527 /* This behaviour is different from /ci_edit closing, where
528 ** is_a_leaf() adds a "+" tag and !is_a_leaf() adds a "*"
529 ** tag. We might want to change this to match for consistency's
530 ** sake, but it currently seems unnecessary to close/re-open a
531 ** non-leaf. */
532 fossil_warning("Skipping non-leaf [%s] %s", zName, zUuid);
533 continue;
534 }else if(fClose && isClosed){
535 fossil_warning("Skipping closed leaf [%s] %s", zName, zUuid);
536 continue;
537 }else if(!fClose && !isClosed){
538 fossil_warning("Skipping non-closed leaf [%s] %s", zName, zUuid);
539 continue;
540 }
541 branch_cmd_tag_add(rid, fClose ? "+closed" : "-closed");
542 if(fVerbose!=0){
543 fossil_print("%s branch [%s] %s\n",
544 fClose ? "Closing" : "Re-opening",
545 zName, zUuid);
546 }
547 }
548 branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
549 }
550
551 /*
552 ** COMMAND: branch
553 **
554 ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
555 **
556 ** Run various subcommands to manage branches of the open repository or
557 ** of the repository identified by the -R or --repository option.
558 **
559 ** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
560 **
561 ** Adds or cancels the "closed" tag to one or more branches.
562 ** It accepts arbitrary unambiguous symbolic names but
563 ** will only resolve checkin names and skips any which resolve
564 ** to non-leaf checkins. Options:
565 ** -n|--dry-run do not commit changes and dump artifact
566 ** to stdout
567 ** -v|--verbose output more information
568 ** --date-override DATE DATE to use instead of 'now'
569 ** --user-override USER USER to use instead of the current default
570 **
571 ** > fossil branch current
572 **
573 ** Print the name of the branch for the current check-out
574 **
575 ** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
576 **
577 ** Adds or cancels the "hidden" tag for the specified branches or
578 ** or checkin IDs. Accepts the same options as the close
579 ** subcommand.
580 **
581 ** > fossil branch info BRANCH-NAME
582 **
583 ** Print information about a branch
584 **
@@ -377,17 +596,17 @@
596 **
597 ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
598 **
599 ** Create a new branch BRANCH-NAME off of check-in BASIS.
600 ** Supported options for this subcommand include:
601 ** --private branch is private (i.e., remains local)
602 ** --bgcolor COLOR use COLOR instead of automatic background
603 ** ("auto" lets Fossil choose it automatically,
604 ** even for private branches)
605 ** --nosign do not sign contents on this branch
606 ** --date-override DATE DATE to use instead of 'now'
607 ** --user-override USER USER to use instead of the current default
608 **
609 ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
610 ** year-month-day form, it may be truncated, the "T" may be
611 ** replaced by a space, and it may also name a timezone offset
612 ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
@@ -452,13 +671,33 @@
671 fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
672 }
673 db_finalize(&q);
674 }else if( strncmp(zCmd,"new",n)==0 ){
675 branch_new();
676 }else if( strncmp(zCmd,"close",5)==0 ){
677 if(g.argc<4){
678 usage("branch close branch-name(s)...");
679 }
680 branch_cmd_close(3, 1);
681 }else if( strncmp(zCmd,"reopen",6)==0 ){
682 if(g.argc<4){
683 usage("branch reopen branch-name(s)...");
684 }
685 branch_cmd_close(3, 0);
686 }else if( strncmp(zCmd,"hide",4)==0 ){
687 if(g.argc<4){
688 usage("branch hide branch-name(s)...");
689 }
690 branch_cmd_hide(3,1);
691 }else if( strncmp(zCmd,"unhide",6)==0 ){
692 if(g.argc<4){
693 usage("branch unhide branch-name(s)...");
694 }
695 branch_cmd_hide(3,0);
696 }else{
697 fossil_fatal("branch subcommand should be one of: "
698 "close current hide info list ls new reopen unhide");
699 }
700 }
701
702 /*
703 ** This is the new-style branch-list page that shows the branch names
704
+246 -7
--- src/branch.c
+++ src/branch.c
@@ -344,22 +344,241 @@
344344
" AND ox.rid=ix.rid)",
345345
TAG_BRANCH, zBrName, TAG_CLOSED
346346
);
347347
}
348348
349
+/*
350
+** Internal helper for branch_cmd_close() and friends. Adds a row to
351
+** the to the brcmdtag TEMP table, initializing that table if needed,
352
+** holding a pending tag for the given blob.rid (which is assumed to
353
+** be valid). zTag must be a fully-formed tag name, including the
354
+** (+,-,*) prefix character.
355
+**
356
+*/
357
+static void branch_cmd_tag_add(int rid, const char *zTag){
358
+ static int once = 0;
359
+ assert(zTag && ('+'==zTag[0] || '-'==zTag[0] || '*'==zTag[0]));
360
+ if(0==once++){
361
+ db_multi_exec("CREATE TEMP TABLE brcmdtag("
362
+ "rid INTEGER UNIQUE ON CONFLICT IGNORE,"
363
+ "tag TEXT NOT NULL"
364
+ ")");
365
+ }
366
+ db_multi_exec("INSERT INTO brcmdtag(rid,tag) VALUES(%d,%Q)",
367
+ rid, zTag);
368
+}
369
+
370
+/*
371
+** Internal helper for branch_cmd_close() and friends. Creates and
372
+** saves a control artifact of tag changes stored via
373
+** branch_cmd_tag_add(). Fails fatally on error, returns 0 if it saves
374
+** an artifact, and a negative value if it does not save anything
375
+** because no tags were queued up. A positive return value is reserved
376
+** for potential future semantics.
377
+**
378
+** This function asserts that a transaction is underway and it ends
379
+** the transaction, committing or rolling back, as appropriate.
380
+*/
381
+static int branch_cmd_tag_finalize(int fDryRun /* roll back if true */,
382
+ int fVerbose /* output extra info */,
383
+ const char *zDateOvrd /* --date-override */,
384
+ const char *zUserOvrd /* --user-override */){
385
+ int nTags = 0;
386
+ Stmt q = empty_Stmt;
387
+ Blob manifest = empty_blob;
388
+ int doRollback = fDryRun!=0;
389
+
390
+ assert(db_transaction_nesting_depth() > 0);
391
+ if(!db_table_exists("temp","brcmdtag")){
392
+ fossil_warning("No tags added - nothing to do.");
393
+ db_end_transaction(1);
394
+ return -1;
395
+ }
396
+ db_prepare(&q, "SELECT b.uuid, t.tag "
397
+ "FROM blob b, brcmdtag t "
398
+ "WHERE b.rid=t.rid "
399
+ "ORDER BY t.tag, b.uuid");
400
+ blob_appendf(&manifest, "D %z\n",
401
+ date_in_standard_format( zDateOvrd ? zDateOvrd : "now"));
402
+ while(SQLITE_ROW==db_step(&q)){
403
+ const char * zHash = db_column_text(&q, 0);
404
+ const char * zTag = db_column_text(&q, 1);
405
+ blob_appendf(&manifest, "T %s %s\n", zTag, zHash);
406
+ ++nTags;
407
+ }
408
+ if(!nTags){
409
+ fossil_warning("No tags added - nothing to do.");
410
+ db_end_transaction(1);
411
+ blob_reset(&manifest);
412
+ return -1;
413
+ }
414
+ user_select();
415
+ blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name());
416
+ { /* Z-card and save artifact */
417
+ int newRid;
418
+ Blob cksum = empty_blob;
419
+ md5sum_blob(&manifest, &cksum);
420
+ blob_appendf(&manifest, "Z %b\n", &cksum);
421
+ blob_reset(&cksum);
422
+ if(fDryRun && fVerbose){
423
+ fossil_print("Dry-run mode: will roll back new artifact:\n%b",
424
+ &manifest);
425
+ /* Run through the saving steps, though, noting that doing so
426
+ ** will clear out &manifest, which is why we output it here
427
+ ** instead of after saving. */
428
+ }
429
+ newRid = content_put(&manifest);
430
+ if(0==newRid){
431
+ fossil_fatal("Problem saving new artifact: %s\n%b",
432
+ g.zErrMsg, &manifest);
433
+ }else if(manifest_crosslink(newRid, &manifest, 0)==0){
434
+ fossil_fatal("Crosslinking error: %s", g.zErrMsg);
435
+ }
436
+ fossil_print("Saved new control artifact %z (RID %d).\n",
437
+ rid_to_uuid(newRid), newRid);
438
+ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", newRid);
439
+ if(fDryRun){
440
+ fossil_print("Dry-run mode: rolling back new artifact.\n");
441
+ assert(0!=doRollback);
442
+ }
443
+ }
444
+ db_multi_exec("DROP TABLE brcmdtag");
445
+ blob_reset(&manifest);
446
+ db_end_transaction(doRollback);
447
+ return 0;
448
+}
449
+
450
+/*
451
+** Internal helper for branch_cmd_close() and friends. zName is a
452
+** symbolic checkin name. Returns the blob.rid of the checkin or fails
453
+** fatally if the name does not resolve unambiguously. If zUuid is
454
+** not NULL, *zUuid is set to the resolved blob.uuid and must be freed
455
+** by the caller via fossil_free().
456
+*/
457
+static int branch_resolve_name(char const *zName, char **zUuid){
458
+ const int rid = name_to_uuid2(zName, "ci", zUuid);
459
+ if(0==rid){
460
+ fossil_fatal("Cannot resolve name: %s", zName);
461
+ }else if(rid<0){
462
+ fossil_fatal("Ambiguous name: %s", zName);
463
+ }
464
+ return rid;
465
+}
466
+
467
+/*
468
+** Implementation of (branch hide/unhide) subcommands. nStartAtArg is
469
+** the g.argv index to start reading branch/checkin names. fHide is
470
+** true for hiding, false for unhiding. Fails fatally on error.
471
+*/
472
+static void branch_cmd_hide(int nStartAtArg, int fHide){
473
+ int argPos = nStartAtArg; /* g.argv pos with first branch/ci name */
474
+ char * zUuid = 0; /* Resolved branch UUID. */
475
+ const int fVerbose = find_option("verbose","v",0)!=0;
476
+ const int fDryRun = find_option("dry-run","n",0)!=0;
477
+ const char *zDateOvrd = find_option("date-override",0,1);
478
+ const char *zUserOvrd = find_option("user-override",0,1);
479
+
480
+ verify_all_options();
481
+ db_begin_transaction();
482
+ for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
483
+ const char * zName = g.argv[argPos];
484
+ const int rid = branch_resolve_name(zName, &zUuid);
485
+ const int isHidden = rid_has_tag(rid, TAG_HIDDEN);
486
+ /* Potential TODO: check for existing 'hidden' flag and skip this
487
+ ** entry if it already has (if fHide) or does not have (if !fHide)
488
+ ** that tag. FWIW, /ci_edit does not do so. */
489
+ if(fHide && isHidden){
490
+ fossil_warning("Skipping hidden checkin %s: %s.", zName, zUuid);
491
+ continue;
492
+ }else if(!fHide && !isHidden){
493
+ fossil_warning("Skipping non-hidden checkin %s: %s.", zName, zUuid);
494
+ continue;
495
+ }
496
+ branch_cmd_tag_add(rid, fHide ? "*hidden" : "-hidden");
497
+ if(fVerbose!=0){
498
+ fossil_print("%s checkin [%s] %s\n",
499
+ fHide ? "Hiding" : "Unhiding",
500
+ zName, zUuid);
501
+ }
502
+ }
503
+ branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
504
+}
505
+
506
+/*
507
+** Implementation of (branch close|reopen) subcommands. nStartAtArg is
508
+** the g.argv index to start reading branch/checkin names. The given
509
+** checkins are closed if fClose is true, else their "closed" tag (if
510
+** any) is cancelled. Fails fatally on error.
511
+*/
512
+static void branch_cmd_close(int nStartAtArg, int fClose){
513
+ int argPos = nStartAtArg; /* g.argv pos with first branch name */
514
+ char * zUuid = 0; /* Resolved branch UUID. */
515
+ const int fVerbose = find_option("verbose","v",0)!=0;
516
+ const int fDryRun = find_option("dry-run","n",0)!=0;
517
+ const char *zDateOvrd = find_option("date-override",0,1);
518
+ const char *zUserOvrd = find_option("user-override",0,1);
519
+
520
+ verify_all_options();
521
+ db_begin_transaction();
522
+ for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
523
+ const char * zName = g.argv[argPos];
524
+ const int rid = branch_resolve_name(zName, &zUuid);
525
+ const int isClosed = leaf_is_closed(rid);
526
+ if(!is_a_leaf(rid)){
527
+ /* This behaviour is different from /ci_edit closing, where
528
+ ** is_a_leaf() adds a "+" tag and !is_a_leaf() adds a "*"
529
+ ** tag. We might want to change this to match for consistency's
530
+ ** sake, but it currently seems unnecessary to close/re-open a
531
+ ** non-leaf. */
532
+ fossil_warning("Skipping non-leaf [%s] %s", zName, zUuid);
533
+ continue;
534
+ }else if(fClose && isClosed){
535
+ fossil_warning("Skipping closed leaf [%s] %s", zName, zUuid);
536
+ continue;
537
+ }else if(!fClose && !isClosed){
538
+ fossil_warning("Skipping non-closed leaf [%s] %s", zName, zUuid);
539
+ continue;
540
+ }
541
+ branch_cmd_tag_add(rid, fClose ? "+closed" : "-closed");
542
+ if(fVerbose!=0){
543
+ fossil_print("%s branch [%s] %s\n",
544
+ fClose ? "Closing" : "Re-opening",
545
+ zName, zUuid);
546
+ }
547
+ }
548
+ branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
549
+}
349550
350551
/*
351552
** COMMAND: branch
352553
**
353554
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
354555
**
355556
** Run various subcommands to manage branches of the open repository or
356557
** of the repository identified by the -R or --repository option.
357558
**
559
+** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
560
+**
561
+** Adds or cancels the "closed" tag to one or more branches.
562
+** It accepts arbitrary unambiguous symbolic names but
563
+** will only resolve checkin names and skips any which resolve
564
+** to non-leaf checkins. Options:
565
+** -n|--dry-run do not commit changes and dump artifact
566
+** to stdout
567
+** -v|--verbose output more information
568
+** --date-override DATE DATE to use instead of 'now'
569
+** --user-override USER USER to use instead of the current default
570
+**
358571
** > fossil branch current
359572
**
360573
** Print the name of the branch for the current check-out
574
+**
575
+** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
576
+**
577
+** Adds or cancels the "hidden" tag for the specified branches or
578
+** or checkin IDs. Accepts the same options as the close
579
+** subcommand.
361580
**
362581
** > fossil branch info BRANCH-NAME
363582
**
364583
** Print information about a branch
365584
**
@@ -377,17 +596,17 @@
377596
**
378597
** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
379598
**
380599
** Create a new branch BRANCH-NAME off of check-in BASIS.
381600
** Supported options for this subcommand include:
382
-** --private branch is private (i.e., remains local)
383
-** --bgcolor COLOR use COLOR instead of automatic background
601
+** --private branch is private (i.e., remains local)
602
+** --bgcolor COLOR use COLOR instead of automatic background
384603
** ("auto" lets Fossil choose it automatically,
385
-** even for private branches)
386
-** --nosign do not sign contents on this branch
387
-** --date-override DATE DATE to use instead of 'now'
388
-** --user-override USER USER to use instead of the current default
604
+** even for private branches)
605
+** --nosign do not sign contents on this branch
606
+** --date-override DATE DATE to use instead of 'now'
607
+** --user-override USER USER to use instead of the current default
389608
**
390609
** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
391610
** year-month-day form, it may be truncated, the "T" may be
392611
** replaced by a space, and it may also name a timezone offset
393612
** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
@@ -452,13 +671,33 @@
452671
fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
453672
}
454673
db_finalize(&q);
455674
}else if( strncmp(zCmd,"new",n)==0 ){
456675
branch_new();
676
+ }else if( strncmp(zCmd,"close",5)==0 ){
677
+ if(g.argc<4){
678
+ usage("branch close branch-name(s)...");
679
+ }
680
+ branch_cmd_close(3, 1);
681
+ }else if( strncmp(zCmd,"reopen",6)==0 ){
682
+ if(g.argc<4){
683
+ usage("branch reopen branch-name(s)...");
684
+ }
685
+ branch_cmd_close(3, 0);
686
+ }else if( strncmp(zCmd,"hide",4)==0 ){
687
+ if(g.argc<4){
688
+ usage("branch hide branch-name(s)...");
689
+ }
690
+ branch_cmd_hide(3,1);
691
+ }else if( strncmp(zCmd,"unhide",6)==0 ){
692
+ if(g.argc<4){
693
+ usage("branch unhide branch-name(s)...");
694
+ }
695
+ branch_cmd_hide(3,0);
457696
}else{
458697
fossil_fatal("branch subcommand should be one of: "
459
- "current info list ls new");
698
+ "close current hide info list ls new reopen unhide");
460699
}
461700
}
462701
463702
/*
464703
** This is the new-style branch-list page that shows the branch names
465704
--- src/branch.c
+++ src/branch.c
@@ -344,22 +344,241 @@
344 " AND ox.rid=ix.rid)",
345 TAG_BRANCH, zBrName, TAG_CLOSED
346 );
347 }
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
350 /*
351 ** COMMAND: branch
352 **
353 ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
354 **
355 ** Run various subcommands to manage branches of the open repository or
356 ** of the repository identified by the -R or --repository option.
357 **
 
 
 
 
 
 
 
 
 
 
 
 
358 ** > fossil branch current
359 **
360 ** Print the name of the branch for the current check-out
 
 
 
 
 
 
361 **
362 ** > fossil branch info BRANCH-NAME
363 **
364 ** Print information about a branch
365 **
@@ -377,17 +596,17 @@
377 **
378 ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
379 **
380 ** Create a new branch BRANCH-NAME off of check-in BASIS.
381 ** Supported options for this subcommand include:
382 ** --private branch is private (i.e., remains local)
383 ** --bgcolor COLOR use COLOR instead of automatic background
384 ** ("auto" lets Fossil choose it automatically,
385 ** even for private branches)
386 ** --nosign do not sign contents on this branch
387 ** --date-override DATE DATE to use instead of 'now'
388 ** --user-override USER USER to use instead of the current default
389 **
390 ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
391 ** year-month-day form, it may be truncated, the "T" may be
392 ** replaced by a space, and it may also name a timezone offset
393 ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
@@ -452,13 +671,33 @@
452 fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
453 }
454 db_finalize(&q);
455 }else if( strncmp(zCmd,"new",n)==0 ){
456 branch_new();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457 }else{
458 fossil_fatal("branch subcommand should be one of: "
459 "current info list ls new");
460 }
461 }
462
463 /*
464 ** This is the new-style branch-list page that shows the branch names
465
--- src/branch.c
+++ src/branch.c
@@ -344,22 +344,241 @@
344 " AND ox.rid=ix.rid)",
345 TAG_BRANCH, zBrName, TAG_CLOSED
346 );
347 }
348
349 /*
350 ** Internal helper for branch_cmd_close() and friends. Adds a row to
351 ** the to the brcmdtag TEMP table, initializing that table if needed,
352 ** holding a pending tag for the given blob.rid (which is assumed to
353 ** be valid). zTag must be a fully-formed tag name, including the
354 ** (+,-,*) prefix character.
355 **
356 */
357 static void branch_cmd_tag_add(int rid, const char *zTag){
358 static int once = 0;
359 assert(zTag && ('+'==zTag[0] || '-'==zTag[0] || '*'==zTag[0]));
360 if(0==once++){
361 db_multi_exec("CREATE TEMP TABLE brcmdtag("
362 "rid INTEGER UNIQUE ON CONFLICT IGNORE,"
363 "tag TEXT NOT NULL"
364 ")");
365 }
366 db_multi_exec("INSERT INTO brcmdtag(rid,tag) VALUES(%d,%Q)",
367 rid, zTag);
368 }
369
370 /*
371 ** Internal helper for branch_cmd_close() and friends. Creates and
372 ** saves a control artifact of tag changes stored via
373 ** branch_cmd_tag_add(). Fails fatally on error, returns 0 if it saves
374 ** an artifact, and a negative value if it does not save anything
375 ** because no tags were queued up. A positive return value is reserved
376 ** for potential future semantics.
377 **
378 ** This function asserts that a transaction is underway and it ends
379 ** the transaction, committing or rolling back, as appropriate.
380 */
381 static int branch_cmd_tag_finalize(int fDryRun /* roll back if true */,
382 int fVerbose /* output extra info */,
383 const char *zDateOvrd /* --date-override */,
384 const char *zUserOvrd /* --user-override */){
385 int nTags = 0;
386 Stmt q = empty_Stmt;
387 Blob manifest = empty_blob;
388 int doRollback = fDryRun!=0;
389
390 assert(db_transaction_nesting_depth() > 0);
391 if(!db_table_exists("temp","brcmdtag")){
392 fossil_warning("No tags added - nothing to do.");
393 db_end_transaction(1);
394 return -1;
395 }
396 db_prepare(&q, "SELECT b.uuid, t.tag "
397 "FROM blob b, brcmdtag t "
398 "WHERE b.rid=t.rid "
399 "ORDER BY t.tag, b.uuid");
400 blob_appendf(&manifest, "D %z\n",
401 date_in_standard_format( zDateOvrd ? zDateOvrd : "now"));
402 while(SQLITE_ROW==db_step(&q)){
403 const char * zHash = db_column_text(&q, 0);
404 const char * zTag = db_column_text(&q, 1);
405 blob_appendf(&manifest, "T %s %s\n", zTag, zHash);
406 ++nTags;
407 }
408 if(!nTags){
409 fossil_warning("No tags added - nothing to do.");
410 db_end_transaction(1);
411 blob_reset(&manifest);
412 return -1;
413 }
414 user_select();
415 blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name());
416 { /* Z-card and save artifact */
417 int newRid;
418 Blob cksum = empty_blob;
419 md5sum_blob(&manifest, &cksum);
420 blob_appendf(&manifest, "Z %b\n", &cksum);
421 blob_reset(&cksum);
422 if(fDryRun && fVerbose){
423 fossil_print("Dry-run mode: will roll back new artifact:\n%b",
424 &manifest);
425 /* Run through the saving steps, though, noting that doing so
426 ** will clear out &manifest, which is why we output it here
427 ** instead of after saving. */
428 }
429 newRid = content_put(&manifest);
430 if(0==newRid){
431 fossil_fatal("Problem saving new artifact: %s\n%b",
432 g.zErrMsg, &manifest);
433 }else if(manifest_crosslink(newRid, &manifest, 0)==0){
434 fossil_fatal("Crosslinking error: %s", g.zErrMsg);
435 }
436 fossil_print("Saved new control artifact %z (RID %d).\n",
437 rid_to_uuid(newRid), newRid);
438 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", newRid);
439 if(fDryRun){
440 fossil_print("Dry-run mode: rolling back new artifact.\n");
441 assert(0!=doRollback);
442 }
443 }
444 db_multi_exec("DROP TABLE brcmdtag");
445 blob_reset(&manifest);
446 db_end_transaction(doRollback);
447 return 0;
448 }
449
450 /*
451 ** Internal helper for branch_cmd_close() and friends. zName is a
452 ** symbolic checkin name. Returns the blob.rid of the checkin or fails
453 ** fatally if the name does not resolve unambiguously. If zUuid is
454 ** not NULL, *zUuid is set to the resolved blob.uuid and must be freed
455 ** by the caller via fossil_free().
456 */
457 static int branch_resolve_name(char const *zName, char **zUuid){
458 const int rid = name_to_uuid2(zName, "ci", zUuid);
459 if(0==rid){
460 fossil_fatal("Cannot resolve name: %s", zName);
461 }else if(rid<0){
462 fossil_fatal("Ambiguous name: %s", zName);
463 }
464 return rid;
465 }
466
467 /*
468 ** Implementation of (branch hide/unhide) subcommands. nStartAtArg is
469 ** the g.argv index to start reading branch/checkin names. fHide is
470 ** true for hiding, false for unhiding. Fails fatally on error.
471 */
472 static void branch_cmd_hide(int nStartAtArg, int fHide){
473 int argPos = nStartAtArg; /* g.argv pos with first branch/ci name */
474 char * zUuid = 0; /* Resolved branch UUID. */
475 const int fVerbose = find_option("verbose","v",0)!=0;
476 const int fDryRun = find_option("dry-run","n",0)!=0;
477 const char *zDateOvrd = find_option("date-override",0,1);
478 const char *zUserOvrd = find_option("user-override",0,1);
479
480 verify_all_options();
481 db_begin_transaction();
482 for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
483 const char * zName = g.argv[argPos];
484 const int rid = branch_resolve_name(zName, &zUuid);
485 const int isHidden = rid_has_tag(rid, TAG_HIDDEN);
486 /* Potential TODO: check for existing 'hidden' flag and skip this
487 ** entry if it already has (if fHide) or does not have (if !fHide)
488 ** that tag. FWIW, /ci_edit does not do so. */
489 if(fHide && isHidden){
490 fossil_warning("Skipping hidden checkin %s: %s.", zName, zUuid);
491 continue;
492 }else if(!fHide && !isHidden){
493 fossil_warning("Skipping non-hidden checkin %s: %s.", zName, zUuid);
494 continue;
495 }
496 branch_cmd_tag_add(rid, fHide ? "*hidden" : "-hidden");
497 if(fVerbose!=0){
498 fossil_print("%s checkin [%s] %s\n",
499 fHide ? "Hiding" : "Unhiding",
500 zName, zUuid);
501 }
502 }
503 branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
504 }
505
506 /*
507 ** Implementation of (branch close|reopen) subcommands. nStartAtArg is
508 ** the g.argv index to start reading branch/checkin names. The given
509 ** checkins are closed if fClose is true, else their "closed" tag (if
510 ** any) is cancelled. Fails fatally on error.
511 */
512 static void branch_cmd_close(int nStartAtArg, int fClose){
513 int argPos = nStartAtArg; /* g.argv pos with first branch name */
514 char * zUuid = 0; /* Resolved branch UUID. */
515 const int fVerbose = find_option("verbose","v",0)!=0;
516 const int fDryRun = find_option("dry-run","n",0)!=0;
517 const char *zDateOvrd = find_option("date-override",0,1);
518 const char *zUserOvrd = find_option("user-override",0,1);
519
520 verify_all_options();
521 db_begin_transaction();
522 for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){
523 const char * zName = g.argv[argPos];
524 const int rid = branch_resolve_name(zName, &zUuid);
525 const int isClosed = leaf_is_closed(rid);
526 if(!is_a_leaf(rid)){
527 /* This behaviour is different from /ci_edit closing, where
528 ** is_a_leaf() adds a "+" tag and !is_a_leaf() adds a "*"
529 ** tag. We might want to change this to match for consistency's
530 ** sake, but it currently seems unnecessary to close/re-open a
531 ** non-leaf. */
532 fossil_warning("Skipping non-leaf [%s] %s", zName, zUuid);
533 continue;
534 }else if(fClose && isClosed){
535 fossil_warning("Skipping closed leaf [%s] %s", zName, zUuid);
536 continue;
537 }else if(!fClose && !isClosed){
538 fossil_warning("Skipping non-closed leaf [%s] %s", zName, zUuid);
539 continue;
540 }
541 branch_cmd_tag_add(rid, fClose ? "+closed" : "-closed");
542 if(fVerbose!=0){
543 fossil_print("%s branch [%s] %s\n",
544 fClose ? "Closing" : "Re-opening",
545 zName, zUuid);
546 }
547 }
548 branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd);
549 }
550
551 /*
552 ** COMMAND: branch
553 **
554 ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
555 **
556 ** Run various subcommands to manage branches of the open repository or
557 ** of the repository identified by the -R or --repository option.
558 **
559 ** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
560 **
561 ** Adds or cancels the "closed" tag to one or more branches.
562 ** It accepts arbitrary unambiguous symbolic names but
563 ** will only resolve checkin names and skips any which resolve
564 ** to non-leaf checkins. Options:
565 ** -n|--dry-run do not commit changes and dump artifact
566 ** to stdout
567 ** -v|--verbose output more information
568 ** --date-override DATE DATE to use instead of 'now'
569 ** --user-override USER USER to use instead of the current default
570 **
571 ** > fossil branch current
572 **
573 ** Print the name of the branch for the current check-out
574 **
575 ** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
576 **
577 ** Adds or cancels the "hidden" tag for the specified branches or
578 ** or checkin IDs. Accepts the same options as the close
579 ** subcommand.
580 **
581 ** > fossil branch info BRANCH-NAME
582 **
583 ** Print information about a branch
584 **
@@ -377,17 +596,17 @@
596 **
597 ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
598 **
599 ** Create a new branch BRANCH-NAME off of check-in BASIS.
600 ** Supported options for this subcommand include:
601 ** --private branch is private (i.e., remains local)
602 ** --bgcolor COLOR use COLOR instead of automatic background
603 ** ("auto" lets Fossil choose it automatically,
604 ** even for private branches)
605 ** --nosign do not sign contents on this branch
606 ** --date-override DATE DATE to use instead of 'now'
607 ** --user-override USER USER to use instead of the current default
608 **
609 ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
610 ** year-month-day form, it may be truncated, the "T" may be
611 ** replaced by a space, and it may also name a timezone offset
612 ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
@@ -452,13 +671,33 @@
671 fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
672 }
673 db_finalize(&q);
674 }else if( strncmp(zCmd,"new",n)==0 ){
675 branch_new();
676 }else if( strncmp(zCmd,"close",5)==0 ){
677 if(g.argc<4){
678 usage("branch close branch-name(s)...");
679 }
680 branch_cmd_close(3, 1);
681 }else if( strncmp(zCmd,"reopen",6)==0 ){
682 if(g.argc<4){
683 usage("branch reopen branch-name(s)...");
684 }
685 branch_cmd_close(3, 0);
686 }else if( strncmp(zCmd,"hide",4)==0 ){
687 if(g.argc<4){
688 usage("branch hide branch-name(s)...");
689 }
690 branch_cmd_hide(3,1);
691 }else if( strncmp(zCmd,"unhide",6)==0 ){
692 if(g.argc<4){
693 usage("branch unhide branch-name(s)...");
694 }
695 branch_cmd_hide(3,0);
696 }else{
697 fossil_fatal("branch subcommand should be one of: "
698 "close current hide info list ls new reopen unhide");
699 }
700 }
701
702 /*
703 ** This is the new-style branch-list page that shows the branch names
704
+14
--- src/tag.c
+++ src/tag.c
@@ -889,5 +889,19 @@
889889
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890890
db_finalize(&q);
891891
@ <br />
892892
style_finish_page();
893893
}
894
+
895
+/*
896
+** Returns true if the given blob.rid value has the given tag ID
897
+** applied to it, else false.
898
+*/
899
+int rid_has_tag(int rid, int tagId){
900
+ return db_exists(
901
+ "SELECT tag.tagid FROM tagxref, tag"
902
+ " WHERE tagxref.rid=%d AND tagtype>0 "
903
+ " AND tag.tagid=%d"
904
+ " AND tagxref.tagid=tag.tagid",
905
+ rid, tagId
906
+ );
907
+}
894908
--- src/tag.c
+++ src/tag.c
@@ -889,5 +889,19 @@
889 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890 db_finalize(&q);
891 @ <br />
892 style_finish_page();
893 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
--- src/tag.c
+++ src/tag.c
@@ -889,5 +889,19 @@
889 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890 db_finalize(&q);
891 @ <br />
892 style_finish_page();
893 }
894
895 /*
896 ** Returns true if the given blob.rid value has the given tag ID
897 ** applied to it, else false.
898 */
899 int rid_has_tag(int rid, int tagId){
900 return db_exists(
901 "SELECT tag.tagid FROM tagxref, tag"
902 " WHERE tagxref.rid=%d AND tagtype>0 "
903 " AND tag.tagid=%d"
904 " AND tagxref.tagid=tag.tagid",
905 rid, tagId
906 );
907 }
908
+14
--- src/tag.c
+++ src/tag.c
@@ -889,5 +889,19 @@
889889
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890890
db_finalize(&q);
891891
@ <br />
892892
style_finish_page();
893893
}
894
+
895
+/*
896
+** Returns true if the given blob.rid value has the given tag ID
897
+** applied to it, else false.
898
+*/
899
+int rid_has_tag(int rid, int tagId){
900
+ return db_exists(
901
+ "SELECT tag.tagid FROM tagxref, tag"
902
+ " WHERE tagxref.rid=%d AND tagtype>0 "
903
+ " AND tag.tagid=%d"
904
+ " AND tagxref.tagid=tag.tagid",
905
+ rid, tagId
906
+ );
907
+}
894908
--- src/tag.c
+++ src/tag.c
@@ -889,5 +889,19 @@
889 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890 db_finalize(&q);
891 @ <br />
892 style_finish_page();
893 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
--- src/tag.c
+++ src/tag.c
@@ -889,5 +889,19 @@
889 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890 db_finalize(&q);
891 @ <br />
892 style_finish_page();
893 }
894
895 /*
896 ** Returns true if the given blob.rid value has the given tag ID
897 ** applied to it, else false.
898 */
899 int rid_has_tag(int rid, int tagId){
900 return db_exists(
901 "SELECT tag.tagid FROM tagxref, tag"
902 " WHERE tagxref.rid=%d AND tagtype>0 "
903 " AND tag.tagid=%d"
904 " AND tagxref.tagid=tag.tagid",
905 rid, tagId
906 );
907 }
908
--- www/changes.wiki
+++ www/changes.wiki
@@ -18,10 +18,12 @@
1818
the current page and list URLs suitable for pasting them into the page.
1919
* Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
2020
and similar.
2121
* Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
2222
the --verbose option.
23
+ * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
24
+ </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
2325
2426
<a name='v2_16'></a>
2527
<h2>Changes for Version 2.16 (2021-07-02)</h2>
2628
* <b>Security:</b> Fix the client-side TLS so that it verifies that the
2729
server hostname matches its certificate.
2830
--- www/changes.wiki
+++ www/changes.wiki
@@ -18,10 +18,12 @@
18 the current page and list URLs suitable for pasting them into the page.
19 * Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
20 and similar.
21 * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
22 the --verbose option.
 
 
23
24 <a name='v2_16'></a>
25 <h2>Changes for Version 2.16 (2021-07-02)</h2>
26 * <b>Security:</b> Fix the client-side TLS so that it verifies that the
27 server hostname matches its certificate.
28
--- www/changes.wiki
+++ www/changes.wiki
@@ -18,10 +18,12 @@
18 the current page and list URLs suitable for pasting them into the page.
19 * Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
20 and similar.
21 * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
22 the --verbose option.
23 * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
24 </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
25
26 <a name='v2_16'></a>
27 <h2>Changes for Version 2.16 (2021-07-02)</h2>
28 * <b>Security:</b> Fix the client-side TLS so that it verifies that the
29 server hostname matches its certificate.
30
--- www/changes.wiki
+++ www/changes.wiki
@@ -18,10 +18,12 @@
1818
the current page and list URLs suitable for pasting them into the page.
1919
* Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
2020
and similar.
2121
* Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
2222
the --verbose option.
23
+ * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
24
+ </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
2325
2426
<a name='v2_16'></a>
2527
<h2>Changes for Version 2.16 (2021-07-02)</h2>
2628
* <b>Security:</b> Fix the client-side TLS so that it verifies that the
2729
server hostname matches its certificate.
2830
--- www/changes.wiki
+++ www/changes.wiki
@@ -18,10 +18,12 @@
18 the current page and list URLs suitable for pasting them into the page.
19 * Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
20 and similar.
21 * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
22 the --verbose option.
 
 
23
24 <a name='v2_16'></a>
25 <h2>Changes for Version 2.16 (2021-07-02)</h2>
26 * <b>Security:</b> Fix the client-side TLS so that it verifies that the
27 server hostname matches its certificate.
28
--- www/changes.wiki
+++ www/changes.wiki
@@ -18,10 +18,12 @@
18 the current page and list URLs suitable for pasting them into the page.
19 * Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
20 and similar.
21 * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
22 the --verbose option.
23 * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
24 </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command].
25
26 <a name='v2_16'></a>
27 <h2>Changes for Version 2.16 (2021-07-02)</h2>
28 * <b>Security:</b> Fix the client-side TLS so that it verifies that the
29 server hostname matches its certificate.
30

Keyboard Shortcuts

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