Fossil SCM

Show before and after context on the /ci_edit page.

drh 2026-05-05 22:56 UTC enhanced-checkin-edit
Commit 664da57e00c2be88ec4ce4a4af1ab1cf956fd44bba7b532b0e2bd0ea3ef68de4
1 file changed +121 -92
+121 -92
--- src/info.c
+++ src/info.c
@@ -271,13 +271,18 @@
271271
}
272272
}
273273
274274
/*
275275
** Show the context graph (immediate parents and children) for
276
-** check-in rid and rid2
276
+** check-in rid and rid2. rid2 might be zero, in which case only
277
+** show the context for rid1.
277278
*/
278
-void render_checkin_context(int rid, int rid2, int parentsOnly, int mFlags){
279
+void render_checkin_context(
280
+ int rid, int rid2, /* One or two checkins for which to show context */
281
+ int mRCCFlags, /* Flags. 1: parents only. 2: no-highlight */
282
+ int mFlags /* Graph flags */
283
+){
279284
Blob sql;
280285
Stmt q;
281286
int rx[2];
282287
int i, n;
283288
rx[0] = rid;
@@ -295,11 +300,11 @@
295300
"INSERT OR IGNORE INTO ok VALUES(%d);"
296301
"INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
297302
rx[i], rx[i]
298303
);
299304
}
300
- if( !parentsOnly ){
305
+ if( (mRCCFlags & 0x01)==0 ){
301306
for(i=0; i<n; i++){
302307
db_multi_exec(
303308
"INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
304309
);
305310
if( db_table_exists("repository","cherrypick") ){
@@ -313,10 +318,13 @@
313318
}
314319
}
315320
}
316321
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
317322
db_prepare(&q, "%s", blob_sql_text(&sql));
323
+ if( mRCCFlags & 0x02 ){
324
+ rid = rid2 = 0;
325
+ }
318326
www_print_timeline(&q,
319327
mFlags
320328
|TIMELINE_GRAPH
321329
|TIMELINE_FILLGAPS
322330
|TIMELINE_NOSCROLL
@@ -3453,74 +3461,93 @@
34533461
while( fossil_isspace(zA[0]) ) zA++;
34543462
return zA[0]==0 && zB[0]==0;
34553463
}
34563464
34573465
/*
3458
-** The following methods operate on the newtags temporary table
3459
-** that is used to collect various changes to be added to a control
3460
-** artifact for a check-in edit.
3466
+*****************************************************************************
3467
+** The following methods operate on the "newtags" temporary table. This
3468
+** table collects changes for a control artifact that will implement an
3469
+** edit to a check-in.
3470
+**
3471
+** Initialize the newtags table
34613472
*/
34623473
static void init_newtags(void){
34633474
db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
34643475
}
34653476
3477
+/* Add a new changes to the newtags table */
34663478
static void change_special(
34673479
const char *zName, /* Name of the special tag */
34683480
const char *zOp, /* Operation prefix (e.g. +,-,*) */
34693481
const char *zValue /* Value of the tag */
34703482
){
34713483
db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
34723484
}
34733485
3486
+/* Change a symbolic tag */
34743487
static void change_sym_tag(const char *zTag, const char *zOp){
34753488
db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
34763489
}
34773490
3491
+/* Cancel a tag */
34783492
static void cancel_special(const char *zTag){
34793493
change_special(zTag,"-",0);
34803494
}
34813495
3496
+/* Add a background color tag. This will be propagating tag
3497
+** if fPropagateColor is true. */
34823498
static void add_color(const char *zNewColor, int fPropagateColor){
34833499
change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
34843500
}
34853501
3502
+/* Cancel a background color tag */
34863503
static void cancel_color(void){
34873504
change_special("bgcolor","-",0);
34883505
}
34893506
3507
+/* Add a comment-change tag */
34903508
static void add_comment(const char *zNewComment){
34913509
change_special("comment","+",zNewComment);
34923510
}
34933511
3512
+/* Add a date-change tag */
34943513
static void add_date(const char *zNewDate){
34953514
change_special("date","+",zNewDate);
34963515
}
34973516
3517
+/* Add a change-user tag */
34983518
static void add_user(const char *zNewUser){
34993519
change_special("user","+",zNewUser);
35003520
}
35013521
3522
+/* Add a generic symbolic tag (one that has no value) */
35023523
static void add_tag(const char *zNewTag){
35033524
change_sym_tag(zNewTag,"+");
35043525
}
35053526
3527
+/* Cancel an existing symbolic tag associated with check-in rid */
35063528
static void cancel_tag(int rid, const char *zCancelTag){
35073529
if( db_exists("SELECT 1 FROM tagxref, tag"
35083530
" WHERE tagxref.rid=%d AND tagtype>0"
35093531
" AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
35103532
rid, zCancelTag)
3511
- ) change_sym_tag(zCancelTag,"-");
3533
+ ){
3534
+ change_sym_tag(zCancelTag,"-");
3535
+ }
35123536
}
35133537
3538
+/* Add a hidden tag that propagates to all ancestors */
35143539
static void hide_branch(void){
35153540
change_special("hidden","*",0);
35163541
}
35173542
3543
+/* Close the branch */
35183544
static void close_leaf(int rid){
35193545
change_special("closed",is_a_leaf(rid)?"+":"*",0);
35203546
}
35213547
3548
+/* Move the check-in to a different branch */
35223549
static void change_branch(int rid, const char *zNewBranch){
35233550
db_multi_exec(
35243551
"REPLACE INTO newtags "
35253552
" SELECT tagname, '-', NULL FROM tagxref, tag"
35263553
" WHERE tagxref.rid=%d AND tagtype==2"
@@ -3531,19 +3558,31 @@
35313558
change_special("branch","*",zNewBranch);
35323559
change_sym_tag(zNewBranch,"*");
35333560
}
35343561
35353562
/*
3536
-** The apply_newtags method is called after all newtags have been added
3537
-** and the control artifact is completed and then written to the DB.
3563
+** Construct a control artifact.
3564
+**
3565
+** Preconditions:
3566
+**
3567
+** (1) "ctrl" contains the beginning of a control artifact with
3568
+** just the D-card showing the timestamp on the control artifact
3569
+** itself.
3570
+**
3571
+** (2) The newtags temporary table has been constructed.
3572
+**
3573
+** (3) Zero or more methods may have been called to populate the
3574
+** newtags table. Or the newtags table might be empty.
3575
+**
3576
+** Construct the complete control artifact. Or, if there are not
3577
+** changes, just zero out the ctrl blob.
35383578
*/
3539
-static void apply_newtags(
3540
- Blob *ctrl,
3541
- int rid,
3542
- const char *zUuid,
3543
- const char *zUserOvrd, /* The user name on the control artifact */
3544
- int fDryRun /* Print control artifact, but make no changes */
3579
+static void construct_newtags_artifact(
3580
+ Blob *ctrl, /* The control artifact text */
3581
+ int rid, /* rid of the check-in that the artifact applies to */
3582
+ const char *zUuid, /* UUID of the check-in to which the artifact applies*/
3583
+ const char *zUserOvrd /* The user name on the control artifact */
35453584
){
35463585
Stmt q;
35473586
int nChng = 0;
35483587
35493588
db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
@@ -3559,33 +3598,46 @@
35593598
blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
35603599
}
35613600
}
35623601
db_finalize(&q);
35633602
if( nChng>0 ){
3564
- int nrid;
35653603
Blob cksum;
35663604
if( zUserOvrd && zUserOvrd[0] ){
35673605
blob_appendf(ctrl, "U %F\n", zUserOvrd);
35683606
}else{
35693607
blob_appendf(ctrl, "U %F\n", login_name());
35703608
}
35713609
md5sum_blob(ctrl, &cksum);
35723610
blob_appendf(ctrl, "Z %b\n", &cksum);
3573
- if( fDryRun ){
3574
- assert( g.isHTTP==0 ); /* Only print control artifact in console mode. */
3575
- fossil_print("%s", blob_str(ctrl));
3576
- blob_reset(ctrl);
3577
- }else{
3578
- db_begin_transaction();
3579
- g.markPrivate = content_is_private(rid);
3580
- nrid = content_put(ctrl);
3581
- manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
3582
- db_end_transaction(0);
3583
- }
3584
- assert( blob_is_reset(ctrl) );
3611
+ blob_reset(&cksum);
3612
+ }else{
3613
+ blob_reset(ctrl);
3614
+ }
3615
+}
3616
+
3617
+/*
3618
+** Return true if the artifact is complete. We assume that any non-empty
3619
+** artifact is complete.
3620
+*/
3621
+static int artifact_is_complete(Blob *ctrl){
3622
+ return blob_size(ctrl)>0;
3623
+}
3624
+
3625
+/*
3626
+** Apply a control artifact to the repository database. Except, if the
3627
+** artifact is empty, this routine is a no-op.
3628
+*/
3629
+static void publish_newtags_artifact(Blob *ctrl, int rid){
3630
+ if( artifact_is_complete(ctrl) ){
3631
+ int nrid;
3632
+ g.markPrivate = content_is_private(rid);
3633
+ nrid = content_put(ctrl);
3634
+ manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
35853635
}
35863636
}
3637
+/* End of the newtags subsystem
3638
+******************************************************************************/
35873639
35883640
/*
35893641
** This method checks that the date can be parsed.
35903642
** Returns 1 if datetime() can validate, 0 otherwise.
35913643
*/
@@ -3644,14 +3696,17 @@
36443696
int fPropagateColor; /* True if color propagates before edit */
36453697
int fNewPropagateColor; /* True if color propagates after edit */
36463698
int fHasHidden = 0; /* True if hidden tag already set */
36473699
int fHasClosed = 0; /* True if closed tag already set */
36483700
const char *zChngTime = 0; /* Value of chngtime= query param, if any */
3701
+ int bApply = P("apply")!=0 && cgi_csrf_safe(2);
3702
+ int bPreview = P("preview")!=0;
36493703
char *zUuid;
36503704
Blob comment;
36513705
char *zBranchName = 0;
36523706
Stmt q;
3707
+ Blob ctrl; /* The generated control artifact */
36533708
36543709
login_check_credentials();
36553710
if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
36563711
rid = name_to_typed_rid(P("r"), "ci");
36573712
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@@ -3684,16 +3739,13 @@
36843739
zNewBrFlag = P("newbr") ? " checked" : "";
36853740
zNewBranch = PDT("brname","");
36863741
zBranchName = branch_of_rid(rid);
36873742
zCloseFlag = P("close") ? " checked" : "";
36883743
zHideFlag = P("hide") ? " checked" : "";
3689
- if( P("apply") && cgi_csrf_safe(2) ){
3690
- Blob ctrl;
3691
- char *zNow;
3692
-
3693
- blob_zero(&ctrl);
3694
- zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
3744
+ blob_zero(&ctrl);
3745
+ if( bApply || bPreview ){
3746
+ char *zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
36953747
blob_appendf(&ctrl, "D %s\n", zNow);
36963748
init_newtags();
36973749
if( zNewColorFlag[0]
36983750
&& zNewColor[0]
36993751
&& (fPropagateColor!=fNewPropagateColor
@@ -3719,69 +3771,39 @@
37193771
db_finalize(&q);
37203772
if( zHideFlag[0] ) hide_branch();
37213773
if( zCloseFlag[0] ) close_leaf(rid);
37223774
if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
37233775
if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
3724
- apply_newtags(&ctrl, rid, zUuid, 0, 0);
3725
- cgi_redirectf("%R/ci/%S", zUuid);
3776
+ construct_newtags_artifact(&ctrl, rid, zUuid, 0);
3777
+ if( bApply && artifact_is_complete(&ctrl) ){
3778
+ db_begin_transaction();
3779
+ publish_newtags_artifact(&ctrl, rid);
3780
+ db_end_transaction(0);
3781
+ blob_reset(&ctrl);
3782
+ cgi_redirectf("%R/ci/%S", zUuid);
3783
+ }
37263784
}
37273785
blob_zero(&comment);
37283786
blob_append(&comment, zNewComment, -1);
3729
- zUuid[10] = 0;
3730
- style_header("Edit Check-in [%s]", zUuid);
3731
- if( P("preview") ){
3732
- Blob suffix;
3733
- int nTag = 0;
3734
- const char *zDplyBr; /* Branch name used to determine BG color */
3735
- const char *zMainBranch = db_main_branch();
3736
- if( zNewBrFlag[0] && zNewBranch[0] ){
3737
- zDplyBr = zNewBranch;
3738
- }else{
3739
- zDplyBr = zBranchName;
3740
- }
3741
- @ <b>Preview:</b>
3742
- @ <blockquote>
3743
- @ <table border=0>
3744
- if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3745
- @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
3746
- }else if( zColor[0] ){
3747
- @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
3748
- }else if( zDplyBr && fossil_strcmp(zDplyBr, zMainBranch)!=0 ){
3749
- @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
3750
- }else{
3751
- @ <tr><td>
3752
- }
3753
- @ %!W(blob_str(&comment))
3754
- blob_zero(&suffix);
3755
- blob_appendf(&suffix, "(user: %h", zNewUser);
3756
- db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
3757
- " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
3758
- " AND tagtype>1 AND tag.tagid=tagxref.tagid",
3759
- rid);
3760
- while( db_step(&q)==SQLITE_ROW ){
3761
- const char *zTag = db_column_text(&q, 0);
3762
- if( nTag==0 ){
3763
- blob_appendf(&suffix, ", tags: %h", zTag);
3764
- }else{
3765
- blob_appendf(&suffix, ", %h", zTag);
3766
- }
3767
- nTag++;
3768
- }
3769
- db_finalize(&q);
3770
- blob_appendf(&suffix, ")");
3771
- @ %s(blob_str(&suffix))
3772
- @ </td></tr></table>
3773
- if( zChngTime ){
3774
- @ <p>The timestamp on the tag used to make the changes above
3775
- @ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
3776
- }
3777
- @ </blockquote>
3778
- @ <hr>
3779
- blob_reset(&suffix);
3780
- }
3781
- @ <p>Make changes to attributes of check-in
3782
- @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
3787
+ style_header("Edit Check-in %S", zUuid);
3788
+ @ <div class="section accordion">Original Context Around \
3789
+ @ Check-in %S(zUuid) on %s(zDate)</div>
3790
+ @ <div class="accordion_panel">
3791
+ render_checkin_context(rid, 0, 2, 0);
3792
+ @ </div>
3793
+ if( bPreview ){
3794
+ @ <div class="section accordion">After The Proposed Changes</div>
3795
+ @ <div class="accordion_panel">
3796
+ db_begin_transaction();
3797
+ publish_newtags_artifact(&ctrl, rid);
3798
+ render_checkin_context(rid, 0, 2, 0);
3799
+ db_end_transaction(1);
3800
+ @ </div>
3801
+ }
3802
+ @ <div class="section accordion">Proposed Changes For \
3803
+ @ Check-In %S(zUuid):</div>
3804
+ @ <div class="accordion_panel">
37833805
form_begin(0, "%R/ci_edit");
37843806
@ <div><input type="hidden" name="r" value="%s(zUuid)">
37853807
@ <table border="0" cellspacing="10">
37863808
37873809
@ <tr><th align="right" valign="top">User:</th>
@@ -4194,13 +4216,20 @@
41944216
fossil_free((void *)pzCancelTags);
41954217
}
41964218
if( fHide && !fHasHidden ) hide_branch();
41974219
if( fClose && !fHasClosed ) close_leaf(rid);
41984220
if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
4199
- apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
4200
- if( fDryRun==0 ){
4221
+ if( !fDryRun ) db_begin_transaction();
4222
+ construct_newtags_artifact(&ctrl, rid, zUuid, zUserOvrd);
4223
+ if( fDryRun ){
4224
+ fossil_print("%s", blob_str(&ctrl));
4225
+ blob_reset(&ctrl);
4226
+ }else{
4227
+ db_begin_transaction();
4228
+ publish_newtags_artifact(&ctrl, rid);
42014229
show_common_info(rid, "hash:", 1, 0);
4230
+ db_end_transaction(0);
42024231
}
42034232
if( g.localOpen ){
42044233
manifest_to_disk(rid);
42054234
}
42064235
}
42074236
--- src/info.c
+++ src/info.c
@@ -271,13 +271,18 @@
271 }
272 }
273
274 /*
275 ** Show the context graph (immediate parents and children) for
276 ** check-in rid and rid2
 
277 */
278 void render_checkin_context(int rid, int rid2, int parentsOnly, int mFlags){
 
 
 
 
279 Blob sql;
280 Stmt q;
281 int rx[2];
282 int i, n;
283 rx[0] = rid;
@@ -295,11 +300,11 @@
295 "INSERT OR IGNORE INTO ok VALUES(%d);"
296 "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
297 rx[i], rx[i]
298 );
299 }
300 if( !parentsOnly ){
301 for(i=0; i<n; i++){
302 db_multi_exec(
303 "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
304 );
305 if( db_table_exists("repository","cherrypick") ){
@@ -313,10 +318,13 @@
313 }
314 }
315 }
316 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
317 db_prepare(&q, "%s", blob_sql_text(&sql));
 
 
 
