| | @@ -116,11 +116,16 @@ |
| 116 | 116 | ** Elements of the path can be traversed by following the PathNode.u.pTo |
| 117 | 117 | ** pointer chain. |
| 118 | 118 | ** |
| 119 | 119 | ** Return NULL if no path is found. |
| 120 | 120 | */ |
| 121 | | -PathNode *path_shortest(int iFrom, int iTo, int directOnly){ |
| 121 | +PathNode *path_shortest( |
| 122 | + int iFrom, /* Path starts here */ |
| 123 | + int iTo, /* Path ends here */ |
| 124 | + int directOnly, /* No merge links if true */ |
| 125 | + int oneWayOnly /* Parent->child only if true */ |
| 126 | +){ |
| 122 | 127 | Stmt s; |
| 123 | 128 | PathNode *pPrev; |
| 124 | 129 | PathNode *p; |
| 125 | 130 | |
| 126 | 131 | path_reset(); |
| | @@ -127,11 +132,15 @@ |
| 127 | 132 | path.pStart = path_new_node(iFrom, 0, 0); |
| 128 | 133 | if( iTo==iFrom ){ |
| 129 | 134 | path.pEnd = path.pStart; |
| 130 | 135 | return path.pStart; |
| 131 | 136 | } |
| 132 | | - if( directOnly ){ |
| 137 | + if( oneWayOnly ){ |
| 138 | + db_prepare(&s, |
| 139 | + "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 140 | + ); |
| 141 | + }else if( directOnly ){ |
| 133 | 142 | db_prepare(&s, |
| 134 | 143 | "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " |
| 135 | 144 | "UNION ALL " |
| 136 | 145 | "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim" |
| 137 | 146 | ); |
| | @@ -193,17 +202,19 @@ |
| 193 | 202 | int iFrom; |
| 194 | 203 | int iTo; |
| 195 | 204 | PathNode *p; |
| 196 | 205 | int n; |
| 197 | 206 | int directOnly; |
| 207 | + int oneWay; |
| 198 | 208 | |
| 199 | 209 | db_find_and_open_repository(0,0); |
| 200 | 210 | directOnly = find_option("no-merge",0,0)!=0; |
| 211 | + oneWay = find_option("one-way",0,0)!=0; |
| 201 | 212 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 202 | 213 | iFrom = name_to_rid(g.argv[2]); |
| 203 | 214 | iTo = name_to_rid(g.argv[3]); |
| 204 | | - p = path_shortest(iFrom, iTo, directOnly); |
| 215 | + p = path_shortest(iFrom, iTo, directOnly, oneWay); |
| 205 | 216 | if( p==0 ){ |
| 206 | 217 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 207 | 218 | } |
| 208 | 219 | for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ |
| 209 | 220 | char *z; |
| | @@ -350,10 +361,11 @@ |
| 350 | 361 | ** infrastructure. |
| 351 | 362 | */ |
| 352 | 363 | void find_filename_changes( |
| 353 | 364 | int iFrom, /* Ancestor check-in */ |
| 354 | 365 | int iTo, /* Recent check-in */ |
| 366 | + int revOk, /* Ok to move backwards (child->parent) if true */ |
| 355 | 367 | int *pnChng, /* Number of name changes along the path */ |
| 356 | 368 | int **aiChng, /* Name changes */ |
| 357 | 369 | const char *zDebug /* Generate trace output if no NULL */ |
| 358 | 370 | ){ |
| 359 | 371 | PathNode *p; /* For looping over path from iFrom to iTo */ |
| | @@ -366,34 +378,40 @@ |
| 366 | 378 | |
| 367 | 379 | *pnChng = 0; |
| 368 | 380 | *aiChng = 0; |
| 369 | 381 | if( iFrom==iTo ) return; |
| 370 | 382 | path_reset(); |
| 371 | | - p = path_shortest(iFrom, iTo, 1); |
| 383 | + p = path_shortest(iFrom, iTo, 1, revOk==0); |
| 372 | 384 | if( p==0 ) return; |
| 373 | 385 | path_reverse_path(); |
| 374 | 386 | db_prepare(&q1, |
| 375 | | - "SELECT pfnid, fnid FROM mlink WHERE mid=:mid AND pfnid>0" |
| 387 | + "SELECT pfnid, fnid FROM mlink" |
| 388 | + " WHERE mid=:mid AND (pfnid>0 OR fid==0)" |
| 389 | + " ORDER BY pfnid" |
| 376 | 390 | ); |
| 377 | 391 | for(p=path.pStart; p; p=p->u.pTo){ |
| 378 | 392 | int fnid, pfnid; |
| 379 | 393 | if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){ |
| 380 | 394 | /* Skip nodes where the parent is not on the path */ |
| 381 | 395 | continue; |
| 382 | 396 | } |
| 383 | 397 | db_bind_int(&q1, ":mid", p->rid); |
| 384 | 398 | while( db_step(&q1)==SQLITE_ROW ){ |
| 385 | | - if( p->fromIsParent ){ |
| 386 | | - fnid = db_column_int(&q1, 1); |
| 387 | | - pfnid = db_column_int(&q1, 0); |
| 388 | | - }else{ |
| 389 | | - fnid = db_column_int(&q1, 0); |
| 390 | | - pfnid = db_column_int(&q1, 1); |
| 399 | + fnid = db_column_int(&q1, 1); |
| 400 | + pfnid = db_column_int(&q1, 0); |
| 401 | + if( pfnid==0 ){ |
| 402 | + pfnid = fnid; |
| 403 | + fnid = 0; |
| 404 | + } |
| 405 | + if( !p->fromIsParent ){ |
| 406 | + int t = fnid; |
| 407 | + fnid = pfnid; |
| 408 | + pfnid = t; |
| 391 | 409 | } |
| 392 | 410 | if( zDebug ){ |
| 393 | | - fossil_print("%s at %d %.10z: %d[%z] -> %d[%z]\n", |
| 394 | | - zDebug, p->rid, |
| 411 | + fossil_print("%s at %d%s %.10z: %d[%z] -> %d[%z]\n", |
| 412 | + zDebug, p->rid, p->fromIsParent ? ">" : "<", |
| 395 | 413 | db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid), |
| 396 | 414 | pfnid, |
| 397 | 415 | db_text(0, "SELECT name FROM filename WHERE fnid=%d", pfnid), |
| 398 | 416 | fnid, |
| 399 | 417 | db_text(0, "SELECT name FROM filename WHERE fnid=%d", fnid)); |
| | @@ -402,28 +420,32 @@ |
| 402 | 420 | if( pChng->curName==pfnid ){ |
| 403 | 421 | pChng->newName = fnid; |
| 404 | 422 | break; |
| 405 | 423 | } |
| 406 | 424 | } |
| 407 | | - if( pChng==0 ){ |
| 425 | + if( pChng==0 && fnid>0 ){ |
| 408 | 426 | pChng = fossil_malloc( sizeof(*pChng) ); |
| 409 | 427 | pChng->pNext = pAll; |
| 410 | 428 | pAll = pChng; |
| 411 | 429 | pChng->origName = pfnid; |
| 412 | 430 | pChng->curName = pfnid; |
| 413 | 431 | pChng->newName = fnid; |
| 414 | 432 | nChng++; |
| 415 | 433 | } |
| 416 | 434 | } |
| 417 | | - for(pChng=pAll; pChng; pChng=pChng->pNext) pChng->curName = pChng->newName; |
| 435 | + for(pChng=pAll; pChng; pChng=pChng->pNext){ |
| 436 | + pChng->curName = pChng->newName; |
| 437 | + } |
| 418 | 438 | db_reset(&q1); |
| 419 | 439 | } |
| 420 | 440 | db_finalize(&q1); |
| 421 | 441 | if( nChng ){ |
| 422 | | - *pnChng = nChng; |
| 423 | 442 | aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) ); |
| 424 | | - for(pChng=pAll, i=0; pChng; pChng=pChng->pNext, i+=2){ |
| 443 | + for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){ |
| 444 | + if( pChng->newName==0 ) continue; |
| 445 | + if( pChng->origName==0 ) continue; |
| 446 | + if( pChng->newName==pChng->origName ) continue; |
| 425 | 447 | aChng[i] = pChng->origName; |
| 426 | 448 | aChng[i+1] = pChng->newName; |
| 427 | 449 | if( zDebug ){ |
| 428 | 450 | fossil_print("%s summary %d[%z] -> %d[%z]\n", |
| 429 | 451 | zDebug, |
| | @@ -430,11 +452,13 @@ |
| 430 | 452 | aChng[i], |
| 431 | 453 | db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i]), |
| 432 | 454 | aChng[i+1], |
| 433 | 455 | db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i+1])); |
| 434 | 456 | } |
| 457 | + i += 2; |
| 435 | 458 | } |
| 459 | + *pnChng = i/2; |
| 436 | 460 | while( pAll ){ |
| 437 | 461 | pChng = pAll; |
| 438 | 462 | pAll = pAll->pNext; |
| 439 | 463 | fossil_free(pChng); |
| 440 | 464 | } |
| | @@ -442,27 +466,31 @@ |
| 442 | 466 | } |
| 443 | 467 | |
| 444 | 468 | /* |
| 445 | 469 | ** COMMAND: test-name-changes |
| 446 | 470 | ** |
| 447 | | -** Usage: %fossil test-name-changes VERSION1 VERSION2 |
| 471 | +** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2 |
| 448 | 472 | ** |
| 449 | 473 | ** Show all filename changes that occur going from VERSION1 to VERSION2 |
| 450 | 474 | */ |
| 451 | 475 | void test_name_change(void){ |
| 452 | 476 | int iFrom; |
| 453 | 477 | int iTo; |
| 454 | 478 | int *aChng; |
| 455 | 479 | int nChng; |
| 456 | 480 | int i; |
| 481 | + const char *zDebug = 0; |
| 482 | + int revOk = 0; |
| 457 | 483 | |
| 458 | 484 | db_find_and_open_repository(0,0); |
| 485 | + zDebug = find_option("debug",0,0)!=0 ? "debug" : 0; |
| 486 | + revOk = find_option("bidirectional",0,0)!=0; |
| 459 | 487 | if( g.argc<4 ) usage("VERSION1 VERSION2"); |
| 460 | 488 | while( g.argc>=4 ){ |
| 461 | 489 | iFrom = name_to_rid(g.argv[2]); |
| 462 | 490 | iTo = name_to_rid(g.argv[3]); |
| 463 | | - find_filename_changes(iFrom, iTo, &nChng, &aChng, 0); |
| 491 | + find_filename_changes(iFrom, iTo, revOk, &nChng, &aChng, zDebug); |
| 464 | 492 | fossil_print("------ Changes for (%d) %s -> (%d) %s\n", |
| 465 | 493 | iFrom, g.argv[2], iTo, g.argv[3]); |
| 466 | 494 | for(i=0; i<nChng; i++){ |
| 467 | 495 | char *zFrom, *zTo; |
| 468 | 496 | |
| 469 | 497 | |