Fossil SCM

Added /json/branch/create. Finally found g.zErrMsg, and started using it in conjunction with json error reporting.

stephan 2011-10-02 22:58 UTC json-multitag-test
Commit 8eaf58ee5179750005b6e4f09186555d35bff818
+34 -7
--- src/json.c
+++ src/json.c
@@ -999,12 +999,10 @@
999999
if(cmd){
10001000
json_string_split(cmd, '/', 0, g.json.cmd.a);
10011001
g.json.cmd.commandStr = cmd;
10021002
}
10031003
}
1004
-
1005
-
10061004
10071005
if(!g.json.jsonp && g.json.post.o){
10081006
g.json.jsonp =
10091007
json_getenv_cstr("jsonp")
10101008
/*cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")))*/
@@ -1242,17 +1240,22 @@
12421240
tmp = json_new_timestamp(-1);
12431241
SET(FossilJsonKeys.timestamp);
12441242
}
12451243
12461244
if( 0 != resultCode ){
1247
- if( ! pMsg ) pMsg = json_err_str(resultCode);
1248
- tmp = json_rc_string(resultCode);
1245
+ if( ! pMsg ){
1246
+ pMsg = g.zErrMsg;
1247
+ if(!pMsg){
1248
+ pMsg = json_err_str(resultCode);
1249
+ }
1250
+ }
1251
+ tmp = cson_value_new_integer(resultCode);
12491252
SET(FossilJsonKeys.resultCode);
12501253
}
12511254
12521255
if( pMsg && *pMsg ){
1253
- tmp = cson_value_new_string(pMsg,strlen(pMsg));
1256
+ tmp = cson_value_new_string(pMsg, strlen(pMsg));
12541257
SET(FossilJsonKeys.resultText);
12551258
}
12561259
12571260
if(g.json.cmd.commandStr){
12581261
tmp = json_new_string(g.json.cmd.commandStr);
@@ -1340,11 +1343,14 @@
13401343
? g.json.resultCode
13411344
: FSL_JSON_E_UNKNOWN);
13421345
cson_value * resp = NULL;
13431346
rc = json_dumbdown_rc(rc);
13441347
if( rc && !msg ){
1345
- msg = json_err_str(rc);
1348
+ msg = g.zErrMsg;
1349
+ if(!msg){
1350
+ msg = json_err_str(rc);
1351
+ }
13461352
}
13471353
resp = json_create_response(rc, msg, NULL);
13481354
if(!resp){
13491355
/* about the only error case here is out-of-memory. DO NOT
13501356
call fossil_panic() here because that calls this function.
@@ -1372,10 +1378,31 @@
13721378
json_send_response(resp);
13731379
}
13741380
cson_value_free(resp);
13751381
}
13761382
1383
+/*
1384
+** Sets g.json.resultCode and g.zErrMsg, but does not report the error
1385
+** via json_err(). Returns the code passed to it.
1386
+**
1387
+** code must be in the inclusive range 1000..9999.
1388
+*/
1389
+int json_set_err( int code, char const * fmt, ... ){
1390
+ assert( (code>=1000) && (code<=9999) );
1391
+ free(g.zErrMsg);
1392
+ g.json.resultCode = code;
1393
+ if(!fmt || !*fmt){
1394
+ g.zErrMsg = mprintf("%s", json_err_str(code));
1395
+ }else{
1396
+ va_list vargs;
1397
+ va_start(vargs,fmt);
1398
+ char * msg = vmprintf(fmt, vargs);
1399
+ va_end(vargs);
1400
+ g.zErrMsg = msg;
1401
+ }
1402
+ return code;
1403
+}
13771404
13781405
/*
13791406
** Iterates through a prepared SELECT statement and converts each row
13801407
** to a JSON object. If pTgt is not NULL then it must be-a Array
13811408
** object and this function will return pTgt. If pTgt is NULL then a
@@ -1787,11 +1814,11 @@
17871814
{"tag", json_page_nyi,0},
17881815
{"ticket", json_page_nyi,0},
17891816
{"timeline", json_page_timeline,0},
17901817
{"user",json_page_user,0},
17911818
{"version",json_page_version,0},
1792
-{"whoami",json_page_whoami,1/*FIXME: work in CLI mode*/},
1819
+{"whoami",json_page_whoami,0/*FIXME: work in CLI mode*/},
17931820
{"wiki",json_page_wiki,0},
17941821
/* Last entry MUST have a NULL name. */
17951822
{NULL,NULL,0}
17961823
};
17971824
17981825
--- src/json.c
+++ src/json.c
@@ -999,12 +999,10 @@
999 if(cmd){
1000 json_string_split(cmd, '/', 0, g.json.cmd.a);
1001 g.json.cmd.commandStr = cmd;
1002 }
1003 }
1004
1005
1006
1007 if(!g.json.jsonp && g.json.post.o){
1008 g.json.jsonp =
1009 json_getenv_cstr("jsonp")
1010 /*cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")))*/
@@ -1242,17 +1240,22 @@
1242 tmp = json_new_timestamp(-1);
1243 SET(FossilJsonKeys.timestamp);
1244 }
1245
1246 if( 0 != resultCode ){
1247 if( ! pMsg ) pMsg = json_err_str(resultCode);
1248 tmp = json_rc_string(resultCode);
 
 
 
 
 
1249 SET(FossilJsonKeys.resultCode);
1250 }
1251
1252 if( pMsg && *pMsg ){
1253 tmp = cson_value_new_string(pMsg,strlen(pMsg));
1254 SET(FossilJsonKeys.resultText);
1255 }
1256
1257 if(g.json.cmd.commandStr){
1258 tmp = json_new_string(g.json.cmd.commandStr);
@@ -1340,11 +1343,14 @@
1340 ? g.json.resultCode
1341 : FSL_JSON_E_UNKNOWN);
1342 cson_value * resp = NULL;
1343 rc = json_dumbdown_rc(rc);
1344 if( rc && !msg ){
1345 msg = json_err_str(rc);
 
 
 
1346 }
1347 resp = json_create_response(rc, msg, NULL);
1348 if(!resp){
1349 /* about the only error case here is out-of-memory. DO NOT
1350 call fossil_panic() here because that calls this function.
@@ -1372,10 +1378,31 @@
1372 json_send_response(resp);
1373 }
1374 cson_value_free(resp);
1375 }
1376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1377
1378 /*
1379 ** Iterates through a prepared SELECT statement and converts each row
1380 ** to a JSON object. If pTgt is not NULL then it must be-a Array
1381 ** object and this function will return pTgt. If pTgt is NULL then a
@@ -1787,11 +1814,11 @@
1787 {"tag", json_page_nyi,0},
1788 {"ticket", json_page_nyi,0},
1789 {"timeline", json_page_timeline,0},
1790 {"user",json_page_user,0},
1791 {"version",json_page_version,0},
1792 {"whoami",json_page_whoami,1/*FIXME: work in CLI mode*/},
1793 {"wiki",json_page_wiki,0},
1794 /* Last entry MUST have a NULL name. */
1795 {NULL,NULL,0}
1796 };
1797
1798
--- src/json.c
+++ src/json.c
@@ -999,12 +999,10 @@
999 if(cmd){
1000 json_string_split(cmd, '/', 0, g.json.cmd.a);
1001 g.json.cmd.commandStr = cmd;
1002 }
1003 }
 
 
1004
1005 if(!g.json.jsonp && g.json.post.o){
1006 g.json.jsonp =
1007 json_getenv_cstr("jsonp")
1008 /*cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")))*/
@@ -1242,17 +1240,22 @@
1240 tmp = json_new_timestamp(-1);
1241 SET(FossilJsonKeys.timestamp);
1242 }
1243
1244 if( 0 != resultCode ){
1245 if( ! pMsg ){
1246 pMsg = g.zErrMsg;
1247 if(!pMsg){
1248 pMsg = json_err_str(resultCode);
1249 }
1250 }
1251 tmp = cson_value_new_integer(resultCode);
1252 SET(FossilJsonKeys.resultCode);
1253 }
1254
1255 if( pMsg && *pMsg ){
1256 tmp = cson_value_new_string(pMsg, strlen(pMsg));
1257 SET(FossilJsonKeys.resultText);
1258 }
1259
1260 if(g.json.cmd.commandStr){
1261 tmp = json_new_string(g.json.cmd.commandStr);
@@ -1340,11 +1343,14 @@
1343 ? g.json.resultCode
1344 : FSL_JSON_E_UNKNOWN);
1345 cson_value * resp = NULL;
1346 rc = json_dumbdown_rc(rc);
1347 if( rc && !msg ){
1348 msg = g.zErrMsg;
1349 if(!msg){
1350 msg = json_err_str(rc);
1351 }
1352 }
1353 resp = json_create_response(rc, msg, NULL);
1354 if(!resp){
1355 /* about the only error case here is out-of-memory. DO NOT
1356 call fossil_panic() here because that calls this function.
@@ -1372,10 +1378,31 @@
1378 json_send_response(resp);
1379 }
1380 cson_value_free(resp);
1381 }
1382
1383 /*
1384 ** Sets g.json.resultCode and g.zErrMsg, but does not report the error
1385 ** via json_err(). Returns the code passed to it.
1386 **
1387 ** code must be in the inclusive range 1000..9999.
1388 */
1389 int json_set_err( int code, char const * fmt, ... ){
1390 assert( (code>=1000) && (code<=9999) );
1391 free(g.zErrMsg);
1392 g.json.resultCode = code;
1393 if(!fmt || !*fmt){
1394 g.zErrMsg = mprintf("%s", json_err_str(code));
1395 }else{
1396 va_list vargs;
1397 va_start(vargs,fmt);
1398 char * msg = vmprintf(fmt, vargs);
1399 va_end(vargs);
1400 g.zErrMsg = msg;
1401 }
1402 return code;
1403 }
1404
1405 /*
1406 ** Iterates through a prepared SELECT statement and converts each row
1407 ** to a JSON object. If pTgt is not NULL then it must be-a Array
1408 ** object and this function will return pTgt. If pTgt is NULL then a
@@ -1787,11 +1814,11 @@
1814 {"tag", json_page_nyi,0},
1815 {"ticket", json_page_nyi,0},
1816 {"timeline", json_page_timeline,0},
1817 {"user",json_page_user,0},
1818 {"version",json_page_version,0},
1819 {"whoami",json_page_whoami,0/*FIXME: work in CLI mode*/},
1820 {"wiki",json_page_wiki,0},
1821 /* Last entry MUST have a NULL name. */
1822 {NULL,NULL,0}
1823 };
1824
1825
+239 -1
--- src/json_branch.c
+++ src/json_branch.c
@@ -22,16 +22,18 @@
2222
#include "json_detail.h"
2323
#endif
2424
2525
2626
static cson_value * json_branch_list();
27
+static cson_value * json_branch_create();
2728
/*
2829
** Mapping of /json/branch/XXX commands/paths to callbacks.
2930
*/
3031
static const JsonPageDef JsonPageDefs_Branch[] = {
32
+{"create", json_branch_create, 0},
3133
{"list", json_branch_list, 0},
32
-{"create", json_page_nyi, 1},
34
+{"new", json_branch_create, -1/* for compat with non-JSON branch command.*/},
3335
/* Last entry MUST have a NULL name. */
3436
{NULL,NULL,0}
3537
};
3638
3739
/*
@@ -137,8 +139,244 @@
137139
}
138140
}
139141
if( sawConversionError ){
140142
json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,sawConversionError);
141143
free(sawConversionError);
144
+ }
145
+ return payV;
146
+}
147
+
148
+/*
149
+** Parameters for the create-branch operation.
150
+*/
151
+typedef struct BranchCreateOptions{
152
+ char const * zName;
153
+ char const * zBasis;
154
+ char const * zColor;
155
+ char isPrivate;
156
+ /**
157
+ Might be set to an error string by
158
+ json_branch_new().
159
+ */
160
+ char const * rcErrMsg;
161
+} BranchCreateOptions;
162
+
163
+/*
164
+** Tries to create a new branch based on the options set in zOpt. If
165
+** an error is encountered, zOpt->rcErrMsg _might_ be set to a
166
+** descriptive string and one of the FossilJsonCodes values will be
167
+** returned. Or fossil_fatal() (or similar) might be called, exiting
168
+** the app.
169
+**
170
+** On success 0 is returned and if zNewRid is not NULL then the rid of
171
+** the new branch is assigned to it.
172
+**
173
+** If zOpt->isPrivate is 0 but the parent branch is private,
174
+** zOpt->isPrivate will be set to a non-zero value and the new branch
175
+** will be private.
176
+*/
177
+static int json_branch_new(BranchCreateOptions * zOpt,
178
+ int *zNewRid){
179
+ /* Mostly copied from branch.c:branch_new(), but refactored a small
180
+ bit to not produce output or interact with the user. The
181
+ down-side to that is that we dropped the gpg-signing. It was
182
+ either that or abort the creation if we couldn't sign. We can't
183
+ sign over HTTP mode, anyway.
184
+ */
185
+ char const * zBranch = zOpt->zName;
186
+ char const * zBasis = zOpt->zBasis;
187
+ char const * zColor = zOpt->zColor;
188
+ int rootid; /* RID of the root check-in - what we branch off of */
189
+ int brid; /* RID of the branch check-in */
190
+ int i; /* Loop counter */
191
+ char *zUuid; /* Artifact ID of origin */
192
+ Stmt q; /* Generic query */
193
+ char *zDate; /* Date that branch was created */
194
+ char *zComment; /* Check-in comment for the new branch */
195
+ Blob branch; /* manifest for the new branch */
196
+ Manifest *pParent; /* Parsed parent manifest */
197
+ Blob mcksum; /* Self-checksum on the manifest */
198
+
199
+ /* fossil branch new name */
200
+ if( zBranch==0 || zBranch[0]==0 ){
201
+ zOpt->rcErrMsg = "Branch name may not be null/empty.";
202
+ return FSL_JSON_E_INVALID_ARGS;
203
+ }
204
+ if( db_exists(
205
+ "SELECT 1 FROM tagxref"
206
+ " WHERE tagtype>0"
207
+ " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')",
208
+ zBranch)!=0 ){
209
+ zOpt->rcErrMsg = "Branch already exists.";
210
+ return FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
211
+ }
212
+
213
+ db_begin_transaction();
214
+ rootid = name_to_typed_rid(zBasis, "ci");
215
+ if( rootid==0 ){
216
+ zOpt->rcErrMsg = "Basis branch not found.";
217
+ return FSL_JSON_E_RESOURCE_NOT_FOUND;
218
+ }
219
+
220
+ pParent = manifest_get(rootid, CFTYPE_MANIFEST);
221
+ if( pParent==0 ){
222
+ zOpt->rcErrMsg = "Could not read parent manifest.";
223
+ return FSL_JSON_E_UNKNOWN;
224
+ }
225
+
226
+ /* Create a manifest for the new branch */
227
+ blob_zero(&branch);
228
+ if( pParent->zBaseline ){
229
+ blob_appendf(&branch, "B %s\n", pParent->zBaseline);
230
+ }
231
+ zComment = mprintf("Create new branch named \"%s\" "
232
+ "from \"%s\".", zBranch, zBasis);
233
+ blob_appendf(&branch, "C %F\n", zComment);
234
+ free(zComment);
235
+ zDate = date_in_standard_format("now");
236
+ blob_appendf(&branch, "D %s\n", zDate);
237
+ free(zDate);
238
+
239
+ /* Copy all of the content from the parent into the branch */
240
+ for(i=0; i<pParent->nFile; ++i){
241
+ blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
242
+ if( pParent->aFile[i].zUuid ){
243
+ blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
244
+ if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){
245
+ blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
246
+ }
247
+ }
248
+ blob_append(&branch, "\n", 1);
249
+ }
250
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
251
+ blob_appendf(&branch, "P %s\n", zUuid);
252
+ free(zUuid);
253
+ if( pParent->zRepoCksum ){
254
+ blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
255
+ }
256
+ manifest_destroy(pParent);
257
+
258
+ /* Add the symbolic branch name and the "branch" tag to identify
259
+ ** this as a new branch */
260
+ if( content_is_private(rootid) ) zOpt->isPrivate = 1;
261
+ if( zOpt->isPrivate && zColor==0 ) zColor = "#fec084";
262
+ if( zColor!=0 ){
263
+ blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
264
+ }
265
+ blob_appendf(&branch, "T *branch * %F\n", zBranch);
266
+ blob_appendf(&branch, "T *sym-%F *\n", zBranch);
267
+ if( zOpt->isPrivate ){
268
+ blob_appendf(&branch, "T +private *\n");
269
+ }
270
+
271
+ /* Cancel all other symbolic tags */
272
+ db_prepare(&q,
273
+ "SELECT tagname FROM tagxref, tag"
274
+ " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
275
+ " AND tagtype>0 AND tagname GLOB 'sym-*'"
276
+ " ORDER BY tagname",
277
+ rootid);
278
+ while( db_step(&q)==SQLITE_ROW ){
279
+ const char *zTag = db_column_text(&q, 0);
280
+ blob_appendf(&branch, "T -%F *\n", zTag);
281
+ }
282
+ db_finalize(&q);
283
+
284
+ blob_appendf(&branch, "U %F\n", g.zLogin);
285
+ md5sum_blob(&branch, &mcksum);
286
+ blob_appendf(&branch, "Z %b\n", &mcksum);
287
+
288
+ brid = content_put(&branch);
289
+ if( brid==0 ){
290
+ fossil_panic("Problem committing manifest: %s", g.zErrMsg);
291
+ }
292
+ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
293
+ if( manifest_crosslink(brid, &branch)==0 ){
294
+ fossil_panic("unable to install new manifest");
295
+ }
296
+ assert( blob_is_reset(&branch) );
297
+ content_deltify(rootid, brid, 0);
298
+ if( zNewRid ){
299
+ *zNewRid = brid;
300
+ }
301
+
302
+ /* Commit */
303
+ db_end_transaction(0);
304
+
305
+#if 0 /* Do an autosync push, if requested */
306
+ /* arugable for JSON mode? */
307
+ if( !g.isHTTP && !isPrivate ) autosync(AUTOSYNC_PUSH);
308
+#endif
309
+ return 0;
142310
}
311
+
312
+
313
+static cson_value * json_branch_create(){
314
+ cson_value * payV = NULL;
315
+ cson_object * pay = NULL;
316
+ int rc = 0;
317
+ BranchCreateOptions opt;
318
+ char * zUuid = NULL;
319
+ int rid = 0;
320
+ if( !g.perm.Write ){
321
+ g.json.resultCode = FSL_JSON_E_DENIED;
322
+ return NULL;
323
+ }
324
+ if(0){
325
+ char const * x = json_command_arg(g.json.dispatchDepth+1);
326
+ fprintf(stderr,"command arg=%s\n",x);
327
+ assert(0);
328
+ }
329
+ memset(&opt,0,sizeof(BranchCreateOptions));
330
+ opt.zName = g.json.post.v
331
+ ? json_getenv_cstr("name")
332
+ : json_command_arg(g.json.dispatchDepth+1);
333
+
334
+ if(!opt.zName){
335
+ json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." );
336
+ return NULL;
337
+ }
338
+
339
+ opt.zBasis = g.json.post.v
340
+ ? json_getenv_cstr("basis")
341
+ : json_command_arg(g.json.dispatchDepth+2);
342
+ if(!opt.zBasis || ('-'==*opt.zBasis/*assume CLI flag*/)){
343
+ opt.zBasis = "trunk";
344
+ }
345
+
346
+ opt.zColor = g.json.post.v
347
+ ? json_getenv_cstr("bgColor")
348
+ : find_option("bgcolor","",1);
349
+
350
+ opt.isPrivate = g.json.post.v
351
+ ? json_getenv_bool("private",0)
352
+ : (NULL != find_option("private","",0))
353
+ ;
354
+
355
+ rc = json_branch_new( &opt, &rid );
356
+ if(rc){
357
+ json_set_err(rc, opt.rcErrMsg );
358
+ goto error;
359
+ }
360
+ payV = cson_value_new_object();
361
+ pay = cson_value_get_object(payV);
362
+
363
+ cson_object_set(pay,"name",json_new_string(opt.zName));
364
+ cson_object_set(pay,"basis",json_new_string(opt.zBasis));
365
+ cson_object_set(pay,"rid",json_new_int(rid));
366
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
367
+ cson_object_set(pay,"uuid", json_new_string(zUuid));
368
+ cson_object_set(pay, "isPrivate", cson_value_new_bool(opt.isPrivate));
369
+ free(zUuid);
370
+ if(opt.zColor){
371
+ cson_object_set(pay,"bgColor",json_new_string(opt.zColor));
372
+ }
373
+
374
+ goto ok;
375
+ error:
376
+ assert( 0 != g.json.resultCode );
377
+ cson_value_free(payV);
378
+ payV = NULL;
379
+ ok:
143380
return payV;
144381
}
382
+
145383
--- src/json_branch.c
+++ src/json_branch.c
@@ -22,16 +22,18 @@
22 #include "json_detail.h"
23 #endif
24
25
26 static cson_value * json_branch_list();
 