318 www_print_timeline(&q,
319 mFlags
320 |TIMELINE_GRAPH
321 |TIMELINE_FILLGAPS
322 |TIMELINE_NOSCROLL
@@ -3453,74 +3461,93 @@
3453 while( fossil_isspace(zA[0]) ) zA++;
3454 return zA[0]==0 && zB[0]==0;
3455 }
3456
3457 /*
3458 ** The following methods operate on the newtags temporary table
3459 ** that is used to collect various changes to be added to a control
3460 ** artifact for a check-in edit.
 
 
 
3461 */
3462 static void init_newtags(void){
3463 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
3464 }
3465
 
3466 static void change_special(
3467 const char *zName, /* Name of the special tag */
3468 const char *zOp, /* Operation prefix (e.g. +,-,*) */
3469 const char *zValue /* Value of the tag */
3470 ){
3471 db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
3472 }
3473
 
3474 static void change_sym_tag(const char *zTag, const char *zOp){
3475 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
3476 }
3477
 
3478 static void cancel_special(const char *zTag){
3479 change_special(zTag,"-",0);
3480 }
3481
 
 
3482 static void add_color(const char *zNewColor, int fPropagateColor){
3483 change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
3484 }
3485
 
3486 static void cancel_color(void){
3487 change_special("bgcolor","-",0);
3488 }
3489
 
