Fossil SCM

fossil-scm / src / bisect.c
Blame History Raw 754 lines
1
/*
2
** Copyright (c) 2010 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
**
15
*******************************************************************************
16
**
17
** This file contains code used to implement the "bisect" command.
18
**
19
** This file also contains logic used to compute the closure of filename
20
** changes that have occurred across multiple check-ins.
21
*/
22
#include "config.h"
23
#include "bisect.h"
24
#include <assert.h>
25
26
/*
27
** Local variables for this module
28
*/
29
static struct {
30
int bad; /* The bad version */
31
int good; /* The good version */
32
} bisect;
33
34
/*
35
** Find the shortest path between bad and good.
36
*/
37
void bisect_path(void){
38
PathNode *p;
39
bisect.bad = db_lget_int("bisect-bad", 0);
40
bisect.good = db_lget_int("bisect-good", 0);
41
if( bisect.good>0 && bisect.bad==0 ){
42
path_shortest(bisect.good, bisect.good, 0, 0, 0, 0);
43
}else if( bisect.bad>0 && bisect.good==0 ){
44
path_shortest(bisect.bad, bisect.bad, 0, 0, 0, 0);
45
}else if( bisect.bad==0 && bisect.good==0 ){
46
fossil_fatal("neither \"good\" nor \"bad\" versions have been identified");
47
}else{
48
Bag skip;
49
int bDirect = bisect_option("direct-only");
50
char *zLog = db_lget("bisect-log","");
51
Blob log, id;
52
bag_init(&skip);
53
blob_init(&log, zLog, -1);
54
while( blob_token(&log, &id) ){
55
if( blob_str(&id)[0]=='s' ){
56
bag_insert(&skip, atoi(blob_str(&id)+1));
57
}
58
}
59
blob_reset(&log);
60
p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip, 0);
61
bag_clear(&skip);
62
if( p==0 ){
63
char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad);
64
char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good);
65
fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
66
zGood, zBad);
67
}
68
}
69
}
70
71
/*
72
** The set of all bisect options.
73
*/
74
static const struct {
75
const char *zName;
76
const char *zDefault;
77
const char *zDesc;
78
} aBisectOption[] = {
79
{ "auto-next", "on", "Automatically run \"bisect next\" after each "
80
"\"bisect good\", \"bisect bad\", or \"bisect "
81
"skip\"" },
82
{ "direct-only", "on", "Follow only primary parent-child links, not "
83
"merges\n" },
84
{ "display", "chart", "Command to run after \"next\". \"chart\", "
85
"\"log\", \"status\", or \"none\"" },
86
{ "linear", "off", "Do a linear scan rather than a true bisect, "
87
"stopping at the first \"bad\" result"},
88
};
89
90
/*
91
** Return the value of a boolean bisect option.
92
*/
93
int bisect_option(const char *zName){
94
unsigned int i;
95
int r = -1;
96
for(i=0; i<count(aBisectOption); i++){
97
if( fossil_strcmp(zName, aBisectOption[i].zName)==0 ){
98
char *zLabel = mprintf("bisect-%s", zName);
99
char *z;
100
if( g.localOpen ){
101
z = db_lget(zLabel, (char*)aBisectOption[i].zDefault);
102
}else{
103
z = (char*)aBisectOption[i].zDefault;
104
}
105
if( is_truth(z) ) r = 1;
106
if( is_false(z) ) r = 0;
107
if( r<0 ) r = is_truth(aBisectOption[i].zDefault);
108
free(zLabel);
109
break;
110
}
111
}
112
assert( r>=0 );
113
return r;
114
}
115
116
/*
117
** List a bisect path.
118
*/
119
static void bisect_list(int abbreviated){
120
PathNode *p;
121
int vid = db_lget_int("checkout", 0);
122
int n;
123
Stmt s;
124
int nStep;
125
int nHidden = 0;
126
bisect_path();
127
db_prepare(&s, "SELECT blob.uuid, datetime(event.mtime) "
128
" FROM blob, event"
129
" WHERE blob.rid=:rid AND event.objid=:rid"
130
" AND event.type='ci'");
131
nStep = path_length();
132
if( abbreviated ){
133
for(p=path_last(); p; p=p->pFrom) p->isHidden = 1;
134
for(p=path_last(), n=0; p; p=p->pFrom, n++){
135
if( p->rid==bisect.good
136
|| p->rid==bisect.bad
137
|| p->rid==vid
138
|| (nStep>1 && n==nStep/2)
139
){
140
p->isHidden = 0;
141
if( p->pFrom ) p->pFrom->isHidden = 0;
142
}
143
}
144
for(p=path_last(); p; p=p->pFrom){
145
if( p->pFrom && p->pFrom->isHidden==0 ) p->isHidden = 0;
146
}
147
}
148
for(p=path_last(), n=0; p; p=p->pFrom, n++){
149
if( p->isHidden && (nHidden || (p->pFrom && p->pFrom->isHidden)) ){
150
nHidden++;
151
continue;
152
}else if( nHidden ){
153
fossil_print(" ... %d other check-ins omitted\n", nHidden);
154
nHidden = 0;
155
}
156
db_bind_int(&s, ":rid", p->rid);
157
if( db_step(&s)==SQLITE_ROW ){
158
const char *zUuid = db_column_text(&s, 0);
159
const char *zDate = db_column_text(&s, 1);
160
fossil_print("%s %S", zDate, zUuid);
161
if( p->rid==bisect.good ) fossil_print(" GOOD");
162
if( p->rid==bisect.bad ) fossil_print(" BAD");
163
if( p->rid==vid ) fossil_print(" CURRENT");
164
if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
165
fossil_print("\n");
166
}
167
db_reset(&s);
168
}
169
db_finalize(&s);
170
}
171
172
/*
173
** Append a new entry to the bisect log. Update the bisect-good or
174
** bisect-bad values as appropriate.
175
**
176
** The bisect-log consists of a list of tokens. Each token is an
177
** integer RID of a check-in. The RID is negative for "bad" check-ins
178
** and positive for "good" check-ins.
179
*/
180
static void bisect_append_log(int rid){
181
if( rid<0 ){
182
if( db_lget_int("bisect-bad",0)==(-rid) ) return;
183
db_lset_int("bisect-bad", -rid);
184
}else{
185
if( db_lget_int("bisect-good",0)==rid ) return;
186
db_lset_int("bisect-good", rid);
187
}
188
db_multi_exec(
189
"REPLACE INTO vvar(name,value) VALUES('bisect-log',"
190
"COALESCE((SELECT value||' ' FROM vvar WHERE name='bisect-log'),'')"
191
" || '%d')", rid);
192
}
193
194
/*
195
** Append a new skip entry to the bisect log.
196
*/
197
static void bisect_append_skip(int rid){
198
db_multi_exec(
199
"UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
200
);
201
}
202
203
/*
204
** Append a VALUES entry to the bilog table insert
205
*/
206
static void bisect_log_append(Blob *pSql,int iSeq,const char *zStat,int iRid){
207
if( (iSeq%6)==3 ){
208
blob_append_sql(pSql, ",\n ");
209
}else if( iSeq>1 ){
210
blob_append_sql(pSql, ",");
211
}
212
if( zStat ){
213
blob_append_sql(pSql, "(%d,%Q,%d)", iSeq, zStat, iRid);
214
}else{
215
blob_append_sql(pSql, "(NULL,NULL,%d)", iRid);
216
}
217
}
218
219
/*
220
** Create a TEMP table named "bilog" that contains the complete history
221
** of the current bisect.
222
**
223
** If iCurrent>0 then it is the RID of the current check-out and is included
224
** in the history table.
225
**
226
** If zDesc is not NULL, then it is the bid= query parameter to /timeline
227
** that describes a bisect. Use the information in zDesc rather than in
228
** the bisect-log variable.
229
**
230
** If bDetail is true, then also include information about every node
231
** in between the inner-most GOOD and BAD nodes.
232
*/
233
int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
234
char *zLog;
235
Blob log, id;
236
int cnt = 0;
237
int lastGood = -1;
238
int lastBad = -1;
239
Blob ins = BLOB_INITIALIZER;
240
241
if( zDesc!=0 ){
242
blob_init(&log, 0, 0);
243
while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
244
int i;
245
char c;
246
int rid;
247
if( blob_size(&log) ) blob_append(&log, " ", 1);
248
if( zDesc[0]=='n' ) blob_append(&log, "-", 1);
249
if( zDesc[0]=='s' ) blob_append(&log, "s", 1);
250
for(i=1; ((c = zDesc[i])>='0' && c<='9') || (c>='a' && c<='f'); i++){}
251
if( i==1 ) break;
252
rid = db_int(0,
253
"SELECT rid FROM blob"
254
" WHERE uuid LIKE '%.*q%%'"
255
" AND EXISTS(SELECT 1 FROM plink WHERE cid=rid)",
256
i-1, zDesc+1
257
);
258
if( rid==0 ) break;
259
blob_appendf(&log, "%d", rid);
260
zDesc += i;
261
while( zDesc[0]=='-' ) zDesc++;
262
}
263
}else{
264
zLog = db_lget("bisect-log","");
265
blob_init(&log, zLog, -1);
266
}
267
db_multi_exec(
268
"CREATE TEMP TABLE bilog("
269
" rid INTEGER PRIMARY KEY," /* Sequence of events */
270
" stat TEXT," /* Type of occurrence */
271
" seq INTEGER UNIQUE" /* Check-in number */
272
");"
273
);
274
blob_append_sql(&ins, "INSERT OR IGNORE INTO bilog(seq,stat,rid) VALUES");
275
while( blob_token(&log, &id) ){
276
int rid;
277
cnt++;
278
if( blob_str(&id)[0]=='s' ){
279
rid = atoi(blob_str(&id)+1);
280
bisect_log_append(&ins, cnt, "SKIP", rid);
281
}else{
282
rid = atoi(blob_str(&id));
283
if( rid>0 ){
284
bisect_log_append(&ins, cnt, "GOOD", rid);
285
lastGood = rid;
286
}else{
287
bisect_log_append(&ins, cnt, "BAD", -rid);
288
lastBad = -rid;
289
}
290
}
291
}
292
if( iCurrent>0 ){
293
bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
294
}
295
if( bDetail && lastGood>0 && lastBad>0 ){
296
PathNode *p;
297
p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0, 0);
298
while( p ){
299
bisect_log_append(&ins, ++cnt, 0, p->rid);
300
p = p->u.pTo;
301
}
302
path_reset();
303
}
304
db_exec_sql(blob_sql_text(&ins));
305
blob_reset(&ins);
306
return 1;
307
}
308
309
/* Return a permalink description of a bisect. Space is obtained from
310
** fossil_malloc() and should be freed by the caller.
311
**
312
** A bisect description consists of characters 'y' and 'n' and lowercase
313
** hex digits. Each term begins with 'y' or 'n' (success or failure) and
314
** is followed by a hash prefix in lowercase hex.
315
*/
316
char *bisect_permalink(void){
317
char *zLog = db_lget("bisect-log","");
318
char *zResult;
319
Blob log;
320
Blob link = BLOB_INITIALIZER;
321
Blob id;
322
blob_init(&log, zLog, -1);
323
while( blob_token(&log, &id) ){
324
const char *zUuid;
325
int rid;
326
char cPrefix = 'y';
327
if( blob_str(&id)[0]=='s' ){
328
rid = atoi(blob_str(&id)+1);
329
cPrefix = 's';
330
}else{
331
rid = atoi(blob_str(&id));
332
if( rid<0 ){
333
cPrefix = 'n';
334
rid = -rid;
335
}
336
}
337
zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid);
338
if( blob_size(&link)>0 ) blob_append(&link, "-", 1);
339
blob_appendf(&link, "%c%.10s", cPrefix, zUuid);
340
}
341
zResult = fossil_strdup(blob_str(&link));
342
blob_reset(&link);
343
blob_reset(&log);
344
blob_reset(&id);
345
return zResult;
346
}
347
348
/*
349
** Show a chart of bisect "good" and "bad" versions. The chart can be
350
** sorted either chronologically by bisect time, or by check-in time.
351
*/
352
static void bisect_chart(int sortByCkinTime){
353
Stmt q;
354
int iCurrent = db_lget_int("checkout",0);
355
bisect_create_bilog_table(iCurrent, 0, 0);
356
db_prepare(&q,
357
"SELECT bilog.seq, bilog.stat,"
358
" substr(blob.uuid,1,16), datetime(event.mtime),"
359
" blob.rid==%d"
360
" FROM bilog, blob, event"
361
" WHERE blob.rid=bilog.rid AND event.objid=bilog.rid"
362
" AND event.type='ci'"
363
" ORDER BY %s bilog.rowid ASC",
364
iCurrent, (sortByCkinTime ? "event.mtime DESC, " : "")
365
);
366
while( db_step(&q)==SQLITE_ROW ){
367
const char *zGoodBad = db_column_text(&q, 1);
368
fossil_print("%3d %-7s %s %s%s\n",
369
db_column_int(&q, 0),
370
zGoodBad,
371
db_column_text(&q, 3),
372
db_column_text(&q, 2),
373
(db_column_int(&q, 4) && zGoodBad[0]!='C') ? " CURRENT" : "");
374
}
375
db_finalize(&q);
376
}
377
378
379
/*
380
** Reset the bisect subsystem.
381
*/
382
void bisect_reset(void){
383
db_multi_exec(
384
"DELETE FROM vvar WHERE name IN "
385
" ('bisect-good', 'bisect-bad', 'bisect-log', 'bisect-complete',"
386
" 'bisect-linear')"
387
);
388
}
389
390
/*
391
** fossil bisect run [OPTIONS] COMMAND
392
**
393
** Invoke COMMAND (with arguments) repeatedly to perform the bisect.
394
**
395
** Options:
396
** -i|--interactive Prompt user for decisions rather than
397
** using the return code from COMMAND
398
** --ii Like -i but also pause after showing
399
** the status after each step.
400
*/
401
static void bisect_run(void){
402
const char *zCmd;
403
int isInteractive = 0;
404
int i;
405
if( g.argc<4 ){
406
fossil_fatal("Usage: fossil bisect run [OPTIONS] COMMAND\n");
407
}
408
for(i=3; i<g.argc-1; i++){
409
const char *zArg = g.argv[i];
410
if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++;
411
if( strcmp(zArg, "-i")==0 || strcmp(zArg, "-interactive")==0 ){
412
isInteractive = 1;
413
continue;
414
}
415
if( strcmp(zArg, "-ii")==0 ){
416
isInteractive = 2;
417
continue;
418
}
419
fossil_fatal("unknown command-line option: \"%s\"\n", g.argv[i]);
420
}
421
zCmd = g.argv[i];
422
if( db_int(0, "SELECT count(*) FROM vvar"
423
" WHERE name IN ('bisect-good','bisect-bad')")!=2 ){
424
fossil_fatal("need good/bad boundaries to use \"fossil bisect run\"");
425
}
426
while( db_lget_int("bisect-complete",0)==0 ){
427
int rc;
428
Blob cmd;
429
blob_init(&cmd, 0, 0);
430
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
431
rc = fossil_unsafe_system(zCmd);
432
if( isInteractive ){
433
Blob in;
434
fossil_print("test-command result: %d\n", rc);
435
while(1){
436
int n;
437
char *z;
438
prompt_user("Enter (g)ood, (b)ad, (s)kip, (a)uto, (h)alt: ", &in);
439
n = blob_size(&in);
440
z = blob_str(&in);
441
if( n<1 ) continue;
442
if( sqlite3_strnicmp("good", z, n)==0 ){
443
rc = 0;
444
break;
445
}
446
if( sqlite3_strnicmp("bad", z, n)==0 ){
447
rc = 1;
448
break;
449
}
450
if( sqlite3_strnicmp("skip", z, n)==0 ){
451
rc = 125;
452
break;
453
}
454
if( sqlite3_strnicmp("auto", z, n)==0 ){
455
isInteractive = 0;
456
break;
457
}
458
if( sqlite3_strnicmp("halt", z, n)==0 ){
459
return;
460
}
461
blob_reset(&in);
462
}
463
}
464
if( rc==0 ){
465
blob_append(&cmd, " bisect good", -1);
466
}else if( rc==125 ){
467
blob_append(&cmd, " bisect skip", -1);
468
}else{
469
blob_append(&cmd, " bisect bad", -1);
470
}
471
fossil_print("%s\n", blob_str(&cmd));
472
fossil_system(blob_str(&cmd));
473
blob_reset(&cmd);
474
if( isInteractive>=2 && db_lget_int("bisect-complete", 0)==0 ){
475
int n;
476
char *z;
477
Blob in;
478
int bContinue = 1;
479
prompt_user("Run testcase again? (Y)es or No: ", &in);
480
n = blob_size(&in);
481
z = blob_str(&in);
482
if( n>0 && sqlite3_strnicmp("no", z, n)==0 ) bContinue = 0;
483
blob_reset(&in);
484
if( !bContinue ) break;
485
}
486
}
487
}
488
489
/*
490
** COMMAND: bisect
491
**
492
** Usage: %fossil bisect SUBCOMMAND ...
493
**
494
** Run various subcommands useful for searching back through the change
495
** history for a particular check-in that causes or fixes a problem.
496
**
497
** > fossil bisect bad ?VERSION?
498
**
499
** Identify version VERSION as non-working. If VERSION is omitted,
500
** the current check-out is marked as non-working.
501
**
502
** > fossil bisect good ?VERSION?
503
**
504
** Identify version VERSION as working. If VERSION is omitted,
505
** the current check-out is marked as working.
506
**
507
** > fossil bisect log
508
** > fossil bisect chart
509
**
510
** Show a log of "good", "bad", and "skip" versions. "bisect log"
511
** shows the events in the order that they were tested.
512
** "bisect chart" shows them in order of check-in.
513
**
514
** > fossil bisect next
515
**
516
** Update to the next version that is halfway between the working and
517
** non-working versions.
518
**
519
** > fossil bisect options ?NAME? ?VALUE?
520
**
521
** List all bisect options, or the value of a single option, or set the
522
** value of a bisect option.
523
**
524
** > fossil bisect reset
525
**
526
** Reinitialize a bisect session. This cancels prior bisect history
527
** and allows a bisect session to start over from the beginning.
528
**
529
** > fossil bisect run [OPTIONS] COMMAND
530
**
531
** Invoke COMMAND repeatedly to run the bisect. The exit code for
532
** COMMAND should be 0 for "good", 125 for "skip", and any other value
533
** for "bad".
534
**
535
** Options:
536
** -i|--interactive Prompt the user for the good/bad/skip decision
537
** after each step, rather than using the exit
538
** code from COMMAND
539
**
540
** > fossil bisect skip ?VERSION?
541
**
542
** Cause VERSION (or the current check-out if VERSION is omitted) to
543
** be ignored for the purpose of the current bisect. This might
544
** be done, for example, because VERSION does not compile correctly
545
** or is otherwise unsuitable to participate in this bisect.
546
**
547
** > fossil bisect vlist|ls|status ?-a|--all?
548
**
549
** List the versions in between the inner-most "bad" and "good".
550
**
551
** > fossil bisect ui ?HOST@USER:PATH?
552
**
553
** Like "fossil ui" except start on a timeline that shows only the
554
** check-ins that are part of the current bisect. If the optional
555
** fourth term is added, then information is shown for the bisect that
556
** occurred in the PATH directory by USER on remote machine HOST.
557
**
558
** > fossil bisect undo
559
**
560
** Undo the most recent "good", "bad", or "skip" command.
561
*/
562
void bisect_cmd(void){
563
int n;
564
const char *zCmd;
565
int foundCmd = 0;
566
db_must_be_within_tree();
567
if( g.argc<3 ){
568
goto usage;
569
}
570
zCmd = g.argv[2];
571
n = strlen(zCmd);
572
if( n==0 ) zCmd = "-";
573
if( strncmp(zCmd, "bad", n)==0 ){
574
int ridBad;
575
foundCmd = 1;
576
if( g.argc==3 ){
577
ridBad = db_lget_int("checkout",0);
578
}else{
579
ridBad = name_to_typed_rid(g.argv[3], "ci");
580
}
581
if( ridBad>0 ){
582
bisect_append_log(-ridBad);
583
if( bisect_option("auto-next") && db_lget_int("bisect-good",0)>0 ){
584
zCmd = "next";
585
n = 4;
586
}
587
}
588
}else if( strncmp(zCmd, "good", n)==0 ){
589
int ridGood;
590
foundCmd = 1;
591
if( g.argc==3 ){
592
ridGood = db_lget_int("checkout",0);
593
}else{
594
ridGood = name_to_typed_rid(g.argv[3], "ci");
595
}
596
if( ridGood>0 ){
597
bisect_append_log(ridGood);
598
if( bisect_option("auto-next") && db_lget_int("bisect-bad",0)>0 ){
599
zCmd = "next";
600
n = 4;
601
}
602
}
603
}else if( strncmp(zCmd, "skip", n)==0 ){
604
int ridSkip;
605
foundCmd = 1;
606
if( g.argc==3 ){
607
ridSkip = db_lget_int("checkout",0);
608
}else{
609
ridSkip = name_to_typed_rid(g.argv[3], "ci");
610
}
611
if( ridSkip>0 ){
612
bisect_append_skip(ridSkip);
613
if( bisect_option("auto-next")
614
&& db_lget_int("bisect-bad",0)>0
615
&& db_lget_int("bisect-good",0)>0
616
){
617
zCmd = "next";
618
n = 4;
619
}
620
}
621
}else if( strncmp(zCmd, "undo", n)==0 ){
622
char *zLog;
623
Blob log, id;
624
int ridBad = 0;
625
int ridGood = 0;
626
int cnt = 0, i;
627
foundCmd = 1;
628
db_begin_transaction();
629
zLog = db_lget("bisect-log","");
630
blob_init(&log, zLog, -1);
631
while( blob_token(&log, &id) ){ cnt++; }
632
if( cnt==0 ){
633
fossil_fatal("no previous bisect steps to undo");
634
}
635
blob_rewind(&log);
636
for(i=0; i<cnt-1; i++){
637
int rid;
638
blob_token(&log, &id);
639
rid = atoi(blob_str(&id));
640
if( rid<0 ) ridBad = -rid;
641
else ridGood = rid;
642
}
643
db_multi_exec(
644
"UPDATE vvar SET value=substr(value,1,%d) WHERE name='bisect-log'",
645
log.iCursor-1
646
);
647
db_lset_int("bisect-bad", ridBad);
648
db_lset_int("bisect-good", ridGood);
649
db_end_transaction(0);
650
if( ridBad && ridGood ){
651
zCmd = "next";
652
n = 4;
653
}
654
}
655
/* No else here so that the above commands can morph themselves into
656
** a "next" command */
657
if( strncmp(zCmd, "next", n)==0 ){
658
PathNode *pMid;
659
char *zDisplay = db_lget("bisect-display","chart");
660
int m = (int)strlen(zDisplay);
661
bisect_path();
662
if( db_lget_boolean("bisect-linear",0) ){
663
pMid = path_next();
664
if( pMid && pMid->rid==db_lget_int("checkout",0) ) pMid = 0;
665
}else{
666
pMid = path_midpoint();
667
}
668
if( pMid==0 ){
669
fossil_print("bisect complete\n");
670
db_lset_int("bisect-complete",1);
671
}else{
672
int nSpan = path_length_not_hidden();
673
int nStep = path_search_depth();
674
g.argv[1] = "update";
675
g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
676
g.argc = 3;
677
g.fNoSync = 1;
678
update_cmd();
679
fossil_print("span: %d steps-remaining: %d\n", nSpan, nStep);
680
}
681
682
if( strncmp(zDisplay,"chart",m)==0 ){
683
bisect_chart(1);
684
}else if( strncmp(zDisplay, "log", m)==0 ){
685
bisect_chart(0);
686
}else if( strncmp(zDisplay, "status", m)==0 ){
687
bisect_list(1);
688
}
689
}else if( strncmp(zCmd, "log", n)==0 ){
690
bisect_chart(0);
691
}else if( strncmp(zCmd, "chart", n)==0 ){
692
bisect_chart(1);
693
}else if( strncmp(zCmd, "run", n)==0 ){
694
bisect_run();
695
}else if( strncmp(zCmd, "options", n)==0 ){
696
if( g.argc==3 ){
697
unsigned int i;
698
for(i=0; i<count(aBisectOption); i++){
699
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
700
fossil_print(" %-15s %-6s ", aBisectOption[i].zName,
701
db_lget(z, (char*)aBisectOption[i].zDefault));
702
fossil_free(z);
703
comment_print(aBisectOption[i].zDesc, 0, 27, -1, get_comment_format());
704
}
705
}else if( g.argc==4 || g.argc==5 ){
706
unsigned int i;
707
n = strlen(g.argv[3]);
708
for(i=0; i<count(aBisectOption); i++){
709
if( strncmp(g.argv[3], aBisectOption[i].zName, n)==0 ){
710
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
711
if( g.argc==5 ){
712
db_lset(z/*works-like:"bisect-%s"*/, g.argv[4]);
713
}
714
fossil_print("%s\n", db_lget(z, (char*)aBisectOption[i].zDefault));
715
fossil_free(z);
716
break;
717
}
718
}
719
if( i>=count(aBisectOption) ){
720
fossil_fatal("no such bisect option: %s", g.argv[3]);
721
}
722
}else{
723
usage("options ?NAME? ?VALUE?");
724
}
725
}else if( strncmp(zCmd, "reset", n)==0 ){
726
bisect_reset();
727
}else if( strcmp(zCmd, "ui")==0 ){
728
char *newArgv[8];
729
verify_all_options();
730
newArgv[0] = g.argv[0];
731
newArgv[1] = "ui";
732
newArgv[2] = "--page";
733
newArgv[3] = "timeline?bisect";
734
if( g.argc==4 ){
735
newArgv[4] = g.argv[3];
736
g.argc = 5;
737
}else{
738
g.argc = 4;
739
}
740
newArgv[g.argc] = 0;
741
g.argv = newArgv;
742
cmd_webserver();
743
}else if( strncmp(zCmd, "vlist", n)==0
744
|| strncmp(zCmd, "ls", n)==0
745
|| strncmp(zCmd, "status", n)==0
746
){
747
int fAll = find_option("all", "a", 0)!=0;
748
bisect_list(!fAll);
749
}else if( !foundCmd ){
750
usage:
751
usage("bad|good|log|chart|next|options|reset|run|skip|status|ui|undo");
752
}
753
}
754

Keyboard Shortcuts

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