Fossil SCM
Add logic for computing the closure of file name changes across a sequence of checkins.
Commit
28aa0e367065f9966071df3cb5ac20ce07499db1
Parent
c49ee72b09f0b56…
1 file changed
+154
-7
+154
-7
| --- src/bisect.c | ||
| +++ src/bisect.c | ||
| @@ -13,10 +13,13 @@ | ||
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** |
| 15 | 15 | ******************************************************************************* |
| 16 | 16 | ** |
| 17 | 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. | |
| 18 | 21 | */ |
| 19 | 22 | #include "config.h" |
| 20 | 23 | #include "bisect.h" |
| 21 | 24 | #include <assert.h> |
| 22 | 25 | |
| @@ -84,21 +87,35 @@ | ||
| 84 | 87 | bisect.nStep = 0; |
| 85 | 88 | } |
| 86 | 89 | |
| 87 | 90 | /* |
| 88 | 91 | ** Compute the shortest path from iFrom to iTo |
| 92 | +** | |
| 93 | +** If directOnly is true, then use only the "primary" links from parent to | |
| 94 | +** child. In other words, ignore merges. | |
| 89 | 95 | */ |
| 90 | -static BisectNode *bisect_shortest_path(int iFrom, int iTo){ | |
| 96 | +static BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){ | |
| 91 | 97 | Stmt s; |
| 92 | 98 | BisectNode *pPrev; |
| 93 | 99 | BisectNode *p; |
| 94 | 100 | |
| 95 | 101 | bisect_reset(); |
| 96 | 102 | bisect.pStart = bisect_new_node(iFrom, 0, 0); |
| 97 | 103 | if( iTo==iFrom ) return bisect.pStart; |
| 98 | - db_prepare(&s, "SELECT cid, 1 FROM plink WHERE pid=:pid " | |
| 99 | - "UNION ALL SELECT pid, 0 FROM plink WHERE cid=:pid"); | |
| 104 | + if( directOnly ){ | |
| 105 | + db_prepare(&s, | |
| 106 | + "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " | |
| 107 | + "UNION ALL " | |
| 108 | + "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim" | |
| 109 | + ); | |
| 110 | + }else{ | |
| 111 | + db_prepare(&s, | |
| 112 | + "SELECT cid, 1 FROM plink WHERE pid=:pid " | |
| 113 | + "UNION ALL " | |
| 114 | + "SELECT pid, 0 FROM plink WHERE cid=:pid" | |
| 115 | + ); | |
| 116 | + } | |
| 100 | 117 | while( bisect.pCurrent ){ |
| 101 | 118 | bisect.nStep++; |
| 102 | 119 | pPrev = bisect.pCurrent; |
| 103 | 120 | bisect.pCurrent = 0; |
| 104 | 121 | while( pPrev ){ |
| @@ -132,24 +149,30 @@ | ||
| 132 | 149 | bisect.pEnd->u.pTo = 0; |
| 133 | 150 | assert( p==bisect.pStart ); |
| 134 | 151 | } |
| 135 | 152 | |
| 136 | 153 | /* |
| 137 | -** COMMAND: test-shortest-path VERSION1 VERSION2 | |
| 154 | +** COMMAND: test-shortest-path | |
| 155 | +** | |
| 156 | +** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2 | |
| 138 | 157 | ** |
| 139 | -** Report the shortest path between two checkins. | |
| 158 | +** Report the shortest path between two checkins. If the --no-merge flag | |
| 159 | +** is used, follow only direct parent-child paths and omit merge links. | |
| 140 | 160 | */ |
| 141 | 161 | void shortest_path_test_cmd(void){ |
| 142 | 162 | int iFrom; |
| 143 | 163 | int iTo; |
| 144 | 164 | BisectNode *p; |
| 145 | 165 | int n; |
| 166 | + int directOnly; | |
| 167 | + | |
| 146 | 168 | db_find_and_open_repository(0,0); |
| 169 | + directOnly = find_option("no-merge",0,0)!=0; | |
| 147 | 170 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 148 | 171 | iFrom = name_to_rid(g.argv[2]); |
| 149 | 172 | iTo = name_to_rid(g.argv[3]); |
| 150 | - p = bisect_shortest_path(iFrom, iTo); | |
| 173 | + p = bisect_shortest_path(iFrom, iTo, directOnly); | |
| 151 | 174 | if( p==0 ){ |
| 152 | 175 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 153 | 176 | } |
| 154 | 177 | bisect_reverse_path(); |
| 155 | 178 | for(n=1, p=bisect.pStart; p; p=p->u.pTo, n++){ |
| @@ -182,11 +205,11 @@ | ||
| 182 | 205 | bisect.good = db_lget_int("bisect-good", 0); |
| 183 | 206 | if( bisect.good==0 ){ |
| 184 | 207 | bisect.good = db_int(0,"SELECT pid FROM plink ORDER BY mtime LIMIT 1"); |
| 185 | 208 | db_lset_int("bisect-good", bisect.good); |
| 186 | 209 | } |
| 187 | - p = bisect_shortest_path(bisect.good, bisect.bad); | |
| 210 | + p = bisect_shortest_path(bisect.good, bisect.bad, 0); | |
| 188 | 211 | if( p==0 ){ |
| 189 | 212 | char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad); |
| 190 | 213 | char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good); |
| 191 | 214 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| 192 | 215 | zGood, zBad); |
| @@ -297,5 +320,129 @@ | ||
| 297 | 320 | db_finalize(&s); |
| 298 | 321 | }else{ |
| 299 | 322 | usage("bad|good|next|reset|vlist ..."); |
| 300 | 323 | } |
| 301 | 324 | } |
| 325 | + | |
| 326 | +/* | |
| 327 | +** A record of a file rename operation. | |
| 328 | +*/ | |
| 329 | +typedef struct NameChange NameChange; | |
| 330 | +struct NameChange { | |
| 331 | + int origName; /* Original name of file */ | |
| 332 | + int curName; /* Current name of the file */ | |
| 333 | + int newName; /* Name of file in next version */ | |
| 334 | + NameChange *pNext; /* List of all name changes */ | |
| 335 | +}; | |
| 336 | + | |
| 337 | +/* | |
| 338 | +** Compute all file name changes that occur going from checkin iFrom | |
| 339 | +** to checkin iTo. | |
| 340 | +** | |
| 341 | +** The number of name changes is written into *pnChng. For each name | |
| 342 | +** change, to integers are allocated for *piChng. The first is the original | |
| 343 | +** name and the second is the new name. Space to hold *piChng is obtained | |
| 344 | +** from fossil_malloc() and should be released by the caller. | |
| 345 | +** | |
| 346 | +** This routine really has nothing to do with bisection. It is located | |
| 347 | +** in this bisect.c module in order to leverage some of the bisect | |
| 348 | +** infrastructure. | |
| 349 | +*/ | |
| 350 | +void find_filename_changes( | |
| 351 | + int iFrom, | |
| 352 | + int iTo, | |
| 353 | + int *pnChng, | |
| 354 | + int **aiChng | |
| 355 | +){ | |
| 356 | + BisectNode *p; /* For looping over path from iFrom to iTo */ | |
| 357 | + NameChange *pAll = 0; /* List of all name changes seen so far */ | |
| 358 | + NameChange *pChng; /* For looping through the name change list */ | |
| 359 | + int nChng = 0; /* Number of files whose names have changed */ | |
| 360 | + int *aChng; /* Two integers per name change */ | |
| 361 | + int i; /* Loop counter */ | |
| 362 | + Stmt q1; /* Query of name changes */ | |
| 363 | + | |
| 364 | + *pnChng = 0; | |
| 365 | + *aiChng = 0; | |
| 366 | + bisect_reset(); | |
| 367 | + p = bisect_shortest_path(iFrom, iTo, 1); | |
| 368 | + if( p==0 ) return; | |
| 369 | + bisect_reverse_path(); | |
| 370 | + db_prepare(&q1, | |
| 371 | + "SELECT pfnid, fnid FROM mlink WHERE mid=:mid AND pfnid>0" | |
| 372 | + ); | |
| 373 | + for(p=bisect.pStart->u.pTo; p; p=p->u.pTo){ | |
| 374 | + int fnid, pfnid; | |
| 375 | + db_bind_int(&q1, ":mid", p->rid); | |
| 376 | + while( db_step(&q1)==SQLITE_ROW ){ | |
| 377 | + if( p->fromIsParent ){ | |
| 378 | + fnid = db_column_int(&q1, 1); | |
| 379 | + pfnid = db_column_int(&q1, 0); | |
| 380 | + }else{ | |
| 381 | + fnid = db_column_int(&q1, 0); | |
| 382 | + pfnid = db_column_int(&q1, 1); | |
| 383 | + } | |
| 384 | + for(pChng=pAll; pChng; pChng=pChng->pNext){ | |
| 385 | + if( pChng->curName==pfnid ){ | |
| 386 | + pChng->newName = fnid; | |
| 387 | + break; | |
| 388 | + } | |
| 389 | + } | |
| 390 | + if( pChng==0 ){ | |
| 391 | + pChng = fossil_malloc( sizeof(*pChng) ); | |
| 392 | + pChng->pNext = pAll; | |
| 393 | + pAll = pChng; | |
| 394 | + pChng->origName = pfnid; | |
| 395 | + pChng->curName = pfnid; | |
| 396 | + pChng->newName = fnid; | |
| 397 | + nChng++; | |
| 398 | + } | |
| 399 | + } | |
| 400 | + for(pChng=pAll; pChng; pChng=pChng->pNext) pChng->curName = pChng->newName; | |
| 401 | + db_reset(&q1); | |
| 402 | + } | |
| 403 | + db_finalize(&q1); | |
| 404 | + if( nChng ){ | |
| 405 | + *pnChng = nChng; | |
| 406 | + aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) ); | |
| 407 | + for(pChng=pAll, i=0; pChng; pChng=pChng->pNext, i+=2){ | |
| 408 | + aChng[i] = pChng->origName; | |
| 409 | + aChng[i+1] = pChng->newName; | |
| 410 | + } | |
| 411 | + while( pAll ){ | |
| 412 | + pChng = pAll; | |
| 413 | + pAll = pAll->pNext; | |
| 414 | + fossil_free(pChng); | |
| 415 | + } | |
| 416 | + } | |
| 417 | +} | |
| 418 | + | |
| 419 | +/* | |
| 420 | +** COMMAND: test-name-changes | |
| 421 | +** | |
| 422 | +** Usage: %fossil test-name-changes VERSION1 VERSION2 | |
| 423 | +** | |
| 424 | +** Show all filename changes that occur going from VERSION1 to VERSION2 | |
| 425 | +*/ | |
| 426 | +void test_name_change(void){ | |
| 427 | + int iFrom; | |
| 428 | + int iTo; | |
| 429 | + int *aChng; | |
| 430 | + int nChng; | |
| 431 | + int i; | |
| 432 | + | |
| 433 | + db_find_and_open_repository(0,0); | |
| 434 | + if( g.argc!=4 ) usage("VERSION1 VERSION2"); | |
| 435 | + iFrom = name_to_rid(g.argv[2]); | |
| 436 | + iTo = name_to_rid(g.argv[3]); | |
| 437 | + find_filename_changes(iFrom, iTo, &nChng, &aChng); | |
| 438 | + for(i=0; i<nChng; i++){ | |
| 439 | + char *zFrom, *zTo; | |
| 440 | + | |
| 441 | + zFrom = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]); | |
| 442 | + zTo = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]); | |
| 443 | + printf("[%s] -> [%s]\n", zFrom, zTo); | |
| 444 | + fossil_free(zFrom); | |
| 445 | + fossil_free(zTo); | |
| 446 | + } | |
| 447 | + fossil_free(aChng); | |
| 448 | +} | |
| 302 | 449 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -13,10 +13,13 @@ | |
| 13 | ** [email protected] |
| 14 | ** |
| 15 | ******************************************************************************* |
| 16 | ** |
| 17 | ** This file contains code used to implement the "bisect" command. |
| 18 | */ |
| 19 | #include "config.h" |
| 20 | #include "bisect.h" |
| 21 | #include <assert.h> |
| 22 | |
| @@ -84,21 +87,35 @@ | |
| 84 | bisect.nStep = 0; |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | ** Compute the shortest path from iFrom to iTo |
| 89 | */ |
| 90 | static BisectNode *bisect_shortest_path(int iFrom, int iTo){ |
| 91 | Stmt s; |
| 92 | BisectNode *pPrev; |
| 93 | BisectNode *p; |
| 94 | |
| 95 | bisect_reset(); |
| 96 | bisect.pStart = bisect_new_node(iFrom, 0, 0); |
| 97 | if( iTo==iFrom ) return bisect.pStart; |
| 98 | db_prepare(&s, "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 99 | "UNION ALL SELECT pid, 0 FROM plink WHERE cid=:pid"); |
| 100 | while( bisect.pCurrent ){ |
| 101 | bisect.nStep++; |
| 102 | pPrev = bisect.pCurrent; |
| 103 | bisect.pCurrent = 0; |
| 104 | while( pPrev ){ |
| @@ -132,24 +149,30 @@ | |
| 132 | bisect.pEnd->u.pTo = 0; |
| 133 | assert( p==bisect.pStart ); |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | ** COMMAND: test-shortest-path VERSION1 VERSION2 |
| 138 | ** |
| 139 | ** Report the shortest path between two checkins. |
| 140 | */ |
| 141 | void shortest_path_test_cmd(void){ |
| 142 | int iFrom; |
| 143 | int iTo; |
| 144 | BisectNode *p; |
| 145 | int n; |
| 146 | db_find_and_open_repository(0,0); |
| 147 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 148 | iFrom = name_to_rid(g.argv[2]); |
| 149 | iTo = name_to_rid(g.argv[3]); |
| 150 | p = bisect_shortest_path(iFrom, iTo); |
| 151 | if( p==0 ){ |
| 152 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 153 | } |
| 154 | bisect_reverse_path(); |
| 155 | for(n=1, p=bisect.pStart; p; p=p->u.pTo, n++){ |
| @@ -182,11 +205,11 @@ | |
| 182 | bisect.good = db_lget_int("bisect-good", 0); |
| 183 | if( bisect.good==0 ){ |
| 184 | bisect.good = db_int(0,"SELECT pid FROM plink ORDER BY mtime LIMIT 1"); |
| 185 | db_lset_int("bisect-good", bisect.good); |
| 186 | } |
| 187 | p = bisect_shortest_path(bisect.good, bisect.bad); |
| 188 | if( p==0 ){ |
| 189 | char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad); |
| 190 | char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good); |
| 191 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| 192 | zGood, zBad); |
| @@ -297,5 +320,129 @@ | |
| 297 | db_finalize(&s); |
| 298 | }else{ |
| 299 | usage("bad|good|next|reset|vlist ..."); |
| 300 | } |
| 301 | } |
| 302 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -13,10 +13,13 @@ | |
| 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 | |
| @@ -84,21 +87,35 @@ | |
| 87 | bisect.nStep = 0; |
| 88 | } |
| 89 | |
| 90 | /* |
| 91 | ** Compute the shortest path from iFrom to iTo |
| 92 | ** |
| 93 | ** If directOnly is true, then use only the "primary" links from parent to |
| 94 | ** child. In other words, ignore merges. |
| 95 | */ |
| 96 | static BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){ |
| 97 | Stmt s; |
| 98 | BisectNode *pPrev; |
| 99 | BisectNode *p; |
| 100 | |
| 101 | bisect_reset(); |
| 102 | bisect.pStart = bisect_new_node(iFrom, 0, 0); |
| 103 | if( iTo==iFrom ) return bisect.pStart; |
| 104 | if( directOnly ){ |
| 105 | db_prepare(&s, |
| 106 | "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " |
| 107 | "UNION ALL " |
| 108 | "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim" |
| 109 | ); |
| 110 | }else{ |
| 111 | db_prepare(&s, |
| 112 | "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 113 | "UNION ALL " |
| 114 | "SELECT pid, 0 FROM plink WHERE cid=:pid" |
| 115 | ); |
| 116 | } |
| 117 | while( bisect.pCurrent ){ |
| 118 | bisect.nStep++; |
| 119 | pPrev = bisect.pCurrent; |
| 120 | bisect.pCurrent = 0; |
| 121 | while( pPrev ){ |
| @@ -132,24 +149,30 @@ | |
| 149 | bisect.pEnd->u.pTo = 0; |
| 150 | assert( p==bisect.pStart ); |
| 151 | } |
| 152 | |
| 153 | /* |
| 154 | ** COMMAND: test-shortest-path |
| 155 | ** |
| 156 | ** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2 |
| 157 | ** |
| 158 | ** Report the shortest path between two checkins. If the --no-merge flag |
| 159 | ** is used, follow only direct parent-child paths and omit merge links. |
| 160 | */ |
| 161 | void shortest_path_test_cmd(void){ |
| 162 | int iFrom; |
| 163 | int iTo; |
| 164 | BisectNode *p; |
| 165 | int n; |
| 166 | int directOnly; |
| 167 | |
| 168 | db_find_and_open_repository(0,0); |
| 169 | directOnly = find_option("no-merge",0,0)!=0; |
| 170 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 171 | iFrom = name_to_rid(g.argv[2]); |
| 172 | iTo = name_to_rid(g.argv[3]); |
| 173 | p = bisect_shortest_path(iFrom, iTo, directOnly); |
| 174 | if( p==0 ){ |
| 175 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 176 | } |
| 177 | bisect_reverse_path(); |
| 178 | for(n=1, p=bisect.pStart; p; p=p->u.pTo, n++){ |
| @@ -182,11 +205,11 @@ | |
| 205 | bisect.good = db_lget_int("bisect-good", 0); |
| 206 | if( bisect.good==0 ){ |
| 207 | bisect.good = db_int(0,"SELECT pid FROM plink ORDER BY mtime LIMIT 1"); |
| 208 | db_lset_int("bisect-good", bisect.good); |
| 209 | } |
| 210 | p = bisect_shortest_path(bisect.good, bisect.bad, 0); |
| 211 | if( p==0 ){ |
| 212 | char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad); |
| 213 | char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good); |
| 214 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| 215 | zGood, zBad); |
| @@ -297,5 +320,129 @@ | |
| 320 | db_finalize(&s); |
| 321 | }else{ |
| 322 | usage("bad|good|next|reset|vlist ..."); |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | /* |
| 327 | ** A record of a file rename operation. |
| 328 | */ |
| 329 | typedef struct NameChange NameChange; |
| 330 | struct NameChange { |
| 331 | int origName; /* Original name of file */ |
| 332 | int curName; /* Current name of the file */ |
| 333 | int newName; /* Name of file in next version */ |
| 334 | NameChange *pNext; /* List of all name changes */ |
| 335 | }; |
| 336 | |
| 337 | /* |
| 338 | ** Compute all file name changes that occur going from checkin iFrom |
| 339 | ** to checkin iTo. |
| 340 | ** |
| 341 | ** The number of name changes is written into *pnChng. For each name |
| 342 | ** change, to integers are allocated for *piChng. The first is the original |
| 343 | ** name and the second is the new name. Space to hold *piChng is obtained |
| 344 | ** from fossil_malloc() and should be released by the caller. |
| 345 | ** |
| 346 | ** This routine really has nothing to do with bisection. It is located |
| 347 | ** in this bisect.c module in order to leverage some of the bisect |
| 348 | ** infrastructure. |
| 349 | */ |
| 350 | void find_filename_changes( |
| 351 | int iFrom, |
| 352 | int iTo, |
| 353 | int *pnChng, |
| 354 | int **aiChng |
| 355 | ){ |
| 356 | BisectNode *p; /* For looping over path from iFrom to iTo */ |
| 357 | NameChange *pAll = 0; /* List of all name changes seen so far */ |
| 358 | NameChange *pChng; /* For looping through the name change list */ |
| 359 | int nChng = 0; /* Number of files whose names have changed */ |
| 360 | int *aChng; /* Two integers per name change */ |
| 361 | int i; /* Loop counter */ |
| 362 | Stmt q1; /* Query of name changes */ |
| 363 | |
| 364 | *pnChng = 0; |
| 365 | *aiChng = 0; |
| 366 | bisect_reset(); |
| 367 | p = bisect_shortest_path(iFrom, iTo, 1); |
| 368 | if( p==0 ) return; |
| 369 | bisect_reverse_path(); |
| 370 | db_prepare(&q1, |
| 371 | "SELECT pfnid, fnid FROM mlink WHERE mid=:mid AND pfnid>0" |
| 372 | ); |
| 373 | for(p=bisect.pStart->u.pTo; p; p=p->u.pTo){ |
| 374 | int fnid, pfnid; |
| 375 | db_bind_int(&q1, ":mid", p->rid); |
| 376 | while( db_step(&q1)==SQLITE_ROW ){ |
| 377 | if( p->fromIsParent ){ |
| 378 | fnid = db_column_int(&q1, 1); |
| 379 | pfnid = db_column_int(&q1, 0); |
| 380 | }else{ |
| 381 | fnid = db_column_int(&q1, 0); |
| 382 | pfnid = db_column_int(&q1, 1); |
| 383 | } |
| 384 | for(pChng=pAll; pChng; pChng=pChng->pNext){ |
| 385 | if( pChng->curName==pfnid ){ |
| 386 | pChng->newName = fnid; |
| 387 | break; |
| 388 | } |
| 389 | } |
| 390 | if( pChng==0 ){ |
| 391 | pChng = fossil_malloc( sizeof(*pChng) ); |
| 392 | pChng->pNext = pAll; |
| 393 | pAll = pChng; |
| 394 | pChng->origName = pfnid; |
| 395 | pChng->curName = pfnid; |
| 396 | pChng->newName = fnid; |
| 397 | nChng++; |
| 398 | } |
| 399 | } |
| 400 | for(pChng=pAll; pChng; pChng=pChng->pNext) pChng->curName = pChng->newName; |
| 401 | db_reset(&q1); |
| 402 | } |
| 403 | db_finalize(&q1); |
| 404 | if( nChng ){ |
| 405 | *pnChng = nChng; |
| 406 | aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) ); |
| 407 | for(pChng=pAll, i=0; pChng; pChng=pChng->pNext, i+=2){ |
| 408 | aChng[i] = pChng->origName; |
| 409 | aChng[i+1] = pChng->newName; |
| 410 | } |
| 411 | while( pAll ){ |
| 412 | pChng = pAll; |
| 413 | pAll = pAll->pNext; |
| 414 | fossil_free(pChng); |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | /* |
| 420 | ** COMMAND: test-name-changes |
| 421 | ** |
| 422 | ** Usage: %fossil test-name-changes VERSION1 VERSION2 |
| 423 | ** |
| 424 | ** Show all filename changes that occur going from VERSION1 to VERSION2 |
| 425 | */ |
| 426 | void test_name_change(void){ |
| 427 | int iFrom; |
| 428 | int iTo; |
| 429 | int *aChng; |
| 430 | int nChng; |
| 431 | int i; |
| 432 | |
| 433 | db_find_and_open_repository(0,0); |
| 434 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 435 | iFrom = name_to_rid(g.argv[2]); |
| 436 | iTo = name_to_rid(g.argv[3]); |
| 437 | find_filename_changes(iFrom, iTo, &nChng, &aChng); |
| 438 | for(i=0; i<nChng; i++){ |
| 439 | char *zFrom, *zTo; |
| 440 | |
| 441 | zFrom = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]); |
| 442 | zTo = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]); |
| 443 | printf("[%s] -> [%s]\n", zFrom, zTo); |
| 444 | fossil_free(zFrom); |
| 445 | fossil_free(zTo); |
| 446 | } |
| 447 | fossil_free(aChng); |
| 448 | } |
| 449 |