3490 static void add_comment(const char *zNewComment){
3491 change_special("comment","+",zNewComment);
3492 }
3493
 
3494 static void add_date(const char *zNewDate){
3495 change_special("date","+",zNewDate);
3496 }
3497
 
3498 static void add_user(const char *zNewUser){
3499 change_special("user","+",zNewUser);
3500 }
3501
 
3502 static void add_tag(const char *zNewTag){
3503 change_sym_tag(zNewTag,"+");
3504 }
3505
 
3506 static void cancel_tag(int rid, const char *zCancelTag){
3507 if( db_exists("SELECT 1 FROM tagxref, tag"
3508 " WHERE tagxref.rid=%d AND tagtype>0"
3509 " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
3510 rid, zCancelTag)
3511 ) change_sym_tag(zCancelTag,"-");
 
 
3512 }
3513
 
3514 static void hide_branch(void){
3515 change_special("hidden","*",0);
3516 }
3517
 
3518 static void close_leaf(int rid){
3519 change_special("closed",is_a_leaf(rid)?"+":"*",0);
3520 }
3521
 
3522 static void change_branch(int rid, const char *zNewBranch){
3523 db_multi_exec(
3524 "REPLACE INTO newtags "
3525 " SELECT tagname, '-', NULL FROM tagxref, tag"
3526 " WHERE tagxref.rid=%d AND tagtype==2"
@@ -3531,19 +3558,31 @@
3531 change_special("branch","*",zNewBranch);
3532 change_sym_tag(zNewBranch,"*");
3533 }
3534
3535 /*
3536 ** The apply_newtags method is called after all newtags have been added
3537 ** and the control artifact is completed and then written to the DB.
 
 
 
 
 
 
 
 
 
 
 
 
 
3538 */
3539 static void apply_newtags(
3540 Blob *ctrl,
3541 int rid,
3542 const char *zUuid,
3543 const char *zUserOvrd, /* The user name on the control artifact */
3544 int fDryRun /* Print control artifact, but make no changes */
3545 ){
3546 Stmt q;
3547 int nChng = 0;
3548
3549 db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
@@ -3559,33 +3598,46 @@
3559 blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
3560 }
3561 }
3562 db_finalize(&q);
3563 if( nChng>0 ){
3564 int nrid;
3565 Blob cksum;
3566 if( zUserOvrd && zUserOvrd[0] ){
3567 blob_appendf(ctrl, "U %F\n", zUserOvrd);
3568 }else{
3569 blob_appendf(ctrl, "U %F\n", login_name());
3570 }
3571 md5sum_blob(ctrl, &cksum);
3572 blob_appendf(ctrl, "Z %b\n", &cksum);
3573 if( fDryRun ){
3574 assert( g.isHTTP==0 ); /* Only print control artifact in console mode. */
3575 fossil_print("%s", blob_str(ctrl));
3576 blob_reset(ctrl);
3577 }else{
3578 db_begin_transaction();
3579 g.markPrivate = content_is_private(rid);
3580 nrid = content_put(ctrl);
3581 manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
3582 db_end_transaction(0);
3583 }
3584 assert( blob_is_reset(ctrl) );
 
 
 
 
 
 
 
 
 
 
 
 
3585 }
3586 }
 
 
3587
3588 /*
3589 ** This method checks that the date can be parsed.
3590 ** Returns 1 if datetime() can validate, 0 otherwise.
3591 */
@@ -3644,14 +3696,17 @@
3644 int fPropagateColor; /* True if color propagates before edit */
3645 int fNewPropagateColor; /* True if color propagates after edit */
3646 int fHasHidden = 0; /* True if hidden tag already set */
3647 int fHasClosed = 0; /* True if closed tag already set */
3648 const char *zChngTime = 0; /* Value of chngtime= query param, if any */
 
 
3649 char *zUuid;
3650 Blob comment;
3651 char *zBranchName = 0;
3652 Stmt q;
 
