Fossil SCM

Integrate new command [/help?cmd=amend|amend] into trunk.

andybradford 2015-08-11 04:03 trunk merge
Commit c73c95cc654b50db91fa5fe26ecc0809fb4c7870
+344 -78
--- src/info.c
+++ src/info.c
@@ -2276,10 +2276,133 @@
22762276
}
22772277
while( fossil_isspace(zB[0]) ) zB++;
22782278
while( fossil_isspace(zA[0]) ) zA++;
22792279
return zA[0]==0 && zB[0]==0;
22802280
}
2281
+
2282
+/*
2283
+** The following methods operate on the newtags temporary table
2284
+** that is used to collect various changes to be added to a control
2285
+** artifact for a check-in edit.
2286
+*/
2287
+static void init_newtags(void){
2288
+ db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
2289
+}
2290
+
2291
+static void change_special(
2292
+ const char *zName, /* Name of the special tag */
2293
+ const char *zOp, /* Operation prefix (e.g. +,-,*) */
2294
+ const char *zValue /* Value of the tag */
2295
+){
2296
+ db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
2297
+}
2298
+
2299
+static void change_sym_tag(const char *zTag, const char *zOp){
2300
+ db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
2301
+}
2302
+
2303
+static void cancel_special(const char *zTag){
2304
+ change_special(zTag,"-",0);
2305
+}
2306
+
2307
+static void add_color(const char *zNewColor, int fPropagateColor){
2308
+ change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
2309
+}
2310
+
2311
+static void cancel_color(void){
2312
+ change_special("bgcolor","-",0);
2313
+}
2314
+
2315
+static void add_comment(const char *zNewComment){
2316
+ change_special("comment","+",zNewComment);
2317
+}
2318
+
2319
+static void add_date(const char *zNewDate){
2320
+ change_special("date","+",zNewDate);
2321
+}
2322
+
2323
+static void add_user(const char *zNewUser){
2324
+ change_special("user","+",zNewUser);
2325
+}
2326
+
2327
+static void add_tag(const char *zNewTag){
2328
+ change_sym_tag(zNewTag,"+");
2329
+}
2330
+
2331
+static void cancel_tag(int rid, const char *zCancelTag){
2332
+ if( db_exists("SELECT 1 FROM tagxref, tag"
2333
+ " WHERE tagxref.rid=%d AND tagtype>0"
2334
+ " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
2335
+ rid, zCancelTag)
2336
+ ) change_sym_tag(zCancelTag,"-");
2337
+}
2338
+
2339
+static void hide_branch(void){
2340
+ change_special("hidden","*",0);
2341
+}
2342
+
2343
+static void close_leaf(int rid){
2344
+ change_special("closed",is_a_leaf(rid)?"+":"*",0);
2345
+}
2346
+
2347
+static void change_branch(int rid, const char *zNewBranch){
2348
+ db_multi_exec(
2349
+ "REPLACE INTO newtags "
2350
+ " SELECT tagname, '-', NULL FROM tagxref, tag"
2351
+ " WHERE tagxref.rid=%d AND tagtype==2"
2352
+ " AND tagname GLOB 'sym-*'"
2353
+ " AND tag.tagid=tagxref.tagid",
2354
+ rid
2355
+ );
2356
+ change_special("branch","*",zNewBranch);
2357
+ change_sym_tag(zNewBranch,"*");
2358
+}
2359
+
2360
+/*
2361
+** The apply_newtags method is called after all newtags have been added
2362
+** and the control artifact is completed and then written to the DB.
2363
+*/
2364
+static void apply_newtags(Blob *ctrl, int rid, const char *zUuid){
2365
+ Stmt q;
2366
+ int nChng = 0;
2367
+
2368
+ db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
2369
+ " ORDER BY prefix || tag");
2370
+ while( db_step(&q)==SQLITE_ROW ){
2371
+ const char *zTag = db_column_text(&q, 0);
2372
+ const char *zPrefix = db_column_text(&q, 1);
2373
+ const char *zValue = db_column_text(&q, 2);
2374
+ nChng++;
2375
+ if( zValue ){
2376
+ blob_appendf(ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue);
2377
+ }else{
2378
+ blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
2379
+ }
2380
+ }
2381
+ db_finalize(&q);
2382
+ if( nChng>0 ){
2383
+ int nrid;
2384
+ Blob cksum;
2385
+ blob_appendf(ctrl, "U %F\n", login_name());
2386
+ md5sum_blob(ctrl, &cksum);
2387
+ blob_appendf(ctrl, "Z %b\n", &cksum);
2388
+ db_begin_transaction();
2389
+ g.markPrivate = content_is_private(rid);
2390
+ nrid = content_put(ctrl);
2391
+ manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
2392
+ assert( blob_is_reset(ctrl) );
2393
+ db_end_transaction(0);
2394
+ }
2395
+}
2396
+
2397
+/*
2398
+** This method checks that the date can be parsed.
2399
+** Returns 1 if datetime() can validate, 0 otherwise.
2400
+*/
2401
+int is_datetime(const char* zDate){
2402
+ return db_int(0, "SELECT datetime(%Q) NOT NULL", zDate);
2403
+}
22812404
22822405
/*
22832406
** WEBPAGE: ci_edit
22842407
** URL: /ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
22852408
**
@@ -2360,36 +2483,19 @@
23602483
23612484
login_verify_csrf_secret();
23622485
blob_zero(&ctrl);
23632486
zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
23642487
blob_appendf(&ctrl, "D %s\n", zNow);
2365
- db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
2488
+ init_newtags();
23662489
if( zNewColor[0]
23672490
&& (fPropagateColor!=fNewPropagateColor
23682491
|| fossil_strcmp(zColor,zNewColor)!=0)
2369
- ){
2370
- char *zPrefix = "+";
2371
- if( fNewPropagateColor ){
2372
- zPrefix = "*";
2373
- }
2374
- db_multi_exec("REPLACE INTO newtags VALUES('bgcolor',%Q,%Q)",
2375
- zPrefix, zNewColor);
2376
- }
2377
- if( zNewColor[0]==0 && zColor[0]!=0 ){
2378
- db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)");
2379
- }
2380
- if( comment_compare(zComment,zNewComment)==0 ){
2381
- db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)",
2382
- zNewComment);
2383
- }
2384
- if( fossil_strcmp(zDate,zNewDate)!=0 ){
2385
- db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)",
2386
- zNewDate);
2387
- }
2388
- if( fossil_strcmp(zUser,zNewUser)!=0 ){
2389
- db_multi_exec("REPLACE INTO newtags VALUES('user','+',%Q)", zNewUser);
2390
- }
2492
+ ) add_color(zNewColor,fNewPropagateColor);
2493
+ if( zNewColor[0]==0 && zColor[0]!=0 ) cancel_color();
2494
+ if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
2495
+ if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate);
2496
+ if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser);
23912497
db_prepare(&q,
23922498
"SELECT tag.tagid, tagname FROM tagxref, tag"
23932499
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
23942500
rid
23952501
);
@@ -2396,65 +2502,18 @@
23962502
while( db_step(&q)==SQLITE_ROW ){
23972503
int tagid = db_column_int(&q, 0);
23982504
const char *zTag = db_column_text(&q, 1);
23992505
char zLabel[30];
24002506
sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
2401
- if( P(zLabel) ){
2402
- db_multi_exec("REPLACE INTO newtags VALUES(%Q,'-',NULL)", zTag);
2403
- }
2404
- }
2405
- db_finalize(&q);
2406
- if( zHideFlag[0] ){
2407
- db_multi_exec("REPLACE INTO newtags VALUES('hidden','*',NULL)");
2408
- }
2409
- if( zCloseFlag[0] ){
2410
- db_multi_exec("REPLACE INTO newtags VALUES('closed','%s',NULL)",
2411
- is_a_leaf(rid)?"+":"*");
2412
- }
2413
- if( zNewTagFlag[0] && zNewTag[0] ){
2414
- db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','+',NULL)", zNewTag);
2415
- }
2416
- if( zNewBrFlag[0] && zNewBranch[0] ){
2417
- db_multi_exec(
2418
- "REPLACE INTO newtags "
2419
- " SELECT tagname, '-', NULL FROM tagxref, tag"
2420
- " WHERE tagxref.rid=%d AND tagtype==2"
2421
- " AND tagname GLOB 'sym-*'"
2422
- " AND tag.tagid=tagxref.tagid",
2423
- rid
2424
- );
2425
- db_multi_exec("REPLACE INTO newtags VALUES('branch','*',%Q)", zNewBranch);
2426
- db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','*',NULL)",
2427
- zNewBranch);
2428
- }
2429
- db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
2430
- " ORDER BY prefix || tag");
2431
- while( db_step(&q)==SQLITE_ROW ){
2432
- const char *zTag = db_column_text(&q, 0);
2433
- const char *zPrefix = db_column_text(&q, 1);
2434
- const char *zValue = db_column_text(&q, 2);
2435
- nChng++;
2436
- if( zValue ){
2437
- blob_appendf(&ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue);
2438
- }else{
2439
- blob_appendf(&ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
2440
- }
2441
- }
2442
- db_finalize(&q);
2443
- if( nChng>0 ){
2444
- int nrid;
2445
- Blob cksum;
2446
- blob_appendf(&ctrl, "U %F\n", login_name());
2447
- md5sum_blob(&ctrl, &cksum);
2448
- blob_appendf(&ctrl, "Z %b\n", &cksum);
2449
- db_begin_transaction();
2450
- g.markPrivate = content_is_private(rid);
2451
- nrid = content_put(&ctrl);
2452
- manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS);
2453
- assert( blob_is_reset(&ctrl) );
2454
- db_end_transaction(0);
2455
- }
2507
+ if( P(zLabel) ) cancel_special(zTag);
2508
+ }
2509
+ db_finalize(&q);
2510
+ if( zHideFlag[0] ) hide_branch();
2511
+ if( zCloseFlag[0] ) close_leaf(rid);
2512
+ if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
2513
+ if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
2514
+ apply_newtags(&ctrl, rid, zUuid);
24562515
cgi_redirectf("ci?name=%s", zUuid);
24572516
}
24582517
blob_zero(&comment);
24592518
blob_append(&comment, zNewComment, -1);
24602519
zUuid[10] = 0;
@@ -2652,5 +2711,212 @@
26522711
@ </td></tr>
26532712
@ </table>
26542713
@ </div></form>
26552714
style_footer();
26562715
}
2716
+
2717
+/*
2718
+** Prepare an ammended commit comment. Let the user modify it using the
2719
+** editor specified in the global_config table or either
2720
+** the VISUAL or EDITOR environment variable.
2721
+**
2722
+** Store the final commit comment in pComment. pComment is assumed
2723
+** to be uninitialized - any prior content is overwritten.
2724
+**
2725
+** Use zInit to initialize the check-in comment so that the user does
2726
+** not have to retype.
2727
+*/
2728
+static void prepare_amend_comment(
2729
+ Blob *pComment,
2730
+ const char *zInit,
2731
+ const char *zUuid
2732
+){
2733
+ Blob prompt;
2734
+#if defined(_WIN32) || defined(__CYGWIN__)
2735
+ int bomSize;
2736
+ const unsigned char *bom = get_utf8_bom(&bomSize);
2737
+ blob_init(&prompt, (const char *) bom, bomSize);
2738
+ if( zInit && zInit[0]){
2739
+ blob_append(&prompt, zInit, -1);
2740
+ }
2741
+#else
2742
+ blob_init(&prompt, zInit, -1);
2743
+#endif
2744
+ blob_append(&prompt, "\n# Enter a new comment for check-in ", -1);
2745
+ if( zUuid && zUuid[0] ){
2746
+ blob_append(&prompt, zUuid, -1);
2747
+ }
2748
+ blob_append(&prompt, ".\n# Lines beginning with a # are ignored.\n", -1);
2749
+ prompt_for_user_comment(pComment, &prompt);
2750
+ blob_reset(&prompt);
2751
+}
2752
+
2753
+#define AMEND_USAGE_STMT "UUID OPTION ?OPTION ...?"
2754
+/*
2755
+** COMMAND: amend
2756
+**
2757
+** Usage: %fossil amend UUID OPTION ?OPTION ...?
2758
+**
2759
+** Amend the tags on check-in UUID to change how it displays in the timeline.
2760
+**
2761
+** Options:
2762
+**
2763
+** --author USER Make USER the author for check-in
2764
+** -m|--comment COMMENT Make COMMENT the check-in comment
2765
+** -M|--message-file FILE Read the amended comment from FILE
2766
+** --edit-comment Launch editor to revise comment
2767
+** --date DATE Make DATE the check-in time
2768
+** --bgcolor COLOR Apply COLOR to this check-in
2769
+** --branchcolor COLOR Apply and propagate COLOR to the branch
2770
+** --tag TAG Add new TAG to this check-in
2771
+** --cancel TAG Cancel TAG from this check-in
2772
+** --branch NAME Make this check-in the start of branch NAME
2773
+** --hide Hide branch starting from this check-in
2774
+** --close Mark this "leaf" as closed
2775
+*/
2776
+void ci_amend_cmd(void){
2777
+ int rid;
2778
+ const char *zComment; /* Current comment on the check-in */
2779
+ const char *zNewComment; /* Revised check-in comment */
2780
+ const char *zComFile; /* Filename from which to read comment */
2781
+ const char *zUser; /* Current user for the check-in */
2782
+ const char *zNewUser; /* Revised user */
2783
+ const char *zDate; /* Current date of the check-in */
2784
+ const char *zNewDate; /* Revised check-in date */
2785
+ const char *zColor;
2786
+ const char *zNewColor;
2787
+ const char *zNewBrColor;
2788
+ const char *zNewBranch;
2789
+ const char **pzNewTags = 0;
2790
+ const char **pzCancelTags = 0;
2791
+ int fClose; /* True if leaf should be closed */
2792
+ int fHide; /* True if branch should be hidden */
2793
+ int fPropagateColor; /* True if color propagates before amend */
2794
+ int fNewPropagateColor = 0; /* True if color propagates after amend */
2795
+ int fHasHidden = 0; /* True if hidden tag already set */
2796
+ int fHasClosed = 0; /* True if closed tag already set */
2797
+ int fEditComment; /* True if editor to be used for comment */
2798
+ const char *zChngTime; /* The change time on the control artifact */
2799
+ const char *zUuid;
2800
+ Blob ctrl;
2801
+ Blob comment;
2802
+ char *zNow;
2803
+ int nTags, nCancels;
2804
+ int i;
2805
+ Stmt q;
2806
+
2807
+ if( g.argc==3 ) usage(AMEND_USAGE_STMT);
2808
+ fEditComment = find_option("edit-comment",0,0)!=0;
2809
+ zNewComment = find_option("comment","m",1);
2810
+ zComFile = find_option("message-file","M",1);
2811
+ zNewBranch = find_option("branch",0,1);
2812
+ zNewColor = find_option("bgcolor",0,1);
2813
+ zNewBrColor = find_option("branchcolor",0,1);
2814
+ if( zNewBrColor ){
2815
+ zNewColor = zNewBrColor;
2816
+ fNewPropagateColor = 1;
2817
+ }
2818
+ zNewDate = find_option("date",0,1);
2819
+ zNewUser = find_option("author",0,1);
2820
+ pzNewTags = find_repeatable_option("tag",0,&nTags);
2821
+ pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
2822
+ fClose = find_option("close",0,0)!=0;
2823
+ fHide = find_option("hide",0,0)!=0;
2824
+ zChngTime = find_option("chngtime",0,1);
2825
+ db_find_and_open_repository(0,0);
2826
+ user_select();
2827
+ verify_all_options();
2828
+ if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
2829
+ rid = name_to_typed_rid(g.argv[2], "ci");
2830
+ if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
2831
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
2832
+ if( zUuid==0 ) fossil_fatal("Unable to find UUID");
2833
+ zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
2834
+ " FROM event WHERE objid=%d", rid);
2835
+ zUser = db_text(0, "SELECT coalesce(euser,user)"
2836
+ " FROM event WHERE objid=%d", rid);
2837
+ zDate = db_text(0, "SELECT datetime(mtime)"
2838
+ " FROM event WHERE objid=%d", rid);
2839
+ zColor = db_text("", "SELECT bgcolor"
2840
+ " FROM event WHERE objid=%d", rid);
2841
+ fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
2842
+ " WHERE rid=%d AND tagid=%d",
2843
+ rid, TAG_BGCOLOR)==2;
2844
+ fNewPropagateColor = zNewColor && zNewColor[0]
2845
+ ? fNewPropagateColor : fPropagateColor;
2846
+ db_prepare(&q,
2847
+ "SELECT tag.tagid FROM tagxref, tag"
2848
+ " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
2849
+ rid
2850
+ );
2851
+ while( db_step(&q)==SQLITE_ROW ){
2852
+ int tagid = db_column_int(&q, 0);
2853
+
2854
+ if( tagid == TAG_CLOSED ){
2855
+ fHasClosed = 1;
2856
+ }else if( tagid==TAG_HIDDEN ){
2857
+ fHasHidden = 1;
2858
+ }else{
2859
+ continue;
2860
+ }
2861
+ }
2862
+ db_finalize(&q);
2863
+ blob_zero(&ctrl);
2864
+ zNow = date_in_standard_format(zChngTime && zChngTime[0] ? zChngTime : "now");
2865
+ blob_appendf(&ctrl, "D %s\n", zNow);
2866
+ init_newtags();
2867
+ if( zNewColor && zNewColor[0]
2868
+ && (fPropagateColor!=fNewPropagateColor
2869
+ || fossil_strcmp(zColor,zNewColor)!=0)
2870
+ ){
2871
+ add_color(
2872
+ mprintf("%s%s", (zNewColor[0]!='#' &&
2873
+ validate16(zNewColor,strlen(zNewColor)) &&
2874
+ (strlen(zNewColor)==6 || strlen(zNewColor)==3)) ? "#" : "",
2875
+ zNewColor
2876
+ ),
2877
+ fNewPropagateColor
2878
+ );
2879
+ }
2880
+ if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
2881
+ cancel_color();
2882
+ }
2883
+ if( fEditComment ){
2884
+ prepare_amend_comment(&comment, zComment, zUuid);
2885
+ zNewComment = blob_str(&comment);
2886
+ }else if( zComFile ){
2887
+ blob_zero(&comment);
2888
+ blob_read_from_file(&comment, zComFile);
2889
+ blob_to_utf8_no_bom(&comment, 1);
2890
+ zNewComment = blob_str(&comment);
2891
+ }
2892
+ if( zNewComment && zNewComment[0]
2893
+ && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
2894
+ if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
2895
+ if( is_datetime(zNewDate) ){
2896
+ add_date(zNewDate);
2897
+ }else{
2898
+ fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
2899
+ }
2900
+ }
2901
+ if( zNewUser && zNewUser[0] && fossil_strcmp(zUser,zNewUser)!=0 ){
2902
+ add_user(zNewUser);
2903
+ }
2904
+ if( pzNewTags!=0 ){
2905
+ for(i=0; i<nTags; i++){
2906
+ if( pzNewTags[i] && pzNewTags[i][0] ) add_tag(pzNewTags[i]);
2907
+ }
2908
+ fossil_free(pzNewTags);
2909
+ }
2910
+ if( pzCancelTags!=0 ){
2911
+ for(i=0; i<nCancels; i++){
2912
+ if( pzCancelTags[i] && pzCancelTags[i][0] )
2913
+ cancel_tag(rid,pzCancelTags[i]);
2914
+ }
2915
+ fossil_free(pzCancelTags);
2916
+ }
2917
+ if( fHide && !fHasHidden ) hide_branch();
2918
+ if( fClose && !fHasClosed ) close_leaf(rid);
2919
+ if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
2920
+ apply_newtags(&ctrl, rid, zUuid);
2921
+ show_common_info(rid, "uuid:", 1, 0);
2922
+}
26572923
--- src/info.c
+++ src/info.c
@@ -2276,10 +2276,133 @@
2276 }
2277 while( fossil_isspace(zB[0]) ) zB++;
2278 while( fossil_isspace(zA[0]) ) zA++;
2279 return zA[0]==0 && zB[0]==0;
2280 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2281
2282 /*
2283 ** WEBPAGE: ci_edit
2284 ** URL: /ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
2285 **
@@ -2360,36 +2483,19 @@
2360
2361 login_verify_csrf_secret();
2362 blob_zero(&ctrl);
2363 zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
2364 blob_appendf(&ctrl, "D %s\n", zNow);
2365 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
2366 if( zNewColor[0]
2367 && (fPropagateColor!=fNewPropagateColor
2368 || fossil_strcmp(zColor,zNewColor)!=0)
2369 ){
2370 char *zPrefix = "+";
2371 if( fNewPropagateColor ){
2372 zPrefix = "*";
2373 }
2374 db_multi_exec("REPLACE INTO newtags VALUES('bgcolor',%Q,%Q)",
2375 zPrefix, zNewColor);
2376 }
2377 if( zNewColor[0]==0 && zColor[0]!=0 ){
2378 db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)");
2379 }
2380 if( comment_compare(zComment,zNewComment)==0 ){
2381 db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)",
2382 zNewComment);
2383 }
2384 if( fossil_strcmp(zDate,zNewDate)!=0 ){
2385 db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)",
2386 zNewDate);
2387 }
2388 if( fossil_strcmp(zUser,zNewUser)!=0 ){
2389 db_multi_exec("REPLACE INTO newtags VALUES('user','+',%Q)", zNewUser);
2390 }
2391 db_prepare(&q,
2392 "SELECT tag.tagid, tagname FROM tagxref, tag"
2393 " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
2394 rid
2395 );
@@ -2396,65 +2502,18 @@
2396 while( db_step(&q)==SQLITE_ROW ){
2397 int tagid = db_column_int(&q, 0);
2398 const char *zTag = db_column_text(&q, 1);
2399 char zLabel[30];
2400 sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
2401 if( P(zLabel) ){
2402 db_multi_exec("REPLACE INTO newtags VALUES(%Q,'-',NULL)", zTag);
2403 }
2404 }
2405 db_finalize(&q);
2406 if( zHideFlag[0] ){
2407 db_multi_exec("REPLACE INTO newtags VALUES('hidden','*',NULL)");
2408 }
2409 if( zCloseFlag[0] ){
2410 db_multi_exec("REPLACE INTO newtags VALUES('closed','%s',NULL)",
2411 is_a_leaf(rid)?"+":"*");
2412 }
2413 if( zNewTagFlag[0] && zNewTag[0] ){
2414 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','+',NULL)", zNewTag);
2415 }
2416 if( zNewBrFlag[0] && zNewBranch[0] ){
2417 db_multi_exec(
2418 "REPLACE INTO newtags "
2419 " SELECT tagname, '-', NULL FROM tagxref, tag"
2420 " WHERE tagxref.rid=%d AND tagtype==2"
2421 " AND tagname GLOB 'sym-*'"
2422 " AND tag.tagid=tagxref.tagid",
2423 rid
2424 );
2425 db_multi_exec("REPLACE INTO newtags VALUES('branch','*',%Q)", zNewBranch);
2426 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','*',NULL)",
2427 zNewBranch);
2428 }
2429 db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
2430 " ORDER BY prefix || tag");
2431 while( db_step(&q)==SQLITE_ROW ){
2432 const char *zTag = db_column_text(&q, 0);
2433 const char *zPrefix = db_column_text(&q, 1);
2434 const char *zValue = db_column_text(&q, 2);
2435 nChng++;
2436 if( zValue ){
2437 blob_appendf(&ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue);
2438 }else{
2439 blob_appendf(&ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
2440 }
2441 }
2442 db_finalize(&q);
2443 if( nChng>0 ){
2444 int nrid;
2445 Blob cksum;
2446 blob_appendf(&ctrl, "U %F\n", login_name());
2447 md5sum_blob(&ctrl, &cksum);
2448 blob_appendf(&ctrl, "Z %b\n", &cksum);
2449 db_begin_transaction();
2450 g.markPrivate = content_is_private(rid);
2451 nrid = content_put(&ctrl);
2452 manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS);
2453 assert( blob_is_reset(&ctrl) );
2454 db_end_transaction(0);
2455 }
2456 cgi_redirectf("ci?name=%s", zUuid);
2457 }
2458 blob_zero(&comment);
2459 blob_append(&comment, zNewComment, -1);
2460 zUuid[10] = 0;
@@ -2652,5 +2711,212 @@
2652 @ </td></tr>
2653 @ </table>
2654 @ </div></form>
2655 style_footer();
2656 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2657
--- src/info.c
+++ src/info.c
@@ -2276,10 +2276,133 @@
2276 }
2277 while( fossil_isspace(zB[0]) ) zB++;
2278 while( fossil_isspace(zA[0]) ) zA++;
2279 return zA[0]==0 && zB[0]==0;
2280 }
2281
2282 /*
2283 ** The following methods operate on the newtags temporary table
2284 ** that is used to collect various changes to be added to a control
2285 ** artifact for a check-in edit.
2286 */
2287 static void init_newtags(void){
2288 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
2289 }
2290
2291 static void change_special(
2292 const char *zName, /* Name of the special tag */
2293 const char *zOp, /* Operation prefix (e.g. +,-,*) */
2294 const char *zValue /* Value of the tag */
2295 ){
2296 db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
2297 }
2298
2299 static void change_sym_tag(const char *zTag, const char *zOp){
2300 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
2301 }
2302
2303 static void cancel_special(const char *zTag){
2304 change_special(zTag,"-",0);
2305 }
2306
2307 static void add_color(const char *zNewColor, int fPropagateColor){
2308 change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
2309 }
2310
2311 static void cancel_color(void){
2312 change_special("bgcolor","-",0);
2313 }
2314
2315 static void add_comment(const char *zNewComment){
2316 change_special("comment","+",zNewComment);
2317 }
2318
2319 static void add_date(const char *zNewDate){
2320 change_special("date","+",zNewDate);
2321 }
2322
2323 static void add_user(const char *zNewUser){
2324 change_special("user","+",zNewUser);
2325 }
2326
2327 static void add_tag(const char *zNewTag){
2328 change_sym_tag(zNewTag,"+");
2329 }
2330
2331 static void cancel_tag(int rid, const char *zCancelTag){
2332 if( db_exists("SELECT 1 FROM tagxref, tag"
2333 " WHERE tagxref.rid=%d AND tagtype>0"
2334 " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
2335 rid, zCancelTag)
2336 ) change_sym_tag(zCancelTag,"-");
2337 }
2338
2339 static void hide_branch(void){
2340 change_special("hidden","*",0);
2341 }
2342
2343 static void close_leaf(int rid){
2344 change_special("closed",is_a_leaf(rid)?"+":"*",0);
2345 }
2346
2347 static void change_branch(int rid, const char *zNewBranch){
2348 db_multi_exec(
2349 "REPLACE INTO newtags "
2350 " SELECT tagname, '-', NULL FROM tagxref, tag"
2351 " WHERE tagxref.rid=%d AND tagtype==2"
2352 " AND tagname GLOB 'sym-*'"
2353 " AND tag.tagid=tagxref.tagid",
2354 rid
2355 );
2356 change_special("branch","*",zNewBranch);
2357 change_sym_tag(zNewBranch,"*");
2358 }
2359
2360 /*
2361 ** The apply_newtags method is called after all newtags have been added
2362 ** and the control artifact is completed and then written to the DB.
2363 */
2364 static void apply_newtags(Blob *ctrl, int rid, const char *zUuid){
2365 Stmt q;
2366 int nChng = 0;
2367
2368 db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
2369 " ORDER BY prefix || tag");
2370 while( db_step(&q)==SQLITE_ROW ){
2371 const char *zTag = db_column_text(&q, 0);
2372 const char *zPrefix = db_column_text(&q, 1);
2373 const char *zValue = db_column_text(&q, 2);
2374 nChng++;
2375 if( zValue ){
2376 blob_appendf(ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue);
2377 }else{
2378 blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
2379 }
2380 }
2381 db_finalize(&q);
2382 if( nChng>0 ){
2383 int nrid;
2384 Blob cksum;
2385 blob_appendf(ctrl, "U %F\n", login_name());
2386 md5sum_blob(ctrl, &cksum);
2387 blob_appendf(ctrl, "Z %b\n", &cksum);
2388 db_begin_transaction();
2389 g.markPrivate = content_is_private(rid);
2390 nrid = content_put(ctrl);
2391 manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
2392 assert( blob_is_reset(ctrl) );
2393 db_end_transaction(0);
2394 }
2395 }
2396
2397 /*
2398 ** This method checks that the date can be parsed.
2399 ** Returns 1 if datetime() can validate, 0 otherwise.
2400 */
2401 int is_datetime(const char* zDate){
2402 return db_int(0, "SELECT datetime(%Q) NOT NULL", zDate);
2403 }
2404
2405 /*
2406 ** WEBPAGE: ci_edit
2407 ** URL: /ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
2408 **
@@ -2360,36 +2483,19 @@
2483
2484 login_verify_csrf_secret();
2485 blob_zero(&ctrl);
2486 zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
2487 blob_appendf(&ctrl, "D %s\n", zNow);
2488 init_newtags();
2489 if( zNewColor[0]
2490 && (fPropagateColor!=fNewPropagateColor
2491 || fossil_strcmp(zColor,zNewColor)!=0)
2492 ) add_color(zNewColor,fNewPropagateColor);
2493 if( zNewColor[0]==0 && zColor[0]!=0 ) cancel_color();
2494 if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
2495 if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate);
2496 if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2497 db_prepare(&q,
2498 "SELECT tag.tagid, tagname FROM tagxref, tag"
2499 " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
2500 rid
2501 );
@@ -2396,65 +2502,18 @@
2502 while( db_step(&q)==SQLITE_ROW ){
2503 int tagid = db_column_int(&q, 0);
2504 const char *zTag = db_column_text(&q, 1);
2505 char zLabel[30];
2506 sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
2507 if( P(zLabel) ) cancel_special(zTag);
2508 }
2509 db_finalize(&q);
2510 if( zHideFlag[0] ) hide_branch();
2511 if( zCloseFlag[0] ) close_leaf(rid);
2512 if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
2513 if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
2514 apply_newtags(&ctrl, rid, zUuid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2515 cgi_redirectf("ci?name=%s", zUuid);
2516 }
2517 blob_zero(&comment);
2518 blob_append(&comment, zNewComment, -1);
2519 zUuid[10] = 0;
@@ -2652,5 +2711,212 @@
2711 @ </td></tr>
2712 @ </table>
2713 @ </div></form>
2714 style_footer();
2715 }
2716
2717 /*
2718 ** Prepare an ammended commit comment. Let the user modify it using the
2719 ** editor specified in the global_config table or either
2720 ** the VISUAL or EDITOR environment variable.
2721 **
2722 ** Store the final commit comment in pComment. pComment is assumed
2723 ** to be uninitialized - any prior content is overwritten.
2724 **
2725 ** Use zInit to initialize the check-in comment so that the user does
2726 ** not have to retype.
2727 */
2728 static void prepare_amend_comment(
2729 Blob *pComment,
2730 const char *zInit,
2731 const char *zUuid
2732 ){
2733 Blob prompt;
2734 #if defined(_WIN32) || defined(__CYGWIN__)
2735 int bomSize;
2736 const unsigned char *bom = get_utf8_bom(&bomSize);
2737 blob_init(&prompt, (const char *) bom, bomSize);
2738 if( zInit && zInit[0]){
2739 blob_append(&prompt, zInit, -1);
2740 }
2741 #else
2742 blob_init(&prompt, zInit, -1);
2743 #endif
2744 blob_append(&prompt, "\n# Enter a new comment for check-in ", -1);
2745 if( zUuid && zUuid[0] ){
2746 blob_append(&prompt, zUuid, -1);
2747 }
2748 blob_append(&prompt, ".\n# Lines beginning with a # are ignored.\n", -1);
2749 prompt_for_user_comment(pComment, &prompt);
2750 blob_reset(&prompt);
2751 }
2752
2753 #define AMEND_USAGE_STMT "UUID OPTION ?OPTION ...?"
2754 /*
2755 ** COMMAND: amend
2756 **
2757 ** Usage: %fossil amend UUID OPTION ?OPTION ...?
2758 **
2759 ** Amend the tags on check-in UUID to change how it displays in the timeline.
2760 **
2761 ** Options:
2762 **
2763 ** --author USER Make USER the author for check-in
2764 ** -m|--comment COMMENT Make COMMENT the check-in comment
2765 ** -M|--message-file FILE Read the amended comment from FILE
2766 ** --edit-comment Launch editor to revise comment
2767 ** --date DATE Make DATE the check-in time
2768 ** --bgcolor COLOR Apply COLOR to this check-in
2769 ** --branchcolor COLOR Apply and propagate COLOR to the branch
2770 ** --tag TAG Add new TAG to this check-in
2771 ** --cancel TAG Cancel TAG from this check-in
2772 ** --branch NAME Make this check-in the start of branch NAME
2773 ** --hide Hide branch starting from this check-in
2774 ** --close Mark this "leaf" as closed
2775 */
2776 void ci_amend_cmd(void){
2777 int rid;
2778 const char *zComment; /* Current comment on the check-in */
2779 const char *zNewComment; /* Revised check-in comment */
2780 const char *zComFile; /* Filename from which to read comment */
2781 const char *zUser; /* Current user for the check-in */
2782 const char *zNewUser; /* Revised user */
2783 const char *zDate; /* Current date of the check-in */
2784 const char *zNewDate; /* Revised check-in date */
2785 const char *zColor;
2786 const char *zNewColor;
2787 const char *zNewBrColor;
2788 const char *zNewBranch;
2789 const char **pzNewTags = 0;
2790 const char **pzCancelTags = 0;
2791 int fClose; /* True if leaf should be closed */
2792 int fHide; /* True if branch should be hidden */
2793 int fPropagateColor; /* True if color propagates before amend */
2794 int fNewPropagateColor = 0; /* True if color propagates after amend */
2795 int fHasHidden = 0; /* True if hidden tag already set */
2796 int fHasClosed = 0; /* True if closed tag already set */
2797 int fEditComment; /* True if editor to be used for comment */
2798 const char *zChngTime; /* The change time on the control artifact */
2799 const char *zUuid;
2800 Blob ctrl;
2801 Blob comment;
2802 char *zNow;
2803 int nTags, nCancels;
2804 int i;
2805 Stmt q;
2806
2807 if( g.argc==3 ) usage(AMEND_USAGE_STMT);
2808 fEditComment = find_option("edit-comment",0,0)!=0;
2809 zNewComment = find_option("comment","m",1);
2810 zComFile = find_option("message-file","M",1);
2811 zNewBranch = find_option("branch",0,1);
2812 zNewColor = find_option("bgcolor",0,1);
2813 zNewBrColor = find_option("branchcolor",0,1);
2814 if( zNewBrColor ){
2815 zNewColor = zNewBrColor;
2816 fNewPropagateColor = 1;
2817 }
2818 zNewDate = find_option("date",0,1);
2819 zNewUser = find_option("author",0,1);
2820 pzNewTags = find_repeatable_option("tag",0,&nTags);
2821 pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
2822 fClose = find_option("close",0,0)!=0;
2823 fHide = find_option("hide",0,0)!=0;
2824 zChngTime = find_option("chngtime",0,1);
2825 db_find_and_open_repository(0,0);
2826 user_select();
2827 verify_all_options();
2828 if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
2829 rid = name_to_typed_rid(g.argv[2], "ci");
2830 if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
2831 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
2832 if( zUuid==0 ) fossil_fatal("Unable to find UUID");
2833 zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
2834 " FROM event WHERE objid=%d", rid);
2835 zUser = db_text(0, "SELECT coalesce(euser,user)"
2836 " FROM event WHERE objid=%d", rid);
2837 zDate = db_text(0, "SELECT datetime(mtime)"
2838 " FROM event WHERE objid=%d", rid);
2839 zColor = db_text("", "SELECT bgcolor"
2840 " FROM event WHERE objid=%d", rid);
2841 fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref"
2842 " WHERE rid=%d AND tagid=%d",
2843 rid, TAG_BGCOLOR)==2;
2844 fNewPropagateColor = zNewColor && zNewColor[0]
2845 ? fNewPropagateColor : fPropagateColor;
2846 db_prepare(&q,
2847 "SELECT tag.tagid FROM tagxref, tag"
2848 " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid",
2849 rid
2850 );
2851 while( db_step(&q)==SQLITE_ROW ){
2852 int tagid = db_column_int(&q, 0);
2853
2854 if( tagid == TAG_CLOSED ){
2855 fHasClosed = 1;
2856 }else if( tagid==TAG_HIDDEN ){
2857 fHasHidden = 1;
2858 }else{
2859 continue;
2860 }
2861 }
2862 db_finalize(&q);
2863 blob_zero(&ctrl);
2864 zNow = date_in_standard_format(zChngTime && zChngTime[0] ? zChngTime : "now");
2865 blob_appendf(&ctrl, "D %s\n", zNow);
2866 init_newtags();
2867 if( zNewColor && zNewColor[0]
2868 && (fPropagateColor!=fNewPropagateColor
2869 || fossil_strcmp(zColor,zNewColor)!=0)
2870 ){
2871 add_color(
2872 mprintf("%s%s", (zNewColor[0]!='#' &&
2873 validate16(zNewColor,strlen(zNewColor)) &&
2874 (strlen(zNewColor)==6 || strlen(zNewColor)==3)) ? "#" : "",
2875 zNewColor
2876 ),
2877 fNewPropagateColor
2878 );
2879 }
2880 if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
2881 cancel_color();
2882 }
2883 if( fEditComment ){
2884 prepare_amend_comment(&comment, zComment, zUuid);
2885 zNewComment = blob_str(&comment);
2886 }else if( zComFile ){
2887 blob_zero(&comment);
2888 blob_read_from_file(&comment, zComFile);
2889 blob_to_utf8_no_bom(&comment, 1);
2890 zNewComment = blob_str(&comment);
2891 }
2892 if( zNewComment && zNewComment[0]
2893 && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);
2894 if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
2895 if( is_datetime(zNewDate) ){
2896 add_date(zNewDate);
2897 }else{
2898 fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
2899 }
2900 }
2901 if( zNewUser && zNewUser[0] && fossil_strcmp(zUser,zNewUser)!=0 ){
2902 add_user(zNewUser);
2903 }
2904 if( pzNewTags!=0 ){
2905 for(i=0; i<nTags; i++){
2906 if( pzNewTags[i] && pzNewTags[i][0] ) add_tag(pzNewTags[i]);
2907 }
2908 fossil_free(pzNewTags);
2909 }
2910 if( pzCancelTags!=0 ){
2911 for(i=0; i<nCancels; i++){
2912 if( pzCancelTags[i] && pzCancelTags[i][0] )
2913 cancel_tag(rid,pzCancelTags[i]);
2914 }
2915 fossil_free(pzCancelTags);
2916 }
2917 if( fHide && !fHasHidden ) hide_branch();
2918 if( fClose && !fHasClosed ) close_leaf(rid);
2919 if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
2920 apply_newtags(&ctrl, rid, zUuid);
2921 show_common_info(rid, "uuid:", 1, 0);
2922 }
2923
+34
--- src/main.c
+++ src/main.c
@@ -876,10 +876,44 @@
876876
break;
877877
}
878878
}
879879
return zReturn;
880880
}
881
+
882
+/*
883
+** Look for multiple occurrences of a command-line option with the
884
+** corresponding argument.
885
+**
886
+** Return a malloc allocated array of pointers to the arguments.
887
+**
888
+** pnUsedArgs is used to store the number of matched arguments.
889
+**
890
+** Caller is responsible to free allocated memory.
891
+*/
892
+const char **find_repeatable_option(
893
+ const char *zLong,
894
+ const char *zShort,
895
+ int *pnUsedArgs
896
+){
897
+ const char *zOption;
898
+ const char **pzArgs = 0;
899
+ int nAllocArgs = 0;
900
+ int nUsedArgs = 0;
901
+
902
+ while( zOption = find_option(zLong, zShort, 1) ){
903
+ if( pzArgs==0 && nAllocArgs==0 ){
904
+ nAllocArgs = 1;
905
+ pzArgs = fossil_malloc( nAllocArgs*sizeof(pzArgs[0]) );
906
+ }else if( nAllocArgs<=nUsedArgs ){
907
+ nAllocArgs = nAllocArgs*2;
908
+ pzArgs = fossil_realloc( pzArgs, nAllocArgs*sizeof(pzArgs[0]) );
909
+ }
910
+ pzArgs[nUsedArgs++] = zOption;
911
+ }
912
+ *pnUsedArgs = nUsedArgs;
913
+ return pzArgs;
914
+}
881915
882916
/*
883917
** Look for a repository command-line option. If present, [re-]cache it in
884918
** the global state and return the new pointer, freeing any previous value.
885919
** If absent and there is no cached value, return NULL.
886920
887921
ADDED test/amend.test
--- src/main.c
+++ src/main.c
@@ -876,10 +876,44 @@
876 break;
877 }
878 }
879 return zReturn;
880 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
881
882 /*
883 ** Look for a repository command-line option. If present, [re-]cache it in
884 ** the global state and return the new pointer, freeing any previous value.
885 ** If absent and there is no cached value, return NULL.
886
887 DDED test/amend.test
--- src/main.c
+++ src/main.c
@@ -876,10 +876,44 @@
876 break;
877 }
878 }
879 return zReturn;
880 }
881
882 /*
883 ** Look for multiple occurrences of a command-line option with the
884 ** corresponding argument.
885 **
886 ** Return a malloc allocated array of pointers to the arguments.
887 **
888 ** pnUsedArgs is used to store the number of matched arguments.
889 **
890 ** Caller is responsible to free allocated memory.
891 */
892 const char **find_repeatable_option(
893 const char *zLong,
894 const char *zShort,
895 int *pnUsedArgs
896 ){
897 const char *zOption;
898 const char **pzArgs = 0;
899 int nAllocArgs = 0;
900 int nUsedArgs = 0;
901
902 while( zOption = find_option(zLong, zShort, 1) ){
903 if( pzArgs==0 && nAllocArgs==0 ){
904 nAllocArgs = 1;
905 pzArgs = fossil_malloc( nAllocArgs*sizeof(pzArgs[0]) );
906 }else if( nAllocArgs<=nUsedArgs ){
907 nAllocArgs = nAllocArgs*2;
908 pzArgs = fossil_realloc( pzArgs, nAllocArgs*sizeof(pzArgs[0]) );
909 }
910 pzArgs[nUsedArgs++] = zOption;
911 }
912 *pnUsedArgs = nUsedArgs;
913 return pzArgs;
914 }
915
916 /*
917 ** Look for a repository command-line option. If present, [re-]cache it in
918 ** the global state and return the new pointer, freeing any previous value.
919 ** If absent and there is no cached value, return NULL.
920
921 DDED test/amend.test
--- a/test/amend.test
+++ b/test/amend.test
@@ -0,0 +1,44 @@
1
+software; you caed
2
+
3
+ returnreturnRichard Hipp
4
+#
5
+# This program is free software; you can redistribute it and/or
6
+# modify it under the terms of the Simplified BSD License (also
7
+# known as the "2-Clause Lishort_uuid {uuid {len 10}} {
8
+ string range $uuid 0 $len-1
9
+}
10
+
11
+proc d Hipp
12
+#
13
+# This program#
14
+# Copyright (c) 2015 D. Richard Hipp
15
+#
16
+# This program is free software; you can redistribute it and/or
17
+# modify it under the terms of the Simplified BSD License (also
18
+# known as the "2-Clause Li#
19
+# Copyright (c) 2015 D. Richard Hipp
20
+#
21
+# This program is free software; you caed
22
+
23
+ return#
24
+# Copyright (c) 2015 D. Richard Hipp
25
+#
26
+# This program is free software; you can redistribute it and/or
27
+# modify it under the terms of the Simplified BSD License (also
28
+# repo_init string range $uuid 0 $len-1
29
+}
30
+
31
+proc d Hipp
32
+#
33
+# This program#
34
+# Copyright (c) 2015 D. Richard Hipp
35
+#
36
+# This program is free software; you can redistribute it and/or
37
+# modify it under the terms of the Simplified BSD License (also
38
+# known as the "2-Clause Li#
39
+# Copyright (c) 2015 D. Richard Hipp
40
+#
41
+# This program is free software; you caed
42
+
43
+ return
44
+ 2.$sc leafset env(EDITOR)unset env(EDITOR)
--- a/test/amend.test
+++ b/test/amend.test
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/amend.test
+++ b/test/amend.test
@@ -0,0 +1,44 @@
1 software; you caed
2
3 returnreturnRichard Hipp
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the Simplified BSD License (also
7 # known as the "2-Clause Lishort_uuid {uuid {len 10}} {
8 string range $uuid 0 $len-1
9 }
10
11 proc d Hipp
12 #
13 # This program#
14 # Copyright (c) 2015 D. Richard Hipp
15 #
16 # This program is free software; you can redistribute it and/or
17 # modify it under the terms of the Simplified BSD License (also
18 # known as the "2-Clause Li#
19 # Copyright (c) 2015 D. Richard Hipp
20 #
21 # This program is free software; you caed
22
23 return#
24 # Copyright (c) 2015 D. Richard Hipp
25 #
26 # This program is free software; you can redistribute it and/or
27 # modify it under the terms of the Simplified BSD License (also
28 # repo_init string range $uuid 0 $len-1
29 }
30
31 proc d Hipp
32 #
33 # This program#
34 # Copyright (c) 2015 D. Richard Hipp
35 #
36 # This program is free software; you can redistribute it and/or
37 # modify it under the terms of the Simplified BSD License (also
38 # known as the "2-Clause Li#
39 # Copyright (c) 2015 D. Richard Hipp
40 #
41 # This program is free software; you caed
42
43 return
44 2.$sc leafset env(EDITOR)unset env(EDITOR)
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
1414
the Tcl integration subsystem.
1515
* Add 'double', 'integer', and 'list' classes to the
1616
<nowiki>[string is]</nowiki> command in TH1.
1717
* Update internal Unicode character tables, used in regular expression
1818
handling, from version 7.0 to 8.0.
19
+ * Add the new [/help?cmd=amend|amend] command which is used to modify
20
+ tags of a "check-in".
1921
2022
<h2>Changes for Version 1.33 (2015-05-23)</h2>
2123
* Improved fork detection on [/help?cmd=update|fossil update],
2224
[/help?cmd=status|fossil status] and related commands.
2325
* Change the default skin to what used to be called "San Francisco Modern".
2426
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
14 the Tcl integration subsystem.
15 * Add 'double', 'integer', and 'list' classes to the
16 <nowiki>[string is]</nowiki> command in TH1.
17 * Update internal Unicode character tables, used in regular expression
18 handling, from version 7.0 to 8.0.
 
 
19
20 <h2>Changes for Version 1.33 (2015-05-23)</h2>
21 * Improved fork detection on [/help?cmd=update|fossil update],
22 [/help?cmd=status|fossil status] and related commands.
23 * Change the default skin to what used to be called "San Francisco Modern".
24
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
14 the Tcl integration subsystem.
15 * Add 'double', 'integer', and 'list' classes to the
16 <nowiki>[string is]</nowiki> command in TH1.
17 * Update internal Unicode character tables, used in regular expression
18 handling, from version 7.0 to 8.0.
19 * Add the new [/help?cmd=amend|amend] command which is used to modify
20 tags of a "check-in".
21
22 <h2>Changes for Version 1.33 (2015-05-23)</h2>
23 * Improved fork detection on [/help?cmd=update|fossil update],
24 [/help?cmd=status|fossil status] and related commands.
25 * Change the default skin to what used to be called "San Francisco Modern".
26
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
1414
the Tcl integration subsystem.
1515
* Add 'double', 'integer', and 'list' classes to the
1616
<nowiki>[string is]</nowiki> command in TH1.
1717
* Update internal Unicode character tables, used in regular expression
1818
handling, from version 7.0 to 8.0.
19
+ * Add the new [/help?cmd=amend|amend] command which is used to modify
20
+ tags of a "check-in".
1921
2022
<h2>Changes for Version 1.33 (2015-05-23)</h2>
2123
* Improved fork detection on [/help?cmd=update|fossil update],
2224
[/help?cmd=status|fossil status] and related commands.
2325
* Change the default skin to what used to be called "San Francisco Modern".
2426
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
14 the Tcl integration subsystem.
15 * Add 'double', 'integer', and 'list' classes to the
16 <nowiki>[string is]</nowiki> command in TH1.
17 * Update internal Unicode character tables, used in regular expression
18 handling, from version 7.0 to 8.0.
 
 
19
20 <h2>Changes for Version 1.33 (2015-05-23)</h2>
21 * Improved fork detection on [/help?cmd=update|fossil update],
22 [/help?cmd=status|fossil status] and related commands.
23 * Change the default skin to what used to be called "San Francisco Modern".
24
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
14 the Tcl integration subsystem.
15 * Add 'double', 'integer', and 'list' classes to the
16 <nowiki>[string is]</nowiki> command in TH1.
17 * Update internal Unicode character tables, used in regular expression
18 handling, from version 7.0 to 8.0.
19 * Add the new [/help?cmd=amend|amend] command which is used to modify
20 tags of a "check-in".
21
22 <h2>Changes for Version 1.33 (2015-05-23)</h2>
23 * Improved fork detection on [/help?cmd=update|fossil update],
24 [/help?cmd=status|fossil status] and related commands.
25 * Change the default skin to what used to be called "San Francisco Modern".
26

Keyboard Shortcuts

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