Fossil SCM
Enhancements to the "bisect" command: Add "bisect log" and "bisect undo". The "bisect status" command is an alias for "bisect vlist". Show the adjacent good and bad versions on the final step of the bisect.
Commit
41c7ac29d7075ac003e1397d856d739a5b133493
Parent
eb3c9b34dfc8df0…
1 file changed
+140
-39
+140
-39
| --- src/bisect.c | ||
| +++ src/bisect.c | ||
| @@ -35,23 +35,25 @@ | ||
| 35 | 35 | ** Find the shortest path between bad and good. |
| 36 | 36 | */ |
| 37 | 37 | void bisect_path(void){ |
| 38 | 38 | PathNode *p; |
| 39 | 39 | bisect.bad = db_lget_int("bisect-bad", 0); |
| 40 | - if( bisect.bad==0 ){ | |
| 41 | - fossil_fatal("no \"bad\" version has been identified"); | |
| 42 | - } | |
| 43 | 40 | bisect.good = db_lget_int("bisect-good", 0); |
| 44 | - if( bisect.good==0 ){ | |
| 45 | - fossil_fatal("no \"good\" version has been identified"); | |
| 46 | - } | |
| 47 | - p = path_shortest(bisect.good, bisect.bad, bisect_option("direct-only"), 0); | |
| 48 | - if( p==0 ){ | |
| 49 | - char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad); | |
| 50 | - char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good); | |
| 51 | - fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", | |
| 52 | - zGood, zBad); | |
| 41 | + if( bisect.good>0 && bisect.bad==0 ){ | |
| 42 | + path_shortest(bisect.good, bisect.good, 0, 0); | |
| 43 | + }else if( bisect.bad>0 && bisect.good==0 ){ | |
| 44 | + path_shortest(bisect.bad, bisect.bad, 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 | + p = path_shortest(bisect.good, bisect.bad, bisect_option("direct-only"), 0); | |
| 49 | + if( p==0 ){ | |
| 50 | + char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); | |
| 51 | + char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); | |
| 52 | + fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", | |
| 53 | + zGood, zBad); | |
| 54 | + } | |
| 53 | 55 | } |
| 54 | 56 | } |
| 55 | 57 | |
| 56 | 58 | /* |
| 57 | 59 | ** The set of all bisect options. |
| @@ -141,10 +143,32 @@ | ||
| 141 | 143 | } |
| 142 | 144 | db_reset(&s); |
| 143 | 145 | } |
| 144 | 146 | db_finalize(&s); |
| 145 | 147 | } |
| 148 | + | |
| 149 | +/* | |
| 150 | +** Append a new entry to the bisect log. Update the bisect-good or | |
| 151 | +** bisect-bad values as appropriate. | |
| 152 | +** | |
| 153 | +** The bisect-log consists of a list of token. Each token is an | |
| 154 | +** integer RID of a check-in. The RID is negative for "bad" check-ins | |
| 155 | +** and positive for "good" check-ins. | |
| 156 | +*/ | |
| 157 | +static void bisect_append_log(int rid){ | |
| 158 | + if( rid<0 ){ | |
| 159 | + if( db_lget_int("bisect-bad",0)==(-rid) ) return; | |
| 160 | + db_lset_int("bisect-bad", -rid); | |
| 161 | + }else{ | |
| 162 | + if( db_lget_int("bisect-good",0)==rid ) return; | |
| 163 | + db_lset_int("bisect-good", rid); | |
| 164 | + } | |
| 165 | + db_multi_exec( | |
| 166 | + "REPLACE INTO vvar(name,value) VALUES('bisect-log'," | |
| 167 | + "COALESCE((SELECT value||' ' FROM vvar WHERE name='bisect-log'),'')" | |
| 168 | + " || '%d')", rid); | |
| 169 | +} | |
| 146 | 170 | |
| 147 | 171 | /* |
| 148 | 172 | ** COMMAND: bisect |
| 149 | 173 | ** |
| 150 | 174 | ** Usage: %fossil bisect SUBCOMMAND ... |
| @@ -159,10 +183,14 @@ | ||
| 159 | 183 | ** fossil bisect good ?VERSION? |
| 160 | 184 | ** |
| 161 | 185 | ** Identify version VERSION as working. If VERSION is omitted, |
| 162 | 186 | ** the current checkout is marked as working. |
| 163 | 187 | ** |
| 188 | +** fossil bisect log | |
| 189 | +** | |
| 190 | +** Show a log of "good" and "bad" versions | |
| 191 | +** | |
| 164 | 192 | ** fossil bisect next |
| 165 | 193 | ** |
| 166 | 194 | ** Update to the next version that is halfway between the working and |
| 167 | 195 | ** non-working versions. |
| 168 | 196 | ** |
| @@ -174,72 +202,141 @@ | ||
| 174 | 202 | ** fossil bisect reset |
| 175 | 203 | ** |
| 176 | 204 | ** Reinitialize a bisect session. This cancels prior bisect history |
| 177 | 205 | ** and allows a bisect session to start over from the beginning. |
| 178 | 206 | ** |
| 179 | -** fossil bisect vlist|ls ?--all? | |
| 207 | +** fossil bisect vlist|ls|status ?--all? | |
| 180 | 208 | ** |
| 181 | 209 | ** List the versions in between "bad" and "good". |
| 210 | +** | |
| 211 | +** fossil bisect undo | |
| 212 | +** | |
| 213 | +** Undo the most recent "good" or "bad" command. | |
| 214 | +** | |
| 215 | +** Summary: | |
| 216 | +** | |
| 217 | +** fossil bisect bad ?VERSION? | |
| 218 | +** fossil bisect good ?VERSION? | |
| 219 | +** fossil bisect log | |
| 220 | +** fossil bisect next | |
| 221 | +** fossil bisect options | |
| 222 | +** fossil bisect reset | |
| 223 | +** fossil bisect status | |
| 224 | +** fossil bisect undo | |
| 182 | 225 | */ |
| 183 | 226 | void bisect_cmd(void){ |
| 184 | 227 | int n; |
| 185 | 228 | const char *zCmd; |
| 229 | + int foundCmd = 0; | |
| 186 | 230 | db_must_be_within_tree(); |
| 187 | 231 | if( g.argc<3 ){ |
| 188 | - usage("bisect SUBCOMMAND ARGS..."); | |
| 232 | + usage("bad|good|log|next|options|reset|status|undo"); | |
| 189 | 233 | } |
| 190 | 234 | zCmd = g.argv[2]; |
| 191 | 235 | n = strlen(zCmd); |
| 192 | 236 | if( n==0 ) zCmd = "-"; |
| 193 | 237 | if( memcmp(zCmd, "bad", n)==0 ){ |
| 194 | 238 | int ridBad; |
| 239 | + foundCmd = 1; | |
| 195 | 240 | if( g.argc==3 ){ |
| 196 | 241 | ridBad = db_lget_int("checkout",0); |
| 197 | 242 | }else{ |
| 198 | 243 | ridBad = name_to_typed_rid(g.argv[3], "ci"); |
| 199 | 244 | } |
| 200 | - db_lset_int("bisect-bad", ridBad); | |
| 201 | - if( ridBad>0 | |
| 202 | - && bisect_option("auto-next") | |
| 203 | - && db_lget_int("bisect-good",0)>0 | |
| 204 | - ){ | |
| 205 | - zCmd = "next"; | |
| 206 | - n = 4; | |
| 207 | - }else{ | |
| 208 | - return; | |
| 245 | + if( ridBad>0 ){ | |
| 246 | + bisect_append_log(-ridBad); | |
| 247 | + if( bisect_option("auto-next") && db_lget_int("bisect-good",0)>0 ){ | |
| 248 | + zCmd = "next"; | |
| 249 | + n = 4; | |
| 250 | + } | |
| 209 | 251 | } |
| 210 | 252 | }else if( memcmp(zCmd, "good", n)==0 ){ |
| 211 | 253 | int ridGood; |
| 254 | + foundCmd = 1; | |
| 212 | 255 | if( g.argc==3 ){ |
| 213 | 256 | ridGood = db_lget_int("checkout",0); |
| 214 | 257 | }else{ |
| 215 | 258 | ridGood = name_to_typed_rid(g.argv[3], "ci"); |
| 216 | 259 | } |
| 260 | + if( ridGood>0 ){ | |
| 261 | + bisect_append_log(ridGood); | |
| 262 | + if( bisect_option("auto-next") && db_lget_int("bisect-bad",0)>0 ){ | |
| 263 | + zCmd = "next"; | |
| 264 | + n = 4; | |
| 265 | + } | |
| 266 | + } | |
| 267 | + }else if( memcmp(zCmd, "undo", n)==0 ){ | |
| 268 | + char *zLog; | |
| 269 | + Blob log, id; | |
| 270 | + int ridBad = 0; | |
| 271 | + int ridGood = 0; | |
| 272 | + foundCmd = 1; | |
| 273 | + int cnt = 0, i; | |
| 274 | + db_begin_transaction(); | |
| 275 | + zLog = db_lget("bisect-log",""); | |
| 276 | + blob_init(&log, zLog, -1); | |
| 277 | + while( blob_token(&log, &id) ){ cnt++; } | |
| 278 | + if( cnt==0 ){ | |
| 279 | + fossil_fatal("no previous bisect steps to undo"); | |
| 280 | + } | |
| 281 | + blob_rewind(&log); | |
| 282 | + for(i=0; i<cnt-1; i++){ | |
| 283 | + int rid; | |
| 284 | + blob_token(&log, &id); | |
| 285 | + rid = atoi(blob_str(&id)); | |
| 286 | + if( rid<0 ) ridBad = -rid; | |
| 287 | + else ridGood = rid; | |
| 288 | + } | |
| 289 | + db_multi_exec( | |
| 290 | + "UPDATE vvar SET value=substr(value,1,%d) WHERE name='bisect-log'", | |
| 291 | + log.iCursor-1 | |
| 292 | + ); | |
| 293 | + db_lset_int("bisect-bad", ridBad); | |
| 217 | 294 | db_lset_int("bisect-good", ridGood); |
| 218 | - if( ridGood>0 | |
| 219 | - && bisect_option("auto-next") | |
| 220 | - && db_lget_int("bisect-bad",0)>0 | |
| 221 | - ){ | |
| 295 | + db_end_transaction(0); | |
| 296 | + if( ridBad && ridGood ){ | |
| 222 | 297 | zCmd = "next"; |
| 223 | 298 | n = 4; |
| 224 | - }else{ | |
| 225 | - return; | |
| 226 | 299 | } |
| 227 | 300 | } |
| 301 | + /* No else here so that the above commands can morph themselves into | |
| 302 | + ** a "next" command */ | |
| 228 | 303 | if( memcmp(zCmd, "next", n)==0 ){ |
| 229 | 304 | PathNode *pMid; |
| 230 | 305 | bisect_path(); |
| 231 | 306 | pMid = path_midpoint(); |
| 232 | 307 | if( pMid==0 ){ |
| 233 | - fossil_fatal("bisect is done - there are no more intermediate versions"); | |
| 308 | + fossil_print("bisect complete\n"); | |
| 309 | + }else{ | |
| 310 | + g.argv[1] = "update"; | |
| 311 | + g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); | |
| 312 | + g.argc = 3; | |
| 313 | + g.fNoSync = 1; | |
| 314 | + update_cmd(); | |
| 234 | 315 | } |
| 235 | - g.argv[1] = "update"; | |
| 236 | - g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); | |
| 237 | - g.argc = 3; | |
| 238 | - g.fNoSync = 1; | |
| 239 | - update_cmd(); | |
| 240 | 316 | bisect_list(1); |
| 317 | + }else if( memcmp(zCmd, "log", n)==0 ){ | |
| 318 | + char *zLog = db_lget("bisect-log",""); | |
| 319 | + Blob log, id; | |
| 320 | + Stmt q; | |
| 321 | + int cnt = 0; | |
| 322 | + blob_init(&log, zLog, -1); | |
| 323 | + db_prepare(&q, "SELECT substr(blob.uuid,1,16), datetime(event.mtime)" | |
| 324 | + " FROM blob, event" | |
| 325 | + " WHERE blob.rid=:rid AND event.objid=:rid" | |
| 326 | + " AND event.type='ci'"); | |
| 327 | + while( blob_token(&log, &id) ){ | |
| 328 | + int rid = atoi(blob_str(&id)); | |
| 329 | + db_bind_int(&q, ":rid", rid<0 ? -rid : rid); | |
| 330 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 331 | + cnt++; | |
| 332 | + fossil_print("%3d %-4s %s %s\n", cnt, rid<0 ? "BAD" : "GOOD", | |
| 333 | + db_column_text(&q, 1), db_column_text(&q, 0)); | |
| 334 | + } | |
| 335 | + db_reset(&q); | |
| 336 | + } | |
| 337 | + db_finalize(&q); | |
| 241 | 338 | }else if( memcmp(zCmd, "options", n)==0 ){ |
| 242 | 339 | if( g.argc==3 ){ |
| 243 | 340 | unsigned int i; |
| 244 | 341 | for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){ |
| 245 | 342 | char *z = mprintf("bisect-%s", aBisectOption[i].zName); |
| @@ -268,14 +365,18 @@ | ||
| 268 | 365 | }else{ |
| 269 | 366 | usage("bisect option ?NAME? ?VALUE?"); |
| 270 | 367 | } |
| 271 | 368 | }else if( memcmp(zCmd, "reset", n)==0 ){ |
| 272 | 369 | db_multi_exec( |
| 273 | - "DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');" | |
| 370 | + "DELETE FROM vvar WHERE name IN " | |
| 371 | + " ('bisect-good', 'bisect-bad', 'bisect-log')" | |
| 274 | 372 | ); |
| 275 | - }else if( memcmp(zCmd, "vlist", n)==0 || memcmp(zCmd, "ls", n)==0 ){ | |
| 373 | + }else if( memcmp(zCmd, "vlist", n)==0 | |
| 374 | + || memcmp(zCmd, "ls", n)==0 | |
| 375 | + || memcmp(zCmd, "status", n)==0 | |
| 376 | + ){ | |
| 276 | 377 | int fAll = find_option("all", 0, 0)!=0; |
| 277 | 378 | bisect_list(!fAll); |
| 278 | - }else{ | |
| 279 | - usage("bad|good|next|reset|vlist ..."); | |
| 379 | + }else if( !foundCmd ){ | |
| 380 | + usage("bad|good|log|next|options|reset|status|undo"); | |
| 280 | 381 | } |
| 281 | 382 | } |
| 282 | 383 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -35,23 +35,25 @@ | |
| 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 | if( bisect.bad==0 ){ |
| 41 | fossil_fatal("no \"bad\" version has been identified"); |
| 42 | } |
| 43 | bisect.good = db_lget_int("bisect-good", 0); |
| 44 | if( bisect.good==0 ){ |
| 45 | fossil_fatal("no \"good\" version has been identified"); |
| 46 | } |
| 47 | p = path_shortest(bisect.good, bisect.bad, bisect_option("direct-only"), 0); |
| 48 | if( p==0 ){ |
| 49 | char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad); |
| 50 | char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good); |
| 51 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| 52 | zGood, zBad); |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | /* |
| 57 | ** The set of all bisect options. |
| @@ -141,10 +143,32 @@ | |
| 141 | } |
| 142 | db_reset(&s); |
| 143 | } |
| 144 | db_finalize(&s); |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | ** COMMAND: bisect |
| 149 | ** |
| 150 | ** Usage: %fossil bisect SUBCOMMAND ... |
| @@ -159,10 +183,14 @@ | |
| 159 | ** fossil bisect good ?VERSION? |
| 160 | ** |
| 161 | ** Identify version VERSION as working. If VERSION is omitted, |
| 162 | ** the current checkout is marked as working. |
| 163 | ** |
| 164 | ** fossil bisect next |
| 165 | ** |
| 166 | ** Update to the next version that is halfway between the working and |
| 167 | ** non-working versions. |
| 168 | ** |
| @@ -174,72 +202,141 @@ | |
| 174 | ** fossil bisect reset |
| 175 | ** |
| 176 | ** Reinitialize a bisect session. This cancels prior bisect history |
| 177 | ** and allows a bisect session to start over from the beginning. |
| 178 | ** |
| 179 | ** fossil bisect vlist|ls ?--all? |
| 180 | ** |
| 181 | ** List the versions in between "bad" and "good". |
| 182 | */ |
| 183 | void bisect_cmd(void){ |
| 184 | int n; |
| 185 | const char *zCmd; |
| 186 | db_must_be_within_tree(); |
| 187 | if( g.argc<3 ){ |
| 188 | usage("bisect SUBCOMMAND ARGS..."); |
| 189 | } |
| 190 | zCmd = g.argv[2]; |
| 191 | n = strlen(zCmd); |
| 192 | if( n==0 ) zCmd = "-"; |
| 193 | if( memcmp(zCmd, "bad", n)==0 ){ |
| 194 | int ridBad; |
| 195 | if( g.argc==3 ){ |
| 196 | ridBad = db_lget_int("checkout",0); |
| 197 | }else{ |
| 198 | ridBad = name_to_typed_rid(g.argv[3], "ci"); |
| 199 | } |
| 200 | db_lset_int("bisect-bad", ridBad); |
| 201 | if( ridBad>0 |
| 202 | && bisect_option("auto-next") |
| 203 | && db_lget_int("bisect-good",0)>0 |
| 204 | ){ |
| 205 | zCmd = "next"; |
| 206 | n = 4; |
| 207 | }else{ |
| 208 | return; |
| 209 | } |
| 210 | }else if( memcmp(zCmd, "good", n)==0 ){ |
| 211 | int ridGood; |
| 212 | if( g.argc==3 ){ |
| 213 | ridGood = db_lget_int("checkout",0); |
| 214 | }else{ |
| 215 | ridGood = name_to_typed_rid(g.argv[3], "ci"); |
| 216 | } |
| 217 | db_lset_int("bisect-good", ridGood); |
| 218 | if( ridGood>0 |
| 219 | && bisect_option("auto-next") |
| 220 | && db_lget_int("bisect-bad",0)>0 |
| 221 | ){ |
| 222 | zCmd = "next"; |
| 223 | n = 4; |
| 224 | }else{ |
| 225 | return; |
| 226 | } |
| 227 | } |
| 228 | if( memcmp(zCmd, "next", n)==0 ){ |
| 229 | PathNode *pMid; |
| 230 | bisect_path(); |
| 231 | pMid = path_midpoint(); |
| 232 | if( pMid==0 ){ |
| 233 | fossil_fatal("bisect is done - there are no more intermediate versions"); |
| 234 | } |
| 235 | g.argv[1] = "update"; |
| 236 | g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); |
| 237 | g.argc = 3; |
| 238 | g.fNoSync = 1; |
| 239 | update_cmd(); |
| 240 | bisect_list(1); |
| 241 | }else if( memcmp(zCmd, "options", n)==0 ){ |
| 242 | if( g.argc==3 ){ |
| 243 | unsigned int i; |
| 244 | for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){ |
| 245 | char *z = mprintf("bisect-%s", aBisectOption[i].zName); |
| @@ -268,14 +365,18 @@ | |
| 268 | }else{ |
| 269 | usage("bisect option ?NAME? ?VALUE?"); |
| 270 | } |
| 271 | }else if( memcmp(zCmd, "reset", n)==0 ){ |
| 272 | db_multi_exec( |
| 273 | "DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');" |
| 274 | ); |
| 275 | }else if( memcmp(zCmd, "vlist", n)==0 || memcmp(zCmd, "ls", n)==0 ){ |
| 276 | int fAll = find_option("all", 0, 0)!=0; |
| 277 | bisect_list(!fAll); |
| 278 | }else{ |
| 279 | usage("bad|good|next|reset|vlist ..."); |
| 280 | } |
| 281 | } |
| 282 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -35,23 +35,25 @@ | |
| 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); |
| 43 | }else if( bisect.bad>0 && bisect.good==0 ){ |
| 44 | path_shortest(bisect.bad, bisect.bad, 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 | p = path_shortest(bisect.good, bisect.bad, bisect_option("direct-only"), 0); |
| 49 | if( p==0 ){ |
| 50 | char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); |
| 51 | char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); |
| 52 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| 53 | zGood, zBad); |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | /* |
| 59 | ** The set of all bisect options. |
| @@ -141,10 +143,32 @@ | |
| 143 | } |
| 144 | db_reset(&s); |
| 145 | } |
| 146 | db_finalize(&s); |
| 147 | } |
| 148 | |
| 149 | /* |
| 150 | ** Append a new entry to the bisect log. Update the bisect-good or |
| 151 | ** bisect-bad values as appropriate. |
| 152 | ** |
| 153 | ** The bisect-log consists of a list of token. Each token is an |
| 154 | ** integer RID of a check-in. The RID is negative for "bad" check-ins |
| 155 | ** and positive for "good" check-ins. |
| 156 | */ |
| 157 | static void bisect_append_log(int rid){ |
| 158 | if( rid<0 ){ |
| 159 | if( db_lget_int("bisect-bad",0)==(-rid) ) return; |
| 160 | db_lset_int("bisect-bad", -rid); |
| 161 | }else{ |
| 162 | if( db_lget_int("bisect-good",0)==rid ) return; |
| 163 | db_lset_int("bisect-good", rid); |
| 164 | } |
| 165 | db_multi_exec( |
| 166 | "REPLACE INTO vvar(name,value) VALUES('bisect-log'," |
| 167 | "COALESCE((SELECT value||' ' FROM vvar WHERE name='bisect-log'),'')" |
| 168 | " || '%d')", rid); |
| 169 | } |
| 170 | |
| 171 | /* |
| 172 | ** COMMAND: bisect |
| 173 | ** |
| 174 | ** Usage: %fossil bisect SUBCOMMAND ... |
| @@ -159,10 +183,14 @@ | |
| 183 | ** fossil bisect good ?VERSION? |
| 184 | ** |
| 185 | ** Identify version VERSION as working. If VERSION is omitted, |
| 186 | ** the current checkout is marked as working. |
| 187 | ** |
| 188 | ** fossil bisect log |
| 189 | ** |
| 190 | ** Show a log of "good" and "bad" versions |
| 191 | ** |
| 192 | ** fossil bisect next |
| 193 | ** |
| 194 | ** Update to the next version that is halfway between the working and |
| 195 | ** non-working versions. |
| 196 | ** |
| @@ -174,72 +202,141 @@ | |
| 202 | ** fossil bisect reset |
| 203 | ** |
| 204 | ** Reinitialize a bisect session. This cancels prior bisect history |
| 205 | ** and allows a bisect session to start over from the beginning. |
| 206 | ** |
| 207 | ** fossil bisect vlist|ls|status ?--all? |
| 208 | ** |
| 209 | ** List the versions in between "bad" and "good". |
| 210 | ** |
| 211 | ** fossil bisect undo |
| 212 | ** |
| 213 | ** Undo the most recent "good" or "bad" command. |
| 214 | ** |
| 215 | ** Summary: |
| 216 | ** |
| 217 | ** fossil bisect bad ?VERSION? |
| 218 | ** fossil bisect good ?VERSION? |
| 219 | ** fossil bisect log |
| 220 | ** fossil bisect next |
| 221 | ** fossil bisect options |
| 222 | ** fossil bisect reset |
| 223 | ** fossil bisect status |
| 224 | ** fossil bisect undo |
| 225 | */ |
| 226 | void bisect_cmd(void){ |
| 227 | int n; |
| 228 | const char *zCmd; |
| 229 | int foundCmd = 0; |
| 230 | db_must_be_within_tree(); |
| 231 | if( g.argc<3 ){ |
| 232 | usage("bad|good|log|next|options|reset|status|undo"); |
| 233 | } |
| 234 | zCmd = g.argv[2]; |
| 235 | n = strlen(zCmd); |
| 236 | if( n==0 ) zCmd = "-"; |
| 237 | if( memcmp(zCmd, "bad", n)==0 ){ |
| 238 | int ridBad; |
| 239 | foundCmd = 1; |
| 240 | if( g.argc==3 ){ |
| 241 | ridBad = db_lget_int("checkout",0); |
| 242 | }else{ |
| 243 | ridBad = name_to_typed_rid(g.argv[3], "ci"); |
| 244 | } |
| 245 | if( ridBad>0 ){ |
| 246 | bisect_append_log(-ridBad); |
| 247 | if( bisect_option("auto-next") && db_lget_int("bisect-good",0)>0 ){ |
| 248 | zCmd = "next"; |
| 249 | n = 4; |
| 250 | } |
| 251 | } |
| 252 | }else if( memcmp(zCmd, "good", n)==0 ){ |
| 253 | int ridGood; |
| 254 | foundCmd = 1; |
| 255 | if( g.argc==3 ){ |
| 256 | ridGood = db_lget_int("checkout",0); |
| 257 | }else{ |
| 258 | ridGood = name_to_typed_rid(g.argv[3], "ci"); |
| 259 | } |
| 260 | if( ridGood>0 ){ |
| 261 | bisect_append_log(ridGood); |
| 262 | if( bisect_option("auto-next") && db_lget_int("bisect-bad",0)>0 ){ |
| 263 | zCmd = "next"; |
| 264 | n = 4; |
| 265 | } |
| 266 | } |
| 267 | }else if( memcmp(zCmd, "undo", n)==0 ){ |
| 268 | char *zLog; |
| 269 | Blob log, id; |
| 270 | int ridBad = 0; |
| 271 | int ridGood = 0; |
| 272 | foundCmd = 1; |
| 273 | int cnt = 0, i; |
| 274 | db_begin_transaction(); |
| 275 | zLog = db_lget("bisect-log",""); |
| 276 | blob_init(&log, zLog, -1); |
| 277 | while( blob_token(&log, &id) ){ cnt++; } |
| 278 | if( cnt==0 ){ |
| 279 | fossil_fatal("no previous bisect steps to undo"); |
| 280 | } |
| 281 | blob_rewind(&log); |
| 282 | for(i=0; i<cnt-1; i++){ |
| 283 | int rid; |
| 284 | blob_token(&log, &id); |
| 285 | rid = atoi(blob_str(&id)); |
| 286 | if( rid<0 ) ridBad = -rid; |
| 287 | else ridGood = rid; |
| 288 | } |
| 289 | db_multi_exec( |
| 290 | "UPDATE vvar SET value=substr(value,1,%d) WHERE name='bisect-log'", |
| 291 | log.iCursor-1 |
| 292 | ); |
| 293 | db_lset_int("bisect-bad", ridBad); |
| 294 | db_lset_int("bisect-good", ridGood); |
| 295 | db_end_transaction(0); |
| 296 | if( ridBad && ridGood ){ |
| 297 | zCmd = "next"; |
| 298 | n = 4; |
| 299 | } |
| 300 | } |
| 301 | /* No else here so that the above commands can morph themselves into |
| 302 | ** a "next" command */ |
| 303 | if( memcmp(zCmd, "next", n)==0 ){ |
| 304 | PathNode *pMid; |
| 305 | bisect_path(); |
| 306 | pMid = path_midpoint(); |
| 307 | if( pMid==0 ){ |
| 308 | fossil_print("bisect complete\n"); |
| 309 | }else{ |
| 310 | g.argv[1] = "update"; |
| 311 | g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); |
| 312 | g.argc = 3; |
| 313 | g.fNoSync = 1; |
| 314 | update_cmd(); |
| 315 | } |
| 316 | bisect_list(1); |
| 317 | }else if( memcmp(zCmd, "log", n)==0 ){ |
| 318 | char *zLog = db_lget("bisect-log",""); |
| 319 | Blob log, id; |
| 320 | Stmt q; |
| 321 | int cnt = 0; |
| 322 | blob_init(&log, zLog, -1); |
| 323 | db_prepare(&q, "SELECT substr(blob.uuid,1,16), datetime(event.mtime)" |
| 324 | " FROM blob, event" |
| 325 | " WHERE blob.rid=:rid AND event.objid=:rid" |
| 326 | " AND event.type='ci'"); |
| 327 | while( blob_token(&log, &id) ){ |
| 328 | int rid = atoi(blob_str(&id)); |
| 329 | db_bind_int(&q, ":rid", rid<0 ? -rid : rid); |
| 330 | if( db_step(&q)==SQLITE_ROW ){ |
| 331 | cnt++; |
| 332 | fossil_print("%3d %-4s %s %s\n", cnt, rid<0 ? "BAD" : "GOOD", |
| 333 | db_column_text(&q, 1), db_column_text(&q, 0)); |
| 334 | } |
| 335 | db_reset(&q); |
| 336 | } |
| 337 | db_finalize(&q); |
| 338 | }else if( memcmp(zCmd, "options", n)==0 ){ |
| 339 | if( g.argc==3 ){ |
| 340 | unsigned int i; |
| 341 | for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){ |
| 342 | char *z = mprintf("bisect-%s", aBisectOption[i].zName); |
| @@ -268,14 +365,18 @@ | |
| 365 | }else{ |
| 366 | usage("bisect option ?NAME? ?VALUE?"); |
| 367 | } |
| 368 | }else if( memcmp(zCmd, "reset", n)==0 ){ |
| 369 | db_multi_exec( |
| 370 | "DELETE FROM vvar WHERE name IN " |
| 371 | " ('bisect-good', 'bisect-bad', 'bisect-log')" |
| 372 | ); |
| 373 | }else if( memcmp(zCmd, "vlist", n)==0 |
| 374 | || memcmp(zCmd, "ls", n)==0 |
| 375 | || memcmp(zCmd, "status", n)==0 |
| 376 | ){ |
| 377 | int fAll = find_option("all", 0, 0)!=0; |
| 378 | bisect_list(!fAll); |
| 379 | }else if( !foundCmd ){ |
| 380 | usage("bad|good|log|next|options|reset|status|undo"); |
| 381 | } |
| 382 | } |
| 383 |