3653
3654 login_check_credentials();
3655 if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
3656 rid = name_to_typed_rid(P("r"), "ci");
3657 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@@ -3684,16 +3739,13 @@
3684 zNewBrFlag = P("newbr") ? " checked" : "";
3685 zNewBranch = PDT("brname","");
3686 zBranchName = branch_of_rid(rid);
3687 zCloseFlag = P("close") ? " checked" : "";
3688 zHideFlag = P("hide") ? " checked" : "";
3689 if( P("apply") && cgi_csrf_safe(2) ){
3690 Blob ctrl;
3691 char *zNow;
3692
3693 blob_zero(&ctrl);
3694 zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
3695 blob_appendf(&ctrl, "D %s\n", zNow);
3696 init_newtags();
3697 if( zNewColorFlag[0]
3698 && zNewColor[0]
3699 && (fPropagateColor!=fNewPropagateColor
@@ -3719,69 +3771,39 @@
3719 db_finalize(&q);
3720 if( zHideFlag[0] ) hide_branch();
3721 if( zCloseFlag[0] ) close_leaf(rid);
3722 if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
3723 if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
3724 apply_newtags(&ctrl, rid, zUuid, 0, 0);
3725 cgi_redirectf("%R/ci/%S", zUuid);
 
 
 
 
 
 
3726 }
3727 blob_zero(&comment);
3728 blob_append(&comment, zNewComment, -1);
3729 zUuid[10] = 0;
3730 style_header("Edit Check-in [%s]", zUuid);
3731 if( P("preview") ){
3732 Blob suffix;
3733 int nTag = 0;
3734 const char *zDplyBr; /* Branch name used to determine BG color */
3735 const char *zMainBranch = db_main_branch();
3736 if( zNewBrFlag[0] && zNewBranch[0] ){
3737 zDplyBr = zNewBranch;
3738 }else{
3739 zDplyBr = zBranchName;
3740 }
3741 @ <b>Preview:</b>
3742 @ <blockquote>
3743 @ <table border=0>
3744 if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3745 @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
3746 }else if( zColor[0] ){
3747 @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
3748 }else if( zDplyBr && fossil_strcmp(zDplyBr, zMainBranch)!=0 ){
3749 @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
3750 }else{
3751 @ <tr><td>
3752 }
3753 @ %!W(blob_str(&comment))
3754 blob_zero(&suffix);
3755 blob_appendf(&suffix, "(user: %h", zNewUser);
3756 db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
3757 " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
3758 " AND tagtype>1 AND tag.tagid=tagxref.tagid",
3759 rid);
3760 while( db_step(&q)==SQLITE_ROW ){
3761 const char *zTag = db_column_text(&q, 0);
3762 if( nTag==0 ){
3763 blob_appendf(&suffix, ", tags: %h", zTag);
3764 }else{
3765 blob_appendf(&suffix, ", %h", zTag);
3766 }
3767 nTag++;
3768 }
3769 db_finalize(&q);
3770 blob_appendf(&suffix, ")");
3771 @ %s(blob_str(&suffix))
3772 @ </td></tr></table>
3773 if( zChngTime ){
3774 @ <p>The timestamp on the tag used to make the changes above
3775 @ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
3776 }
3777 @ </blockquote>
3778 @ <hr>
3779 blob_reset(&suffix);
3780 }
3781 @ <p>Make changes to attributes of check-in
3782 @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
3783 form_begin(0, "%R/ci_edit");
3784 @ <div><input type="hidden" name="r" value="%s(zUuid)">
3785 @ <table border="0" cellspacing="10">
3786
3787 @ <tr><th align="right" valign="top">User:</th>
@@ -4194,13 +4216,20 @@
4194 fossil_free((void *)pzCancelTags);
4195 }
4196 if( fHide && !fHasHidden ) hide_branch();
4197 if( fClose && !fHasClosed ) close_leaf(rid);
4198 if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
4199 apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
4200 if( fDryRun==0 ){
 
 
 
 
 
 
4201 show_common_info(rid, "hash:", 1, 0);
 
4202 }
4203 if( g.localOpen ){
4204 manifest_to_disk(rid);
4205 }
4206 }
4207
--- src/info.c
+++ src/info.c
@@ -271,13 +271,18 @@
271 }
272 }
273
274 /*
275 ** Show the context graph (immediate parents and children) for
276 ** check-in rid and rid2. rid2 might be zero, in which case only
277 ** show the context for rid1.
278 */
279 void render_checkin_context(
280 int rid, int rid2, /* One or two checkins for which to show context */
281 int mRCCFlags, /* Flags. 1: parents only. 2: no-highlight */
282 int mFlags /* Graph flags */
283 ){
284 Blob sql;
285 Stmt q;
286 int rx[2];
287 int i, n;
288 rx[0] = rid;
@@ -295,11 +300,11 @@
300 "INSERT OR IGNORE INTO ok VALUES(%d);"
301 "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
302 rx[i], rx[i]
303 );
304 }
305 if( (mRCCFlags & 0x01)==0 ){
306 for(i=0; i<n; i++){
307 db_multi_exec(
308 "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
309 );
310 if( db_table_exists("repository","cherrypick") ){
@@ -313,10 +318,13 @@
318 }
319 }
320 }
321 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
322 db_prepare(&q, "%s", blob_sql_text(&sql));
323 if( mRCCFlags & 0x02 ){
324 rid = rid2 = 0;
325 }
326 www_print_timeline(&q,
327 mFlags
328 |TIMELINE_GRAPH
329 |TIMELINE_FILLGAPS
330 |TIMELINE_NOSCROLL
@@ -3453,74 +3461,93 @@
3461 while( fossil_isspace(zA[0]) ) zA++;
3462 return zA[0]==0 && zB[0]==0;
3463 }
3464
3465 /*
3466 *****************************************************************************
3467 ** The following methods operate on the "newtags" temporary table. This
3468 ** table collects changes for a control artifact that will implement an
3469 ** edit to a check-in.
3470 **
3471 ** Initialize the newtags table
3472 */
3473 static void init_newtags(void){
3474 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
3475 }
3476
3477 /* Add a new changes to the newtags table */
3478 static void change_special(
3479 const char *zName, /* Name of the special tag */
3480 const char *zOp, /* Operation prefix (e.g. +,-,*) */
3481 const char *zValue /* Value of the tag */
3482 ){
3483 db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
3484 }
3485
3486 /* Change a symbolic tag */
3487 static void change_sym_tag(const char *zTag, const char *zOp){
3488 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
3489 }
3490
3491 /* Cancel a tag */
3492 static void cancel_special(const char *zTag){
3493 change_special(zTag,"-",0);
3494 }
3495
3496 /* Add a background color tag. This will be propagating tag
3497 ** if fPropagateColor is true. */
3498 static void add_color(const char *zNewColor, int fPropagateColor){
3499 change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
3500 }
3501
3502 /* Cancel a background color tag */
3503 static void cancel_color(void){
3504 change_special("bgcolor","-",0);
3505 }
3506
3507 /* Add a comment-change tag */
3508 static void add_comment(const char *zNewComment){
3509 change_special("comment","+",zNewComment);
3510 }
3511
3512 /* Add a date-change tag */
3513 static void add_date(const char *zNewDate){
3514 change_special("date","+",zNewDate);
3515 }
3516
3517 /* Add a change-user tag */
3518 static void add_user(const char *zNewUser){
3519 change_special("user","+",zNewUser);
3520 }
3521
3522 /* Add a generic symbolic tag (one that has no value) */
3523 static void add_tag(const char *zNewTag){
3524 change_sym_tag(zNewTag,"+");
3525 }
3526
3527 /* Cancel an existing symbolic tag associated with check-in rid */
3528 static void cancel_tag(int rid, const char *zCancelTag){
3529 if( db_exists("SELECT 1 FROM tagxref, tag"
3530 " WHERE tagxref.rid=%d AND tagtype>0"
3531 " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
3532 rid, zCancelTag)
3533 ){
3534 change_sym_tag(zCancelTag,"-");
3535 }
3536 }
3537
3538 /* Add a hidden tag that propagates to all ancestors */
3539 static void hide_branch(void){
3540 change_special("hidden","*",0);
3541 }
3542
3543 /* Close the branch */
3544 static void close_leaf(int rid){
3545 change_special("closed",is_a_leaf(rid)?"+":"*",0);
3546 }
3547
3548 /* Move the check-in to a different branch */
3549 static void change_branch(int rid, const char *zNewBranch){
3550 db_multi_exec(
3551 "REPLACE INTO newtags "
3552 " SELECT tagname, '-', NULL FROM tagxref, tag"
3553 " WHERE tagxref.rid=%d AND tagtype==2"
@@ -3531,19 +3558,31 @@
3558 change_special("branch","*",zNewBranch);
3559 change_sym_tag(zNewBranch,"*");
3560 }
3561
3562 /*
3563 ** Construct a control artifact.
3564 **
3565 ** Preconditions:
3566 **
3567 ** (1) "ctrl" contains the beginning of a control artifact with
3568 ** just the D-card showing the timestamp on the control artifact
3569 ** itself.
3570 **
3571 ** (2) The newtags temporary table has been constructed.
3572 **
3573 ** (3) Zero or more methods may have been called to populate the
3574 ** newtags table. Or the newtags table might be empty.
3575 **
3576 ** Construct the complete control artifact. Or, if there are not
3577 ** changes, just zero out the ctrl blob.
3578 */
3579 static void construct_newtags_artifact(
3580 Blob *ctrl, /* The control artifact text */
3581 int rid, /* rid of the check-in that the artifact applies to */
3582 const char *zUuid, /* UUID of the check-in to which the artifact applies*/
3583 const char *zUserOvrd /* The user name on the control artifact */
 
3584 ){
3585 Stmt q;
3586 int nChng = 0;
3587
3588 db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
@@ -3559,33 +3598,46 @@
3598 blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
3599 }
3600 }
3601 db_finalize(&q);
3602 if( nChng>0 ){
 
3603 Blob cksum;
3604 if( zUserOvrd && zUserOvrd[0] ){
3605 blob_appendf(ctrl, "U %F\n", zUserOvrd);
3606 }else{
3607 blob_appendf(ctrl, "U %F\n", login_name());
3608 }
3609 md5sum_blob(ctrl, &cksum);
3610 blob_appendf(ctrl, "Z %b\n", &cksum);
3611 blob_reset(&cksum);
3612 }else{
3613 blob_reset(ctrl);
3614 }
3615 }
3616
3617 /*
3618 ** Return true if the artifact is complete. We assume that any non-empty
3619 ** artifact is complete.
3620 */
3621 static int artifact_is_complete(Blob *ctrl){
3622 return blob_size(ctrl)>0;
3623 }
3624
3625 /*
3626 ** Apply a control artifact to the repository database. Except, if the
3627 ** artifact is empty, this routine is a no-op.
3628 */
3629 static void publish_newtags_artifact(Blob *ctrl, int rid){
3630 if( artifact_is_complete(ctrl) ){
3631 int nrid;
3632 g.markPrivate = content_is_private(rid);
3633 nrid = content_put(ctrl);
3634 manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
3635 }
3636 }
3637 /* End of the newtags subsystem
3638 ******************************************************************************/
3639
3640 /*
3641 ** This method checks that the date can be parsed.
3642 ** Returns 1 if datetime() can validate, 0 otherwise.
3643 */
@@ -3644,14 +3696,17 @@
3696 int fPropagateColor; /* True if color propagates before edit */
3697 int fNewPropagateColor; /* True if color propagates after edit */
3698 int fHasHidden = 0; /* True if hidden tag already set */
3699 int fHasClosed = 0; /* True if closed tag already set */
3700 const char *zChngTime = 0; /* Value of chngtime= query param, if any */
3701 int bApply = P("apply")!=0 && cgi_csrf_safe(2);
3702 int bPreview = P("preview")!=0;
3703 char *zUuid;
3704 Blob comment;
3705 char *zBranchName = 0;
3706 Stmt q;
3707 Blob ctrl; /* The generated control artifact */
3708
3709 login_check_credentials();
3710 if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
3711 rid = name_to_typed_rid(P("r"), "ci");
3712 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@@ -3684,16 +3739,13 @@
3739 zNewBrFlag = P("newbr") ? " checked" : "";
3740 zNewBranch = PDT("brname","");
3741 zBranchName = branch_of_rid(rid);
3742 zCloseFlag = P("close") ? " checked" : "";
3743 zHideFlag = P("hide") ? " checked" : "";
3744 blob_zero(&ctrl);
3745 if( bApply || bPreview ){
3746 char *zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
 
 
 
3747 blob_appendf(&ctrl, "D %s\n", zNow);
3748 init_newtags();
3749 if( zNewColorFlag[0]
3750 && zNewColor[0]
3751 && (fPropagateColor!=fNewPropagateColor
@@ -3719,69 +3771,39 @@
3771 db_finalize(&q);
3772 if( zHideFlag[0] ) hide_branch();
3773 if( zCloseFlag[0] ) close_leaf(rid);
3774 if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
3775 if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
3776 construct_newtags_artifact(&ctrl, rid, zUuid, 0);
3777 if( bApply && artifact_is_complete(&ctrl) ){
3778 db_begin_transaction();
3779 publish_newtags_artifact(&ctrl, rid);
3780 db_end_transaction(0);
3781 blob_reset(&ctrl);
3782 cgi_redirectf("%R/ci/%S", zUuid);
3783 }
3784 }
3785 blob_zero(&comment);
3786 blob_append(&comment, zNewComment, -1);
3787 style_header("Edit Check-in %S", zUuid);
3788 @ <div class="section accordion">Original Context Around \
3789 @ Check-in %S(zUuid) on %s(zDate)</div>
3790 @ <div class="accordion_panel">
3791 render_checkin_context(rid, 0, 2, 0);
3792 @ </div>
3793 if( bPreview ){
3794 @ <div class="section accordion">After The Proposed Changes</div>
3795 @ <div class="accordion_panel">
3796 db_begin_transaction();
3797 publish_newtags_artifact(&ctrl, rid);
3798 render_checkin_context(rid, 0, 2, 0);
3799 db_end_transaction(1);
3800 @ </div>
3801 }
3802 @ <div class="section accordion">Proposed Changes For \
3803 @ Check-In %S(zUuid):</div>
3804 @ <div class="accordion_panel">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3805 form_begin(0, "%R/ci_edit");
3806 @ <div><input type="hidden" name="r" value="%s(zUuid)">
3807 @ <table border="0" cellspacing="10">
3808
3809 @ <tr><th align="right" valign="top">User:</th>
@@ -4194,13 +4216,20 @@
4216 fossil_free((void *)pzCancelTags);
4217 }
4218 if( fHide && !fHasHidden ) hide_branch();
4219 if( fClose && !fHasClosed ) close_leaf(rid);
4220 if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
4221 if( !fDryRun ) db_begin_transaction();
4222 construct_newtags_artifact(&ctrl, rid, zUuid, zUserOvrd);
4223 if( fDryRun ){
4224 fossil_print("%s", blob_str(&ctrl));
4225 blob_reset(&ctrl);
4226 }else{
4227 db_begin_transaction();
4228 publish_newtags_artifact(&ctrl, rid);
4229 show_common_info(rid, "hash:", 1, 0);
4230 db_end_transaction(0);
4231 }
4232 if( g.localOpen ){
4233 manifest_to_disk(rid);
4234 }
4235 }
4236

Keyboard Shortcuts

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