Fossil SCM

Add --user-override and --date-override options to the "fossil ticket add" command.

drh 2012-08-06 14:41 trunk
Commit ea4acb5c5db5d15b86da6d785bc616c4ff300103
1 file changed +309 -304
+309 -304
--- src/tkt.c
+++ src/tkt.c
@@ -855,332 +855,337 @@
855855
** COMMAND: ticket*
856856
** Usage: %fossil ticket SUBCOMMAND ...
857857
**
858858
** Run various subcommands to control tickets
859859
**
860
-** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
861
-**
862
-** options can be:
863
-** ?-l|--limit LIMITCHAR?
864
-** ?-q|--quote?
865
-** ?-R|--repository FILE?
866
-**
867
-** Run the ticket report, identified by the report format title
868
-** used in the gui. The data is written as flat file on stdout,
869
-** using "," as separator. The separator "," can be changed using
870
-** the -l or --limit option.
871
-**
872
-** If TICKETFILTER is given on the commandline, the query is
873
-** limited with a new WHERE-condition.
874
-** example: Report lists a column # with the uuid
875
-** TICKETFILTER may be [#]='uuuuuuuuu'
876
-** example: Report only lists rows with status not open
877
-** TICKETFILTER: status != 'open'
878
-** If the option -q|--quote is used, the tickets are encoded by
879
-** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
880
-** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
881
-** Otherwise, the simplified encoding as on the show report raw
882
-** page in the gui is used. This has no effect in JSON mode.
883
-**
884
-** Instead of the report title its possible to use the report
885
-** number. Using the special report number 0 list all columns,
886
-** defined in the ticket table.
887
-**
888
-** %fossil ticket list fields
889
-**
890
-** list all fields, defined for ticket in the fossil repository
891
-**
892
-** %fossil ticket list reports
893
-**
894
-** list all ticket reports, defined in the fossil repository
895
-**
896
-** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
897
-** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
898
-**
899
-** change ticket identified by TICKETUUID and set the value of
900
-** field FIELD to VALUE. Valid field descriptions are:
901
-** status, type, severity, priority, resolution,
902
-** foundin, private_contact, resolution, title or comment
903
-** Field names given above are the ones, defined in a standard
904
-** fossil environment. If you have added, deleted columns, you
905
-** change the all your configured columns.
906
-** If you use +FIELD, the VALUE Is appended to the field FIELD.
907
-** You can use more than one field/value pair on the commandline.
908
-** Using -q|--quote enables the special character decoding as
909
-** in "ticket show". So it's possible, to set multiline text or
910
-** text with special characters.
911
-**
912
-** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
913
-**
914
-** like set, but create a new ticket with the given values.
915
-**
916
-** %fossil ticket history TICKETUUID
917
-**
918
-** Show the complete change history for the ticket
860
+** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
861
+**
862
+** options can be:
863
+** ?-l|--limit LIMITCHAR?
864
+** ?-q|--quote?
865
+** ?-R|--repository FILE?
866
+**
867
+** Run the ticket report, identified by the report format title
868
+** used in the gui. The data is written as flat file on stdout,
869
+** using "," as separator. The separator "," can be changed using
870
+** the -l or --limit option.
871
+**
872
+** If TICKETFILTER is given on the commandline, the query is
873
+** limited with a new WHERE-condition.
874
+** example: Report lists a column # with the uuid
875
+** TICKETFILTER may be [#]='uuuuuuuuu'
876
+** example: Report only lists rows with status not open
877
+** TICKETFILTER: status != 'open'
878
+** If the option -q|--quote is used, the tickets are encoded by
879
+** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
880
+** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
881
+** Otherwise, the simplified encoding as on the show report raw
882
+** page in the gui is used. This has no effect in JSON mode.
883
+**
884
+** Instead of the report title its possible to use the report
885
+** number. Using the special report number 0 list all columns,
886
+** defined in the ticket table.
887
+**
888
+** %fossil ticket list fields
889
+**
890
+** list all fields, defined for ticket in the fossil repository
891
+**
892
+** %fossil ticket list reports
893
+**
894
+** list all ticket reports, defined in the fossil repository
895
+**
896
+** %fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote?
897
+** %fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote?
898
+**
899
+** change ticket identified by TICKETUUID and set the value of
900
+** field FIELD to VALUE.
901
+**
902
+** Field names as defined in the TICKET table. By default, these
903
+** names include: type, status, subsystem, priority, severity, foundin,
904
+** resolution, title, and comment, but other field names can be added
905
+** or substituted in customized installations.
906
+**
907
+** If you use +FIELD, the VALUE Is appended to the field FIELD.
908
+** You can use more than one field/value pair on the commandline.
909
+** Using -q|--quote enables the special character decoding as
910
+** in "ticket show". So it's possible, to set multiline text or
911
+** text with special characters.
912
+**
913
+** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
914
+**
915
+** like set, but create a new ticket with the given values.
916
+**
917
+** %fossil ticket history TICKETUUID
918
+**
919
+** Show the complete change history for the ticket
919920
**
920921
** The values in set|add are not validated against the definitions
921922
** given in "Ticket Common Script".
922923
*/
923924
void ticket_cmd(void){
924925
int n;
926
+ const char *zUser;
927
+ const char *zDate;
925928
926929
/* do some ints, we want to be inside a checkout */
927930
db_find_and_open_repository(0, 0);
928931
user_select();
932
+
933
+ zUser = find_option("user-override",0,1);
934
+ if( zUser==0 ) zUser = g.zLogin;
935
+ zDate = find_option("date-override",0,1);
936
+ if( zDate==0 ) zDate = "now";
937
+ zDate = date_in_standard_format(zDate);
938
+
929939
/*
930940
** Check that the user exists.
931941
*/
932
- if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
933
- fossil_fatal("no such user: %s", g.zLogin);
942
+ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", zUser) ){
943
+ fossil_fatal("no such user: %s", zUser);
934944
}
935945
936946
if( g.argc<3 ){
937947
usage("add|fieldlist|set|show|history");
938
- }else{
939
- n = strlen(g.argv[2]);
940
- if( n==1 && g.argv[2][0]=='s' ){
941
- /* set/show cannot be distinguished, so show the usage */
942
- usage("add|fieldlist|set|show|history");
943
- }else if( strncmp(g.argv[2],"list",n)==0 ){
944
- if( g.argc==3 ){
945
- usage("list fields|reports");
946
- }else{
947
- n = strlen(g.argv[3]);
948
- if( !strncmp(g.argv[3],"fields",n) ){
949
- /* simply show all field names */
950
- int i;
951
-
952
- /* read all available ticket fields */
953
- getAllTicketFields();
954
- for(i=0; i<nField; i++){
955
- printf("%s\n",azField[i]);
956
- }
957
- }else if( !strncmp(g.argv[3],"reports",n) ){
958
- rpt_list_reports();
959
- }else{
960
- fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
961
- }
962
- }
963
- }else{
964
- /* add a new ticket or set fields on existing tickets */
965
- tTktShowEncoding tktEncoding;
966
-
967
- tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
968
-
969
- if( strncmp(g.argv[2],"show",n)==0 ){
970
- if( g.argc==3 ){
971
- usage("show REPORTNR");
972
- }else{
973
- const char *zRep = 0;
974
- const char *zSep = 0;
975
- const char *zFilterUuid = 0;
976
- zSep = find_option("limit","l",1);
977
- zRep = g.argv[3];
978
- if( !strcmp(zRep,"0") ){
979
- zRep = 0;
980
- }
981
- if( g.argc>4 ){
982
- zFilterUuid = g.argv[4];
983
- }
984
- rptshow( zRep, zSep, zFilterUuid, tktEncoding );
985
- }
986
- }else{
987
- /* add a new ticket or update an existing ticket */
988
- enum { set,add,history,err } eCmd = err;
989
- int i = 0;
990
- int rid;
991
- const char *zTktUuid = 0;
992
- Blob tktchng, cksum;
993
-
994
- /* get command type (set/add) and get uuid, if needed for set */
995
- if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
996
- strncmp(g.argv[2],"history",n)==0 ){
997
- if( strncmp(g.argv[2],"history",n)==0 ){
998
- eCmd = history;
999
- }else{
1000
- eCmd = set;
1001
- }
1002
- if( g.argc==3 ){
1003
- usage("set TICKETUUID");
1004
- }
1005
- zTktUuid = db_text(0,
1006
- "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1007
- );
1008
- if( !zTktUuid ){
1009
- fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
1010
- }
1011
- i=4;
1012
- }else if( strncmp(g.argv[2],"add",n)==0 ){
1013
- eCmd = add;
1014
- i = 3;
1015
- zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
1016
- }
1017
- /* none of set/add, so show the usage! */
1018
- if( eCmd==err ){
1019
- usage("add|fieldlist|set|show|history");
1020
- }
1021
-
1022
- /* we just handle history separately here, does not get out */
1023
- if( eCmd==history ){
1024
- Stmt q;
1025
- int tagid;
1026
-
1027
- if ( i != g.argc ){
1028
- fossil_fatal("no other parameters expected to %s!",g.argv[2]);
1029
- }
1030
- tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zTktUuid);
1031
- if( tagid==0 ){
1032
- fossil_fatal("no such ticket %h", zTktUuid);
1033
- }
1034
- db_prepare(&q,
1035
- "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
1036
- " FROM event, blob"
1037
- " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
1038
- " AND blob.rid=event.objid"
1039
- " UNION "
1040
- "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
1041
- " FROM attachment, blob"
1042
- " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
1043
- " AND blob.rid=attachid"
1044
- " ORDER BY 1 DESC",
1045
- tagid, tagid
1046
- );
1047
- while( db_step(&q)==SQLITE_ROW ){
1048
- Manifest *pTicket;
1049
- char zShort[12];
1050
- const char *zDate = db_column_text(&q, 0);
1051
- int rid = db_column_int(&q, 1);
1052
- const char *zChngUuid = db_column_text(&q, 2);
1053
- const char *zFile = db_column_text(&q, 4);
1054
- memcpy(zShort, zChngUuid, 10);
1055
- zShort[10] = 0;
1056
- if( zFile!=0 ){
1057
- const char *zSrc = db_column_text(&q, 3);
1058
- const char *zUser = db_column_text(&q, 5);
1059
- if( zSrc==0 || zSrc[0]==0 ){
1060
- fossil_print("Delete attachment %h\n", zFile);
1061
- }else{
1062
- fossil_print("Add attachment %h\n", zFile);
1063
- }
1064
- fossil_print(" by %h on %h\n", zUser, zDate);
1065
- }else{
1066
- pTicket = manifest_get(rid, CFTYPE_TICKET);
1067
- if( pTicket ){
1068
- int i;
1069
-
1070
- fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate);
1071
- for(i=0; i<pTicket->nField; i++){
1072
- Blob val;
1073
- const char *z;
1074
- z = pTicket->aField[i].zName;
1075
- blob_set(&val, pTicket->aField[i].zValue);
1076
- if( z[0]=='+' ){
1077
- fossil_print(" Append to ");
948
+ }
949
+ n = strlen(g.argv[2]);
950
+ if( n==1 && g.argv[2][0]=='s' ){
951
+ /* set/show cannot be distinguished, so show the usage */
952
+ usage("add|fieldlist|set|show|history");
953
+ }
954
+ if( strncmp(g.argv[2],"list",n)==0 ){
955
+ if( g.argc==3 ){
956
+ usage("list fields|reports");
957
+ }else{
958
+ n = strlen(g.argv[3]);
959
+ if( !strncmp(g.argv[3],"fields",n) ){
960
+ /* simply show all field names */
961
+ int i;
962
+
963
+ /* read all available ticket fields */
964
+ getAllTicketFields();
965
+ for(i=0; i<nField; i++){
966
+ printf("%s\n",azField[i]);
967
+ }
968
+ }else if( !strncmp(g.argv[3],"reports",n) ){
969
+ rpt_list_reports();
970
+ }else{
971
+ fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
972
+ }
973
+ }
974
+ }else{
975
+ /* add a new ticket or set fields on existing tickets */
976
+ tTktShowEncoding tktEncoding;
977
+
978
+ tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
979
+
980
+ if( strncmp(g.argv[2],"show",n)==0 ){
981
+ if( g.argc==3 ){
982
+ usage("show REPORTNR");
983
+ }else{
984
+ const char *zRep = 0;
985
+ const char *zSep = 0;
986
+ const char *zFilterUuid = 0;
987
+ zSep = find_option("limit","l",1);
988
+ zRep = g.argv[3];
989
+ if( !strcmp(zRep,"0") ){
990
+ zRep = 0;
991
+ }
992
+ if( g.argc>4 ){
993
+ zFilterUuid = g.argv[4];
994
+ }
995
+ rptshow( zRep, zSep, zFilterUuid, tktEncoding );
996
+ }
997
+ }else{
998
+ /* add a new ticket or update an existing ticket */
999
+ enum { set,add,history,err } eCmd = err;
1000
+ int i = 0;
1001
+ int rid;
1002
+ const char *zTktUuid = 0;
1003
+ Blob tktchng, cksum;
1004
+
1005
+ /* get command type (set/add) and get uuid, if needed for set */
1006
+ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
1007
+ strncmp(g.argv[2],"history",n)==0 ){
1008
+ if( strncmp(g.argv[2],"history",n)==0 ){
1009
+ eCmd = history;
1010
+ }else{
1011
+ eCmd = set;
1012
+ }
1013
+ if( g.argc==3 ){
1014
+ usage("set TICKETUUID");
1015
+ }
1016
+ zTktUuid = db_text(0,
1017
+ "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1018
+ );
1019
+ if( !zTktUuid ){
1020
+ fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
1021
+ }
1022
+ i=4;
1023
+ }else if( strncmp(g.argv[2],"add",n)==0 ){
1024
+ eCmd = add;
1025
+ i = 3;
1026
+ zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
1027
+ }
1028
+ /* none of set/add, so show the usage! */
1029
+ if( eCmd==err ){
1030
+ usage("add|fieldlist|set|show|history");
1031
+ }
1032
+
1033
+ /* we just handle history separately here, does not get out */
1034
+ if( eCmd==history ){
1035
+ Stmt q;
1036
+ int tagid;
1037
+
1038
+ if ( i != g.argc ){
1039
+ fossil_fatal("no other parameters expected to %s!",g.argv[2]);
1040
+ }
1041
+ tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zTktUuid);
1042
+ if( tagid==0 ){
1043
+ fossil_fatal("no such ticket %h", zTktUuid);
1044
+ }
1045
+ db_prepare(&q,
1046
+ "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
1047
+ " FROM event, blob"
1048
+ " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
1049
+ " AND blob.rid=event.objid"
1050
+ " UNION "
1051
+ "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
1052
+ " FROM attachment, blob"
1053
+ " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
1054
+ " AND blob.rid=attachid"
1055
+ " ORDER BY 1 DESC",
1056
+ tagid, tagid
1057
+ );
1058
+ while( db_step(&q)==SQLITE_ROW ){
1059
+ Manifest *pTicket;
1060
+ char zShort[12];
1061
+ const char *zDate = db_column_text(&q, 0);
1062
+ int rid = db_column_int(&q, 1);
1063
+ const char *zChngUuid = db_column_text(&q, 2);
1064
+ const char *zFile = db_column_text(&q, 4);
1065
+ memcpy(zShort, zChngUuid, 10);
1066
+ zShort[10] = 0;
1067
+ if( zFile!=0 ){
1068
+ const char *zSrc = db_column_text(&q, 3);
1069
+ const char *zUser = db_column_text(&q, 5);
1070
+ if( zSrc==0 || zSrc[0]==0 ){
1071
+ fossil_print("Delete attachment %h\n", zFile);
1072
+ }else{
1073
+ fossil_print("Add attachment %h\n", zFile);
1074
+ }
1075
+ fossil_print(" by %h on %h\n", zUser, zDate);
1076
+ }else{
1077
+ pTicket = manifest_get(rid, CFTYPE_TICKET);
1078
+ if( pTicket ){
1079
+ int i;
1080
+
1081
+ fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate);
1082
+ for(i=0; i<pTicket->nField; i++){
1083
+ Blob val;
1084
+ const char *z;
1085
+ z = pTicket->aField[i].zName;
1086
+ blob_set(&val, pTicket->aField[i].zValue);
1087
+ if( z[0]=='+' ){
1088
+ fossil_print(" Append to ");
10781089
z++;
10791090
}else{
10801091
fossil_print(" Change ");
1081
- }
1082
- fossil_print("%h: ",z);
1083
- if( blob_size(&val)>50 || contains_newline(&val)) {
1084
- fossil_print("\n ",blob_str(&val));
1085
- comment_print(blob_str(&val),4,79);
1086
- }else{
1087
- fossil_print("%s\n",blob_str(&val));
1088
- }
1089
- blob_reset(&val);
1090
- }
1091
- }
1092
- manifest_destroy(pTicket);
1093
- }
1094
- }
1095
- db_finalize(&q);
1096
- return;
1097
- }
1098
- /* read all given ticket field/value pairs from command line */
1099
- if( i==g.argc ){
1100
- fossil_fatal("empty %s command aborted!",g.argv[2]);
1101
- }
1102
- getAllTicketFields();
1103
- /* read commandline and assign fields in the azValue array */
1104
- while( i<g.argc ){
1105
- char *zFName;
1106
- char *zFValue;
1107
- int j;
1108
- int append = 0;
1109
-
1110
- zFName = g.argv[i++];
1111
- if( i==g.argc ){
1112
- fossil_fatal("missing value for '%s'!",zFName);
1113
- }
1114
- zFValue = g.argv[i++];
1115
- if( tktEncoding == tktFossilize ){
1116
- zFValue=mprintf("%s",zFValue);
1117
- defossilize(zFValue);
1118
- }
1119
- append = (zFName[0] == '+');
1120
- if (append){
1121
- zFName++;
1122
- }
1123
- j = fieldId(zFName);
1124
- if( j == -1 ){
1125
- fossil_fatal("unknown field name '%s'!",zFName);
1126
- }else{
1127
- if (append) {
1128
- azAppend[j] = zFValue;
1129
- } else {
1130
- azValue[j] = zFValue;
1131
- }
1132
- }
1133
- }
1134
-
1135
- /* now add the needed artifacts to the repository */
1136
- blob_zero(&tktchng);
1137
- { /* add the time to the ticket manifest */
1138
- char *zDate;
1139
-
1140
- zDate = date_in_standard_format("now");
1141
- blob_appendf(&tktchng, "D %s\n", zDate);
1142
- free(zDate);
1143
- }
1144
- /* append defined elements */
1145
- for(i=0; i<nField; i++){
1146
- char *zValue = 0;
1147
- char *zPfx;
1148
-
1149
- if (azAppend[i] && azAppend[i][0] ){
1150
- zPfx = " +";
1151
- zValue = azAppend[i];
1152
- } else if( azValue[i] && azValue[i][0] ){
1153
- zPfx = " ";
1154
- zValue = azValue[i];
1155
- } else {
1156
- continue;
1157
- }
1158
- if( strncmp(azField[i], "private_", 8)==0 ){
1159
- zValue = db_conceal(zValue, strlen(zValue));
1160
- blob_appendf(&tktchng, "J%s%s %s\n", zPfx, azField[i], zValue);
1161
- }else{
1162
- blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
1163
- azField[i], strlen(zValue), zValue);
1164
- }
1165
- if( tktEncoding == tktFossilize ){
1166
- free(azValue[i]);
1167
- }
1168
- }
1169
- blob_appendf(&tktchng, "K %s\n", zTktUuid);
1170
- blob_appendf(&tktchng, "U %F\n", g.zLogin);
1171
- md5sum_blob(&tktchng, &cksum);
1172
- blob_appendf(&tktchng, "Z %b\n", &cksum);
1173
- rid = content_put(&tktchng);
1174
- if( rid==0 ){
1175
- fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1176
- }
1177
- manifest_crosslink_begin();
1178
- manifest_crosslink(rid, &tktchng);
1179
- manifest_crosslink_end();
1180
- assert( blob_is_reset(&tktchng) );
1181
- printf("ticket %s succeeded for UID %s\n",
1182
- (eCmd==set?"set":"add"),zTktUuid);
1183
- }
1092
+ }
1093
+ fossil_print("%h: ",z);
1094
+ if( blob_size(&val)>50 || contains_newline(&val)) {
1095
+ fossil_print("\n ",blob_str(&val));
1096
+ comment_print(blob_str(&val),4,79);
1097
+ }else{
1098
+ fossil_print("%s\n",blob_str(&val));
1099
+ }
1100
+ blob_reset(&val);
1101
+ }
1102
+ }
1103
+ manifest_destroy(pTicket);
1104
+ }
1105
+ }
1106
+ db_finalize(&q);
1107
+ return;
1108
+ }
1109
+ /* read all given ticket field/value pairs from command line */
1110
+ if( i==g.argc ){
1111
+ fossil_fatal("empty %s command aborted!",g.argv[2]);
1112
+ }
1113
+ getAllTicketFields();
1114
+ /* read commandline and assign fields in the azValue array */
1115
+ while( i<g.argc ){
1116
+ char *zFName;
1117
+ char *zFValue;
1118
+ int j;
1119
+ int append = 0;
1120
+
1121
+ zFName = g.argv[i++];
1122
+ if( i==g.argc ){
1123
+ fossil_fatal("missing value for '%s'!",zFName);
1124
+ }
1125
+ zFValue = g.argv[i++];
1126
+ if( tktEncoding == tktFossilize ){
1127
+ zFValue=mprintf("%s",zFValue);
1128
+ defossilize(zFValue);
1129
+ }
1130
+ append = (zFName[0] == '+');
1131
+ if (append){
1132
+ zFName++;
1133
+ }
1134
+ j = fieldId(zFName);
1135
+ if( j == -1 ){
1136
+ fossil_fatal("unknown field name '%s'!",zFName);
1137
+ }else{
1138
+ if (append) {
1139
+ azAppend[j] = zFValue;
1140
+ } else {
1141
+ azValue[j] = zFValue;
1142
+ }
1143
+ }
1144
+ }
1145
+
1146
+ /* now add the needed artifacts to the repository */
1147
+ blob_zero(&tktchng);
1148
+ /* add the time to the ticket manifest */
1149
+ blob_appendf(&tktchng, "D %s\n", zDate);
1150
+ /* append defined elements */
1151
+ for(i=0; i<nField; i++){
1152
+ char *zValue = 0;
1153
+ char *zPfx;
1154
+
1155
+ if (azAppend[i] && azAppend[i][0] ){
1156
+ zPfx = " +";
1157
+ zValue = azAppend[i];
1158
+ } else if( azValue[i] && azValue[i][0] ){
1159
+ zPfx = " ";
1160
+ zValue = azValue[i];
1161
+ } else {
1162
+ continue;
1163
+ }
1164
+ if( strncmp(azField[i], "private_", 8)==0 ){
1165
+ zValue = db_conceal(zValue, strlen(zValue));
1166
+ blob_appendf(&tktchng, "J%s%s %s\n", zPfx, azField[i], zValue);
1167
+ }else{
1168
+ blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
1169
+ azField[i], strlen(zValue), zValue);
1170
+ }
1171
+ if( tktEncoding == tktFossilize ){
1172
+ free(azValue[i]);
1173
+ }
1174
+ }
1175
+ blob_appendf(&tktchng, "K %s\n", zTktUuid);
1176
+ blob_appendf(&tktchng, "U %F\n", zUser);
1177
+ md5sum_blob(&tktchng, &cksum);
1178
+ blob_appendf(&tktchng, "Z %b\n", &cksum);
1179
+ rid = content_put(&tktchng);
1180
+ if( rid==0 ){
1181
+ fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1182
+ }
1183
+ manifest_crosslink_begin();
1184
+ manifest_crosslink(rid, &tktchng);
1185
+ manifest_crosslink_end();
1186
+ assert( blob_is_reset(&tktchng) );
1187
+ printf("ticket %s succeeded for %s\n",
1188
+ (eCmd==set?"set":"add"),zTktUuid);
11841189
}
11851190
}
11861191
}
11871192
--- src/tkt.c
+++ src/tkt.c
@@ -855,332 +855,337 @@
855 ** COMMAND: ticket*
856 ** Usage: %fossil ticket SUBCOMMAND ...
857 **
858 ** Run various subcommands to control tickets
859 **
860 ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
861 **
862 ** options can be:
863 ** ?-l|--limit LIMITCHAR?
864 ** ?-q|--quote?
865 ** ?-R|--repository FILE?
866 **
867 ** Run the ticket report, identified by the report format title
868 ** used in the gui. The data is written as flat file on stdout,
869 ** using "," as separator. The separator "," can be changed using
870 ** the -l or --limit option.
871 **
872 ** If TICKETFILTER is given on the commandline, the query is
873 ** limited with a new WHERE-condition.
874 ** example: Report lists a column # with the uuid
875 ** TICKETFILTER may be [#]='uuuuuuuuu'
876 ** example: Report only lists rows with status not open
877 ** TICKETFILTER: status != 'open'
878 ** If the option -q|--quote is used, the tickets are encoded by
879 ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
880 ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
881 ** Otherwise, the simplified encoding as on the show report raw
882 ** page in the gui is used. This has no effect in JSON mode.
883 **
884 ** Instead of the report title its possible to use the report
885 ** number. Using the special report number 0 list all columns,
886 ** defined in the ticket table.
887 **
888 ** %fossil ticket list fields
889 **
890 ** list all fields, defined for ticket in the fossil repository
891 **
892 ** %fossil ticket list reports
893 **
894 ** list all ticket reports, defined in the fossil repository
895 **
896 ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
897 ** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
898 **
899 ** change ticket identified by TICKETUUID and set the value of
900 ** field FIELD to VALUE. Valid field descriptions are:
901 ** status, type, severity, priority, resolution,
902 ** foundin, private_contact, resolution, title or comment
903 ** Field names given above are the ones, defined in a standard
904 ** fossil environment. If you have added, deleted columns, you
905 ** change the all your configured columns.
906 ** If you use +FIELD, the VALUE Is appended to the field FIELD.
907 ** You can use more than one field/value pair on the commandline.
908 ** Using -q|--quote enables the special character decoding as
909 ** in "ticket show". So it's possible, to set multiline text or
910 ** text with special characters.
911 **
912 ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
913 **
914 ** like set, but create a new ticket with the given values.
915 **
916 ** %fossil ticket history TICKETUUID
917 **
918 ** Show the complete change history for the ticket
 
919 **
920 ** The values in set|add are not validated against the definitions
921 ** given in "Ticket Common Script".
922 */
923 void ticket_cmd(void){
924 int n;
 
 
925
926 /* do some ints, we want to be inside a checkout */
927 db_find_and_open_repository(0, 0);
928 user_select();
 
 
 
 
 
 
 
929 /*
930 ** Check that the user exists.
931 */
932 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
933 fossil_fatal("no such user: %s", g.zLogin);
934 }
935
936 if( g.argc<3 ){
937 usage("add|fieldlist|set|show|history");
938 }else{
939 n = strlen(g.argv[2]);
940 if( n==1 && g.argv[2][0]=='s' ){
941 /* set/show cannot be distinguished, so show the usage */
942 usage("add|fieldlist|set|show|history");
943 }else if( strncmp(g.argv[2],"list",n)==0 ){
944 if( g.argc==3 ){
945 usage("list fields|reports");
946 }else{
947 n = strlen(g.argv[3]);
948 if( !strncmp(g.argv[3],"fields",n) ){
949 /* simply show all field names */
950 int i;
951
952 /* read all available ticket fields */
953 getAllTicketFields();
954 for(i=0; i<nField; i++){
955 printf("%s\n",azField[i]);
956 }
957 }else if( !strncmp(g.argv[3],"reports",n) ){
958 rpt_list_reports();
959 }else{
960 fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
961 }
962 }
963 }else{
964 /* add a new ticket or set fields on existing tickets */
965 tTktShowEncoding tktEncoding;
966
967 tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
968
969 if( strncmp(g.argv[2],"show",n)==0 ){
970 if( g.argc==3 ){
971 usage("show REPORTNR");
972 }else{
973 const char *zRep = 0;
974 const char *zSep = 0;
975 const char *zFilterUuid = 0;
976 zSep = find_option("limit","l",1);
977 zRep = g.argv[3];
978 if( !strcmp(zRep,"0") ){
979 zRep = 0;
980 }
981 if( g.argc>4 ){
982 zFilterUuid = g.argv[4];
983 }
984 rptshow( zRep, zSep, zFilterUuid, tktEncoding );
985 }
986 }else{
987 /* add a new ticket or update an existing ticket */
988 enum { set,add,history,err } eCmd = err;
989 int i = 0;
990 int rid;
991 const char *zTktUuid = 0;
992 Blob tktchng, cksum;
993
994 /* get command type (set/add) and get uuid, if needed for set */
995 if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
996 strncmp(g.argv[2],"history",n)==0 ){
997 if( strncmp(g.argv[2],"history",n)==0 ){
998 eCmd = history;
999 }else{
1000 eCmd = set;
1001 }
1002 if( g.argc==3 ){
1003 usage("set TICKETUUID");
1004 }
1005 zTktUuid = db_text(0,
1006 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1007 );
1008 if( !zTktUuid ){
1009 fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
1010 }
1011 i=4;
1012 }else if( strncmp(g.argv[2],"add",n)==0 ){
1013 eCmd = add;
1014 i = 3;
1015 zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
1016 }
1017 /* none of set/add, so show the usage! */
1018 if( eCmd==err ){
1019 usage("add|fieldlist|set|show|history");
1020 }
1021
1022 /* we just handle history separately here, does not get out */
1023 if( eCmd==history ){
1024 Stmt q;
1025 int tagid;
1026
1027 if ( i != g.argc ){
1028 fossil_fatal("no other parameters expected to %s!",g.argv[2]);
1029 }
1030 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zTktUuid);
1031 if( tagid==0 ){
1032 fossil_fatal("no such ticket %h", zTktUuid);
1033 }
1034 db_prepare(&q,
1035 "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
1036 " FROM event, blob"
1037 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
1038 " AND blob.rid=event.objid"
1039 " UNION "
1040 "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
1041 " FROM attachment, blob"
1042 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
1043 " AND blob.rid=attachid"
1044 " ORDER BY 1 DESC",
1045 tagid, tagid
1046 );
1047 while( db_step(&q)==SQLITE_ROW ){
1048 Manifest *pTicket;
1049 char zShort[12];
1050 const char *zDate = db_column_text(&q, 0);
1051 int rid = db_column_int(&q, 1);
1052 const char *zChngUuid = db_column_text(&q, 2);
1053 const char *zFile = db_column_text(&q, 4);
1054 memcpy(zShort, zChngUuid, 10);
1055 zShort[10] = 0;
1056 if( zFile!=0 ){
1057 const char *zSrc = db_column_text(&q, 3);
1058 const char *zUser = db_column_text(&q, 5);
1059 if( zSrc==0 || zSrc[0]==0 ){
1060 fossil_print("Delete attachment %h\n", zFile);
1061 }else{
1062 fossil_print("Add attachment %h\n", zFile);
1063 }
1064 fossil_print(" by %h on %h\n", zUser, zDate);
1065 }else{
1066 pTicket = manifest_get(rid, CFTYPE_TICKET);
1067 if( pTicket ){
1068 int i;
1069
1070 fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate);
1071 for(i=0; i<pTicket->nField; i++){
1072 Blob val;
1073 const char *z;
1074 z = pTicket->aField[i].zName;
1075 blob_set(&val, pTicket->aField[i].zValue);
1076 if( z[0]=='+' ){
1077 fossil_print(" Append to ");
 
1078 z++;
1079 }else{
1080 fossil_print(" Change ");
1081 }
1082 fossil_print("%h: ",z);
1083 if( blob_size(&val)>50 || contains_newline(&val)) {
1084 fossil_print("\n ",blob_str(&val));
1085 comment_print(blob_str(&val),4,79);
1086 }else{
1087 fossil_print("%s\n",blob_str(&val));
1088 }
1089 blob_reset(&val);
1090 }
1091 }
1092 manifest_destroy(pTicket);
1093 }
1094 }
1095 db_finalize(&q);
1096 return;
1097 }
1098 /* read all given ticket field/value pairs from command line */
1099 if( i==g.argc ){
1100 fossil_fatal("empty %s command aborted!",g.argv[2]);
1101 }
1102 getAllTicketFields();
1103 /* read commandline and assign fields in the azValue array */
1104 while( i<g.argc ){
1105 char *zFName;
1106 char *zFValue;
1107 int j;
1108 int append = 0;
1109
1110 zFName = g.argv[i++];
1111 if( i==g.argc ){
1112 fossil_fatal("missing value for '%s'!",zFName);
1113 }
1114 zFValue = g.argv[i++];
1115 if( tktEncoding == tktFossilize ){
1116 zFValue=mprintf("%s",zFValue);
1117 defossilize(zFValue);
1118 }
1119 append = (zFName[0] == '+');
1120 if (append){
1121 zFName++;
1122 }
1123 j = fieldId(zFName);
1124 if( j == -1 ){
1125 fossil_fatal("unknown field name '%s'!",zFName);
1126 }else{
1127 if (append) {
1128 azAppend[j] = zFValue;
1129 } else {
1130 azValue[j] = zFValue;
1131 }
1132 }
1133 }
1134
1135 /* now add the needed artifacts to the repository */
1136 blob_zero(&tktchng);
1137 { /* add the time to the ticket manifest */
1138 char *zDate;
1139
1140 zDate = date_in_standard_format("now");
1141 blob_appendf(&tktchng, "D %s\n", zDate);
1142 free(zDate);
1143 }
1144 /* append defined elements */
1145 for(i=0; i<nField; i++){
1146 char *zValue = 0;
1147 char *zPfx;
1148
1149 if (azAppend[i] && azAppend[i][0] ){
1150 zPfx = " +";
1151 zValue = azAppend[i];
1152 } else if( azValue[i] && azValue[i][0] ){
1153 zPfx = " ";
1154 zValue = azValue[i];
1155 } else {
1156 continue;
1157 }
1158 if( strncmp(azField[i], "private_", 8)==0 ){
1159 zValue = db_conceal(zValue, strlen(zValue));
1160 blob_appendf(&tktchng, "J%s%s %s\n", zPfx, azField[i], zValue);
1161 }else{
1162 blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
1163 azField[i], strlen(zValue), zValue);
1164 }
1165 if( tktEncoding == tktFossilize ){
1166 free(azValue[i]);
1167 }
1168 }
1169 blob_appendf(&tktchng, "K %s\n", zTktUuid);
1170 blob_appendf(&tktchng, "U %F\n", g.zLogin);
1171 md5sum_blob(&tktchng, &cksum);
1172 blob_appendf(&tktchng, "Z %b\n", &cksum);
1173 rid = content_put(&tktchng);
1174 if( rid==0 ){
1175 fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1176 }
1177 manifest_crosslink_begin();
1178 manifest_crosslink(rid, &tktchng);
1179 manifest_crosslink_end();
1180 assert( blob_is_reset(&tktchng) );
1181 printf("ticket %s succeeded for UID %s\n",
1182 (eCmd==set?"set":"add"),zTktUuid);
1183 }
1184 }
1185 }
1186 }
1187
--- src/tkt.c
+++ src/tkt.c
@@ -855,332 +855,337 @@
855 ** COMMAND: ticket*
856 ** Usage: %fossil ticket SUBCOMMAND ...
857 **
858 ** Run various subcommands to control tickets
859 **
860 ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
861 **
862 ** options can be:
863 ** ?-l|--limit LIMITCHAR?
864 ** ?-q|--quote?
865 ** ?-R|--repository FILE?
866 **
867 ** Run the ticket report, identified by the report format title
868 ** used in the gui. The data is written as flat file on stdout,
869 ** using "," as separator. The separator "," can be changed using
870 ** the -l or --limit option.
871 **
872 ** If TICKETFILTER is given on the commandline, the query is
873 ** limited with a new WHERE-condition.
874 ** example: Report lists a column # with the uuid
875 ** TICKETFILTER may be [#]='uuuuuuuuu'
876 ** example: Report only lists rows with status not open
877 ** TICKETFILTER: status != 'open'
878 ** If the option -q|--quote is used, the tickets are encoded by
879 ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
880 ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
881 ** Otherwise, the simplified encoding as on the show report raw
882 ** page in the gui is used. This has no effect in JSON mode.
883 **
884 ** Instead of the report title its possible to use the report
885 ** number. Using the special report number 0 list all columns,
886 ** defined in the ticket table.
887 **
888 ** %fossil ticket list fields
889 **
890 ** list all fields, defined for ticket in the fossil repository
891 **
892 ** %fossil ticket list reports
893 **
894 ** list all ticket reports, defined in the fossil repository
895 **
896 ** %fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote?
897 ** %fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote?
898 **
899 ** change ticket identified by TICKETUUID and set the value of
900 ** field FIELD to VALUE.
901 **
902 ** Field names as defined in the TICKET table. By default, these
903 ** names include: type, status, subsystem, priority, severity, foundin,
904 ** resolution, title, and comment, but other field names can be added
905 ** or substituted in customized installations.
906 **
907 ** If you use +FIELD, the VALUE Is appended to the field FIELD.
908 ** You can use more than one field/value pair on the commandline.
909 ** Using -q|--quote enables the special character decoding as
910 ** in "ticket show". So it's possible, to set multiline text or
911 ** text with special characters.
912 **
913 ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
914 **
915 ** like set, but create a new ticket with the given values.
916 **
917 ** %fossil ticket history TICKETUUID
918 **
919 ** Show the complete change history for the ticket
920 **
921 ** The values in set|add are not validated against the definitions
922 ** given in "Ticket Common Script".
923 */
924 void ticket_cmd(void){
925 int n;
926 const char *zUser;
927 const char *zDate;
928
929 /* do some ints, we want to be inside a checkout */
930 db_find_and_open_repository(0, 0);
931 user_select();
932
933 zUser = find_option("user-override",0,1);
934 if( zUser==0 ) zUser = g.zLogin;
935 zDate = find_option("date-override",0,1);
936 if( zDate==0 ) zDate = "now";
937 zDate = date_in_standard_format(zDate);
938
939 /*
940 ** Check that the user exists.
941 */
942 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", zUser) ){
943 fossil_fatal("no such user: %s", zUser);
944 }
945
946 if( g.argc<3 ){
947 usage("add|fieldlist|set|show|history");
948 }
949 n = strlen(g.argv[2]);
950 if( n==1 && g.argv[2][0]=='s' ){
951 /* set/show cannot be distinguished, so show the usage */
952 usage("add|fieldlist|set|show|history");
953 }
954 if( strncmp(g.argv[2],"list",n)==0 ){
955 if( g.argc==3 ){
956 usage("list fields|reports");
957 }else{
958 n = strlen(g.argv[3]);
959 if( !strncmp(g.argv[3],"fields",n) ){
960 /* simply show all field names */
961 int i;
962
963 /* read all available ticket fields */
964 getAllTicketFields();
965 for(i=0; i<nField; i++){
966 printf("%s\n",azField[i]);
967 }
968 }else if( !strncmp(g.argv[3],"reports",n) ){
969 rpt_list_reports();
970 }else{
971 fossil_fatal("unknown ticket list option '%s'!",g.argv[3]);
972 }
973 }
974 }else{
975 /* add a new ticket or set fields on existing tickets */
976 tTktShowEncoding tktEncoding;
977
978 tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab;
979
980 if( strncmp(g.argv[2],"show",n)==0 ){
981 if( g.argc==3 ){
982 usage("show REPORTNR");
983 }else{
984 const char *zRep = 0;
985 const char *zSep = 0;
986 const char *zFilterUuid = 0;
987 zSep = find_option("limit","l",1);
988 zRep = g.argv[3];
989 if( !strcmp(zRep,"0") ){
990 zRep = 0;
991 }
992 if( g.argc>4 ){
993 zFilterUuid = g.argv[4];
994 }
995 rptshow( zRep, zSep, zFilterUuid, tktEncoding );
996 }
997 }else{
998 /* add a new ticket or update an existing ticket */
999 enum { set,add,history,err } eCmd = err;
1000 int i = 0;
1001 int rid;
1002 const char *zTktUuid = 0;
1003 Blob tktchng, cksum;
1004
1005 /* get command type (set/add) and get uuid, if needed for set */
1006 if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
1007 strncmp(g.argv[2],"history",n)==0 ){
1008 if( strncmp(g.argv[2],"history",n)==0 ){
1009 eCmd = history;
1010 }else{
1011 eCmd = set;
1012 }
1013 if( g.argc==3 ){
1014 usage("set TICKETUUID");
1015 }
1016 zTktUuid = db_text(0,
1017 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1018 );
1019 if( !zTktUuid ){
1020 fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
1021 }
1022 i=4;
1023 }else if( strncmp(g.argv[2],"add",n)==0 ){
1024 eCmd = add;
1025 i = 3;
1026 zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
1027 }
1028 /* none of set/add, so show the usage! */
1029 if( eCmd==err ){
1030 usage("add|fieldlist|set|show|history");
1031 }
1032
1033 /* we just handle history separately here, does not get out */
1034 if( eCmd==history ){
1035 Stmt q;
1036 int tagid;
1037
1038 if ( i != g.argc ){
1039 fossil_fatal("no other parameters expected to %s!",g.argv[2]);
1040 }
1041 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zTktUuid);
1042 if( tagid==0 ){
1043 fossil_fatal("no such ticket %h", zTktUuid);
1044 }
1045 db_prepare(&q,
1046 "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
1047 " FROM event, blob"
1048 " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
1049 " AND blob.rid=event.objid"
1050 " UNION "
1051 "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
1052 " FROM attachment, blob"
1053 " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
1054 " AND blob.rid=attachid"
1055 " ORDER BY 1 DESC",
1056 tagid, tagid
1057 );
1058 while( db_step(&q)==SQLITE_ROW ){
1059 Manifest *pTicket;
1060 char zShort[12];
1061 const char *zDate = db_column_text(&q, 0);
1062 int rid = db_column_int(&q, 1);
1063 const char *zChngUuid = db_column_text(&q, 2);
1064 const char *zFile = db_column_text(&q, 4);
1065 memcpy(zShort, zChngUuid, 10);
1066 zShort[10] = 0;
1067 if( zFile!=0 ){
1068 const char *zSrc = db_column_text(&q, 3);
1069 const char *zUser = db_column_text(&q, 5);
1070 if( zSrc==0 || zSrc[0]==0 ){
1071 fossil_print("Delete attachment %h\n", zFile);
1072 }else{
1073 fossil_print("Add attachment %h\n", zFile);
1074 }
1075 fossil_print(" by %h on %h\n", zUser, zDate);
1076 }else{
1077 pTicket = manifest_get(rid, CFTYPE_TICKET);
1078 if( pTicket ){
1079 int i;
1080
1081 fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate);
1082 for(i=0; i<pTicket->nField; i++){
1083 Blob val;
1084 const char *z;
1085 z = pTicket->aField[i].zName;
1086 blob_set(&val, pTicket->aField[i].zValue);
1087 if( z[0]=='+' ){
1088 fossil_print(" Append to ");
1089 z++;
1090 }else{
1091 fossil_print(" Change ");
1092 }
1093 fossil_print("%h: ",z);
1094 if( blob_size(&val)>50 || contains_newline(&val)) {
1095 fossil_print("\n ",blob_str(&val));
1096 comment_print(blob_str(&val),4,79);
1097 }else{
1098 fossil_print("%s\n",blob_str(&val));
1099 }
1100 blob_reset(&val);
1101 }
1102 }
1103 manifest_destroy(pTicket);
1104 }
1105 }
1106 db_finalize(&q);
1107 return;
1108 }
1109 /* read all given ticket field/value pairs from command line */
1110 if( i==g.argc ){
1111 fossil_fatal("empty %s command aborted!",g.argv[2]);
1112 }
1113 getAllTicketFields();
1114 /* read commandline and assign fields in the azValue array */
1115 while( i<g.argc ){
1116 char *zFName;
1117 char *zFValue;
1118 int j;
1119 int append = 0;
1120
1121 zFName = g.argv[i++];
1122 if( i==g.argc ){
1123 fossil_fatal("missing value for '%s'!",zFName);
1124 }
1125 zFValue = g.argv[i++];
1126 if( tktEncoding == tktFossilize ){
1127 zFValue=mprintf("%s",zFValue);
1128 defossilize(zFValue);
1129 }
1130 append = (zFName[0] == '+');
1131 if (append){
1132 zFName++;
1133 }
1134 j = fieldId(zFName);
1135 if( j == -1 ){
1136 fossil_fatal("unknown field name '%s'!",zFName);
1137 }else{
1138 if (append) {
1139 azAppend[j] = zFValue;
1140 } else {
1141 azValue[j] = zFValue;
1142 }
1143 }
1144 }
1145
1146 /* now add the needed artifacts to the repository */
1147 blob_zero(&tktchng);
1148 /* add the time to the ticket manifest */
1149 blob_appendf(&tktchng, "D %s\n", zDate);
1150 /* append defined elements */
1151 for(i=0; i<nField; i++){
1152 char *zValue = 0;
1153 char *zPfx;
1154
1155 if (azAppend[i] && azAppend[i][0] ){
1156 zPfx = " +";
1157 zValue = azAppend[i];
1158 } else if( azValue[i] && azValue[i][0] ){
1159 zPfx = " ";
1160 zValue = azValue[i];
1161 } else {
1162 continue;
1163 }
1164 if( strncmp(azField[i], "private_", 8)==0 ){
1165 zValue = db_conceal(zValue, strlen(zValue));
1166 blob_appendf(&tktchng, "J%s%s %s\n", zPfx, azField[i], zValue);
1167 }else{
1168 blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
1169 azField[i], strlen(zValue), zValue);
1170 }
1171 if( tktEncoding == tktFossilize ){
1172 free(azValue[i]);
1173 }
1174 }
1175 blob_appendf(&tktchng, "K %s\n", zTktUuid);
1176 blob_appendf(&tktchng, "U %F\n", zUser);
1177 md5sum_blob(&tktchng, &cksum);
1178 blob_appendf(&tktchng, "Z %b\n", &cksum);
1179 rid = content_put(&tktchng);
1180 if( rid==0 ){
1181 fossil_panic("trouble committing ticket: %s", g.zErrMsg);
1182 }
1183 manifest_crosslink_begin();
1184 manifest_crosslink(rid, &tktchng);
1185 manifest_crosslink_end();
1186 assert( blob_is_reset(&tktchng) );
1187 printf("ticket %s succeeded for %s\n",
1188 (eCmd==set?"set":"add"),zTktUuid);
 
 
 
 
 
 
1189 }
1190 }
1191 }
1192

Keyboard Shortcuts

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