27 /*
28 ** Mapping of /json/branch/XXX commands/paths to callbacks.
29 */
30 static const JsonPageDef JsonPageDefs_Branch[] = {
 
31 {"list", json_branch_list, 0},
32 {"create", json_page_nyi, 1},
33 /* Last entry MUST have a NULL name. */
34 {NULL,NULL,0}
35 };
36
37 /*
@@ -137,8 +139,244 @@
137 }
138 }
139 if( sawConversionError ){
140 json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,sawConversionError);
141 free(sawConversionError);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143 return payV;
144 }
 
145
--- src/json_branch.c
+++ src/json_branch.c
@@ -22,16 +22,18 @@
22 #include "json_detail.h"
23 #endif
24
25
26 static cson_value * json_branch_list();
27 static cson_value * json_branch_create();
28 /*
29 ** Mapping of /json/branch/XXX commands/paths to callbacks.
30 */
31 static const JsonPageDef JsonPageDefs_Branch[] = {
32 {"create", json_branch_create, 0},
33 {"list", json_branch_list, 0},
34 {"new", json_branch_create, -1/* for compat with non-JSON branch command.*/},
35 /* Last entry MUST have a NULL name. */
36 {NULL,NULL,0}
37 };
38
39 /*
@@ -137,8 +139,244 @@
139 }
140 }
141 if( sawConversionError ){
142 json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,sawConversionError);
143 free(sawConversionError);
144 }
145 return payV;
146 }
147
148 /*
149 ** Parameters for the create-branch operation.
150 */
151 typedef struct BranchCreateOptions{
152 char const * zName;
153 char const * zBasis;
154 char const * zColor;
155 char isPrivate;
156 /**
157 Might be set to an error string by
158 json_branch_new().
159 */
160 char const * rcErrMsg;
161 } BranchCreateOptions;
162
163 /*
164 ** Tries to create a new branch based on the options set in zOpt. If
165 ** an error is encountered, zOpt->rcErrMsg _might_ be set to a
166 ** descriptive string and one of the FossilJsonCodes values will be
167 ** returned. Or fossil_fatal() (or similar) might be called, exiting
168 ** the app.
169 **
170 ** On success 0 is returned and if zNewRid is not NULL then the rid of
171 ** the new branch is assigned to it.
172 **
173 ** If zOpt->isPrivate is 0 but the parent branch is private,
174 ** zOpt->isPrivate will be set to a non-zero value and the new branch
175 ** will be private.
176 */
177 static int json_branch_new(BranchCreateOptions * zOpt,
178 int *zNewRid){
179 /* Mostly copied from branch.c:branch_new(), but refactored a small
180 bit to not produce output or interact with the user. The
181 down-side to that is that we dropped the gpg-signing. It was
182 either that or abort the creation if we couldn't sign. We can't
183 sign over HTTP mode, anyway.
184 */
185 char const * zBranch = zOpt->zName;
186 char const * zBasis = zOpt->zBasis;
187 char const * zColor = zOpt->zColor;
188 int rootid; /* RID of the root check-in - what we branch off of */
189 int brid; /* RID of the branch check-in */
190 int i; /* Loop counter */
191 char *zUuid; /* Artifact ID of origin */
192 Stmt q; /* Generic query */
193 char *zDate; /* Date that branch was created */
194 char *zComment; /* Check-in comment for the new branch */
195 Blob branch; /* manifest for the new branch */
196 Manifest *pParent; /* Parsed parent manifest */
197 Blob mcksum; /* Self-checksum on the manifest */
198
199 /* fossil branch new name */
200 if( zBranch==0 || zBranch[0]==0 ){
201 zOpt->rcErrMsg = "Branch name may not be null/empty.";
202 return FSL_JSON_E_INVALID_ARGS;
203 }
204 if( db_exists(
205 "SELECT 1 FROM tagxref"
206 " WHERE tagtype>0"
207 " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')",
208 zBranch)!=0 ){
209 zOpt->rcErrMsg = "Branch already exists.";
210 return FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
211 }
212
213 db_begin_transaction();
214 rootid = name_to_typed_rid(zBasis, "ci");
215 if( rootid==0 ){
216 zOpt->rcErrMsg = "Basis branch not found.";
217 return FSL_JSON_E_RESOURCE_NOT_FOUND;
218 }
219
220 pParent = manifest_get(rootid, CFTYPE_MANIFEST);
221 if( pParent==0 ){
222 zOpt->rcErrMsg = "Could not read parent manifest.";
223 return FSL_JSON_E_UNKNOWN;
224 }
225
226 /* Create a manifest for the new branch */
227 blob_zero(&branch);
228 if( pParent->zBaseline ){
229 blob_appendf(&branch, "B %s\n", pParent->zBaseline);
230 }
231 zComment = mprintf("Create new branch named \"%s\" "
232 "from \"%s\".", zBranch, zBasis);
233 blob_appendf(&branch, "C %F\n", zComment);
234 free(zComment);
235 zDate = date_in_standard_format("now");
236 blob_appendf(&branch, "D %s\n", zDate);
237 free(zDate);
238
239 /* Copy all of the content from the parent into the branch */
240 for(i=0; i<pParent->nFile; ++i){
241 blob_appendf(&branch, "F %F", pParent->aFile[i].zName);
242 if( pParent->aFile[i].zUuid ){
243 blob_appendf(&branch, " %s", pParent->aFile[i].zUuid);
244 if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){
245 blob_appendf(&branch, " %s", pParent->aFile[i].zPerm);
246 }
247 }
248 blob_append(&branch, "\n", 1);
249 }
250 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
251 blob_appendf(&branch, "P %s\n", zUuid);
252 free(zUuid);
253 if( pParent->zRepoCksum ){
254 blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
255 }
256 manifest_destroy(pParent);
257
258 /* Add the symbolic branch name and the "branch" tag to identify
259 ** this as a new branch */
260 if( content_is_private(rootid) ) zOpt->isPrivate = 1;
261 if( zOpt->isPrivate && zColor==0 ) zColor = "#fec084";
262 if( zColor!=0 ){
263 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
264 }
265 blob_appendf(&branch, "T *branch * %F\n", zBranch);
266 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
267 if( zOpt->isPrivate ){
268 blob_appendf(&branch, "T +private *\n");
269 }
270
271 /* Cancel all other symbolic tags */
272 db_prepare(&q,
273 "SELECT tagname FROM tagxref, tag"
274 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
275 " AND tagtype>0 AND tagname GLOB 'sym-*'"
276 " ORDER BY tagname",
277 rootid);
278 while( db_step(&q)==SQLITE_ROW ){
279 const char *zTag = db_column_text(&q, 0);
280 blob_appendf(&branch, "T -%F *\n", zTag);
281 }
282 db_finalize(&q);
283
284 blob_appendf(&branch, "U %F\n", g.zLogin);
285 md5sum_blob(&branch, &mcksum);
286 blob_appendf(&branch, "Z %b\n", &mcksum);
287
288 brid = content_put(&branch);
289 if( brid==0 ){
290 fossil_panic("Problem committing manifest: %s", g.zErrMsg);
291 }
292 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
293 if( manifest_crosslink(brid, &branch)==0 ){
294 fossil_panic("unable to install new manifest");
295 }
296 assert( blob_is_reset(&branch) );
297 content_deltify(rootid, brid, 0);
298 if( zNewRid ){
299 *zNewRid = brid;
300 }
301
302 /* Commit */
303 db_end_transaction(0);
304
305 #if 0 /* Do an autosync push, if requested */
306 /* arugable for JSON mode? */
307 if( !g.isHTTP && !isPrivate ) autosync(AUTOSYNC_PUSH);
308 #endif
309 return 0;
310 }
311
312
313 static cson_value * json_branch_create(){
314 cson_value * payV = NULL;
315 cson_object * pay = NULL;
316 int rc = 0;
317 BranchCreateOptions opt;
318 char * zUuid = NULL;
319 int rid = 0;
320 if( !g.perm.Write ){
321 g.json.resultCode = FSL_JSON_E_DENIED;
322 return NULL;
323 }
324 if(0){
325 char const * x = json_command_arg(g.json.dispatchDepth+1);
326 fprintf(stderr,"command arg=%s\n",x);
327 assert(0);
328 }
329 memset(&opt,0,sizeof(BranchCreateOptions));
330 opt.zName = g.json.post.v
331 ? json_getenv_cstr("name")
332 : json_command_arg(g.json.dispatchDepth+1);
333
334 if(!opt.zName){
335 json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." );
336 return NULL;
337 }
338
339 opt.zBasis = g.json.post.v
340 ? json_getenv_cstr("basis")
341 : json_command_arg(g.json.dispatchDepth+2);
342 if(!opt.zBasis || ('-'==*opt.zBasis/*assume CLI flag*/)){
343 opt.zBasis = "trunk";
344 }
345
346 opt.zColor = g.json.post.v
347 ? json_getenv_cstr("bgColor")
348 : find_option("bgcolor","",1);
349
350 opt.isPrivate = g.json.post.v
351 ? json_getenv_bool("private",0)
352 : (NULL != find_option("private","",0))
353 ;
354
355 rc = json_branch_new( &opt, &rid );
356 if(rc){
357 json_set_err(rc, opt.rcErrMsg );
358 goto error;
359 }
360 payV = cson_value_new_object();
361 pay = cson_value_get_object(payV);
362
363 cson_object_set(pay,"name",json_new_string(opt.zName));
364 cson_object_set(pay,"basis",json_new_string(opt.zBasis));
365 cson_object_set(pay,"rid",json_new_int(rid));
366 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
367 cson_object_set(pay,"uuid", json_new_string(zUuid));
368 cson_object_set(pay, "isPrivate", cson_value_new_bool(opt.isPrivate));
369 free(zUuid);
370 if(opt.zColor){
371 cson_object_set(pay,"bgColor",json_new_string(opt.zColor));
372 }
373
374 goto ok;
375 error:
376 assert( 0 != g.json.resultCode );
377 cson_value_free(payV);
378 payV = NULL;
379 ok:
380 return payV;
381 }
382
383
+1
--- src/main.c
+++ src/main.c
@@ -292,10 +292,11 @@
292292
** used by fossil.
293293
*/
294294
void fossil_atexit() {
295295
296296
cson_value_free(g.json.gc.v);
297
+ free(g.zErrMsg);
297298
memset(&g.json, 0, sizeof(g.json));
298299
if(g.db){
299300
db_close(0);
300301
}
301302
}
302303
--- src/main.c
+++ src/main.c
@@ -292,10 +292,11 @@
292 ** used by fossil.
293 */
294 void fossil_atexit() {
295
296 cson_value_free(g.json.gc.v);
 
297 memset(&g.json, 0, sizeof(g.json));
298 if(g.db){
299 db_close(0);
300 }
301 }
302
--- src/main.c
+++ src/main.c
@@ -292,10 +292,11 @@
292 ** used by fossil.
293 */
294 void fossil_atexit() {
295
296 cson_value_free(g.json.gc.v);
297 free(g.zErrMsg);
298 memset(&g.json, 0, sizeof(g.json));
299 if(g.db){
300 db_close(0);
301 }
302 }
303

Keyboard Shortcuts

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