| | @@ -37,17 +37,30 @@ |
| 37 | 37 | void bisect_path(void){ |
| 38 | 38 | PathNode *p; |
| 39 | 39 | bisect.bad = db_lget_int("bisect-bad", 0); |
| 40 | 40 | bisect.good = db_lget_int("bisect-good", 0); |
| 41 | 41 | if( bisect.good>0 && bisect.bad==0 ){ |
| 42 | | - path_shortest(bisect.good, bisect.good, 0, 0); |
| 42 | + path_shortest(bisect.good, bisect.good, 0, 0, 0); |
| 43 | 43 | }else if( bisect.bad>0 && bisect.good==0 ){ |
| 44 | | - path_shortest(bisect.bad, bisect.bad, 0, 0); |
| 44 | + path_shortest(bisect.bad, bisect.bad, 0, 0, 0); |
| 45 | 45 | }else if( bisect.bad==0 && bisect.good==0 ){ |
| 46 | 46 | fossil_fatal("neither \"good\" nor \"bad\" versions have been identified"); |
| 47 | 47 | }else{ |
| 48 | | - p = path_shortest(bisect.good, bisect.bad, bisect_option("direct-only"), 0); |
| 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); |
| 61 | + bag_clear(&skip); |
| 49 | 62 | if( p==0 ){ |
| 50 | 63 | char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); |
| 51 | 64 | char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); |
| 52 | 65 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| 53 | 66 | zGood, zBad); |
| | @@ -167,31 +180,51 @@ |
| 167 | 180 | db_multi_exec( |
| 168 | 181 | "REPLACE INTO vvar(name,value) VALUES('bisect-log'," |
| 169 | 182 | "COALESCE((SELECT value||' ' FROM vvar WHERE name='bisect-log'),'')" |
| 170 | 183 | " || '%d')", rid); |
| 171 | 184 | } |
| 185 | + |
| 186 | +/* |
| 187 | +** Append a new skip entry to the bisect log. |
| 188 | +*/ |
| 189 | +static void bisect_append_skip(int rid){ |
| 190 | + db_multi_exec( |
| 191 | + "UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid |
| 192 | + ); |
| 193 | +} |
| 172 | 194 | |
| 173 | 195 | /* |
| 174 | 196 | ** Create a TEMP table named "bilog" that contains the complete history |
| 175 | 197 | ** of the current bisect. |
| 198 | +** |
| 199 | +** If iCurrent>0 then it is the RID of the current checkout and is included |
| 200 | +** in the history table. |
| 201 | +** |
| 202 | +** If zDesc is not NULL, then it is the bid= query parameter to /timeline |
| 203 | +** that describes a bisect. Use the information in zDesc rather than in |
| 204 | +** the bisect-log variable. |
| 205 | +** |
| 206 | +** If bDetail is true, then also include information about every node |
| 207 | +** in between the inner-most GOOD and BAD nodes. |
| 176 | 208 | */ |
| 177 | | -int bisect_create_bilog_table(int iCurrent, const char *zDesc){ |
| 209 | +int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){ |
| 178 | 210 | char *zLog; |
| 179 | 211 | Blob log, id; |
| 180 | 212 | Stmt q; |
| 181 | 213 | int cnt = 0; |
| 182 | 214 | int lastGood = -1; |
| 183 | 215 | int lastBad = -1; |
| 184 | 216 | |
| 185 | 217 | if( zDesc!=0 ){ |
| 186 | 218 | blob_init(&log, 0, 0); |
| 187 | | - while( zDesc[0]=='y' || zDesc[0]=='n' ){ |
| 219 | + while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){ |
| 188 | 220 | int i; |
| 189 | 221 | char c; |
| 190 | 222 | int rid; |
| 191 | 223 | if( blob_size(&log) ) blob_append(&log, " ", 1); |
| 192 | 224 | if( zDesc[0]=='n' ) blob_append(&log, "-", 1); |
| 225 | + if( zDesc[0]=='s' ) blob_append(&log, "s", 1); |
| 193 | 226 | for(i=1; ((c = zDesc[i])>='0' && c<='9') || (c>='a' && c<='f'); i++){} |
| 194 | 227 | if( i==1 ) break; |
| 195 | 228 | rid = db_int(0, |
| 196 | 229 | "SELECT rid FROM blob" |
| 197 | 230 | " WHERE uuid LIKE '%.*q%%'" |
| | @@ -214,20 +247,27 @@ |
| 214 | 247 | ");" |
| 215 | 248 | ); |
| 216 | 249 | db_prepare(&q, "INSERT OR IGNORE INTO bilog(seq,stat,rid)" |
| 217 | 250 | " VALUES(:seq,:stat,:rid)"); |
| 218 | 251 | while( blob_token(&log, &id) ){ |
| 219 | | - int rid = atoi(blob_str(&id)); |
| 252 | + int rid; |
| 220 | 253 | db_bind_int(&q, ":seq", ++cnt); |
| 221 | | - if( rid>0 ){ |
| 222 | | - db_bind_text(&q, ":stat","GOOD"); |
| 254 | + if( blob_str(&id)[0]=='s' ){ |
| 255 | + rid = atoi(blob_str(&id)+1); |
| 256 | + db_bind_text(&q, ":stat", "SKIP"); |
| 223 | 257 | db_bind_int(&q, ":rid", rid); |
| 224 | | - lastGood = rid; |
| 225 | 258 | }else{ |
| 226 | | - db_bind_text(&q, ":stat", "BAD"); |
| 227 | | - db_bind_int(&q, ":rid", -rid); |
| 228 | | - lastBad = -rid; |
| 259 | + rid = atoi(blob_str(&id)); |
| 260 | + if( rid>0 ){ |
| 261 | + db_bind_text(&q, ":stat","GOOD"); |
| 262 | + db_bind_int(&q, ":rid", rid); |
| 263 | + lastGood = rid; |
| 264 | + }else{ |
| 265 | + db_bind_text(&q, ":stat", "BAD"); |
| 266 | + db_bind_int(&q, ":rid", -rid); |
| 267 | + lastBad = -rid; |
| 268 | + } |
| 229 | 269 | } |
| 230 | 270 | db_step(&q); |
| 231 | 271 | db_reset(&q); |
| 232 | 272 | } |
| 233 | 273 | if( iCurrent>0 ){ |
| | @@ -235,13 +275,13 @@ |
| 235 | 275 | db_bind_text(&q, ":stat", "CURRENT"); |
| 236 | 276 | db_bind_int(&q, ":rid", iCurrent); |
| 237 | 277 | db_step(&q); |
| 238 | 278 | db_reset(&q); |
| 239 | 279 | } |
| 240 | | - if( lastGood>0 && lastBad>0 ){ |
| 280 | + if( bDetail && lastGood>0 && lastBad>0 ){ |
| 241 | 281 | PathNode *p; |
| 242 | | - p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0); |
| 282 | + p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0); |
| 243 | 283 | while( p ){ |
| 244 | 284 | db_bind_null(&q, ":seq"); |
| 245 | 285 | db_bind_null(&q, ":stat"); |
| 246 | 286 | db_bind_int(&q, ":rid", p->rid); |
| 247 | 287 | db_step(&q); |
| | @@ -267,14 +307,25 @@ |
| 267 | 307 | Blob log; |
| 268 | 308 | Blob link = BLOB_INITIALIZER; |
| 269 | 309 | Blob id; |
| 270 | 310 | blob_init(&log, zLog, -1); |
| 271 | 311 | while( blob_token(&log, &id) ){ |
| 272 | | - int rid = atoi(blob_str(&id)); |
| 273 | | - char *zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", |
| 274 | | - rid<0 ? -rid : rid); |
| 275 | | - blob_appendf(&link, "%c%.10s", rid<0 ? 'n' : 'y', zUuid); |
| 312 | + const char *zUuid; |
| 313 | + int rid; |
| 314 | + char cPrefix = 'y'; |
| 315 | + if( blob_str(&id)[0]=='s' ){ |
| 316 | + rid = atoi(blob_str(&id)+1); |
| 317 | + cPrefix = 's'; |
| 318 | + }else{ |
| 319 | + rid = atoi(blob_str(&id)); |
| 320 | + if( rid<0 ){ |
| 321 | + cPrefix = 'n'; |
| 322 | + rid = -rid; |
| 323 | + } |
| 324 | + } |
| 325 | + zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid); |
| 326 | + blob_appendf(&link, "%c%.10s", cPrefix, zUuid); |
| 276 | 327 | } |
| 277 | 328 | zResult = mprintf("%s", blob_str(&link)); |
| 278 | 329 | blob_reset(&link); |
| 279 | 330 | blob_reset(&log); |
| 280 | 331 | blob_reset(&id); |
| | @@ -286,11 +337,11 @@ |
| 286 | 337 | ** sorted either chronologically by bisect time, or by check-in time. |
| 287 | 338 | */ |
| 288 | 339 | static void bisect_chart(int sortByCkinTime){ |
| 289 | 340 | Stmt q; |
| 290 | 341 | int iCurrent = db_lget_int("checkout",0); |
| 291 | | - bisect_create_bilog_table(iCurrent, 0); |
| 342 | + bisect_create_bilog_table(iCurrent, 0, 0); |
| 292 | 343 | db_prepare(&q, |
| 293 | 344 | "SELECT bilog.seq, bilog.stat," |
| 294 | 345 | " substr(blob.uuid,1,16), datetime(event.mtime)," |
| 295 | 346 | " blob.rid==%d" |
| 296 | 347 | " FROM bilog, blob, event" |
| | @@ -359,10 +410,17 @@ |
| 359 | 410 | ** > fossil bisect reset |
| 360 | 411 | ** |
| 361 | 412 | ** Reinitialize a bisect session. This cancels prior bisect history |
| 362 | 413 | ** and allows a bisect session to start over from the beginning. |
| 363 | 414 | ** |
| 415 | +** > fossil bisect skip ?VERSION? |
| 416 | +** |
| 417 | +** Cause VERSION (or the current checkout if VERSION is omitted) to |
| 418 | +** be ignored for the purpose of the current bisect. This might |
| 419 | +** be done, for example, because VERSION does not compile correctly |
| 420 | +** or is otherwise unsuitable to participate in this bisect. |
| 421 | +** |
| 364 | 422 | ** > fossil bisect vlist|ls|status ?-a|--all? |
| 365 | 423 | ** |
| 366 | 424 | ** List the versions in between "bad" and "good". |
| 367 | 425 | ** |
| 368 | 426 | ** > fossil bisect ui |
| | @@ -424,10 +482,28 @@ |
| 424 | 482 | bisect_append_log(ridGood); |
| 425 | 483 | if( bisect_option("auto-next") && db_lget_int("bisect-bad",0)>0 ){ |
| 426 | 484 | zCmd = "next"; |
| 427 | 485 | n = 4; |
| 428 | 486 | } |
| 487 | + } |
| 488 | + }else if( strncmp(zCmd, "skip", n)==0 ){ |
| 489 | + int ridSkip; |
| 490 | + foundCmd = 1; |
| 491 | + if( g.argc==3 ){ |
| 492 | + ridSkip = db_lget_int("checkout",0); |
| 493 | + }else{ |
| 494 | + ridSkip = name_to_typed_rid(g.argv[3], "ci"); |
| 495 | + } |
| 496 | + if( ridSkip>0 ){ |
| 497 | + bisect_append_skip(ridSkip); |
| 498 | + if( bisect_option("auto-next") |
| 499 | + && db_lget_int("bisect-bad",0)>0 |
| 500 | + && db_lget_int("bisect-good",0)>0 |
| 501 | + ){ |
| 502 | + zCmd = "next"; |
| 503 | + n = 4; |
| 504 | + } |
| 429 | 505 | } |
| 430 | 506 | }else if( strncmp(zCmd, "undo", n)==0 ){ |
| 431 | 507 | char *zLog; |
| 432 | 508 | Blob log, id; |
| 433 | 509 | int ridBad = 0; |
| | @@ -470,11 +546,11 @@ |
| 470 | 546 | bisect_path(); |
| 471 | 547 | pMid = path_midpoint(); |
| 472 | 548 | if( pMid==0 ){ |
| 473 | 549 | fossil_print("bisect complete\n"); |
| 474 | 550 | }else{ |
| 475 | | - int nSpan = path_length(); |
| 551 | + int nSpan = path_length_not_hidden(); |
| 476 | 552 | int nStep = path_search_depth(); |
| 477 | 553 | g.argv[1] = "update"; |
| 478 | 554 | g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); |
| 479 | 555 | g.argc = 3; |
| 480 | 556 | g.fNoSync = 1; |
| 481 | 557 | |