Fossil SCM

Add logic for computing the closure of file name changes across a sequence of checkins.

drh 2010-12-13 15:45 trunk
Commit 28aa0e367065f9966071df3cb5ac20ce07499db1
1 file changed +154 -7
+154 -7
--- src/bisect.c
+++ src/bisect.c
@@ -13,10 +13,13 @@
1313
** [email protected]
1414
**
1515
*******************************************************************************
1616
**
1717
** 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.
1821
*/
1922
#include "config.h"
2023
#include "bisect.h"
2124
#include <assert.h>
2225
@@ -84,21 +87,35 @@
8487
bisect.nStep = 0;
8588
}
8689
8790
/*
8891
** 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.
8995
*/
90
-static BisectNode *bisect_shortest_path(int iFrom, int iTo){
96
+static BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){
9197
Stmt s;
9298
BisectNode *pPrev;
9399
BisectNode *p;
94100
95101
bisect_reset();
96102
bisect.pStart = bisect_new_node(iFrom, 0, 0);
97103
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
+ }
100117
while( bisect.pCurrent ){
101118
bisect.nStep++;
102119
pPrev = bisect.pCurrent;
103120
bisect.pCurrent = 0;
104121
while( pPrev ){
@@ -132,24 +149,30 @@
132149
bisect.pEnd->u.pTo = 0;
133150
assert( p==bisect.pStart );
134151
}
135152
136153
/*
137
-** COMMAND: test-shortest-path VERSION1 VERSION2
154
+** COMMAND: test-shortest-path
155
+**
156
+** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2
138157
**
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.
140160
*/
141161
void shortest_path_test_cmd(void){
142162
int iFrom;
143163
int iTo;
144164
BisectNode *p;
145165
int n;
166
+ int directOnly;
167
+
146168
db_find_and_open_repository(0,0);
169
+ directOnly = find_option("no-merge",0,0)!=0;
147170
if( g.argc!=4 ) usage("VERSION1 VERSION2");
148171
iFrom = name_to_rid(g.argv[2]);
149172
iTo = name_to_rid(g.argv[3]);
150
- p = bisect_shortest_path(iFrom, iTo);
173
+ p = bisect_shortest_path(iFrom, iTo, directOnly);
151174
if( p==0 ){
152175
fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
153176
}
154177
bisect_reverse_path();
155178
for(n=1, p=bisect.pStart; p; p=p->u.pTo, n++){
@@ -182,11 +205,11 @@
182205
bisect.good = db_lget_int("bisect-good", 0);
183206
if( bisect.good==0 ){
184207
bisect.good = db_int(0,"SELECT pid FROM plink ORDER BY mtime LIMIT 1");
185208
db_lset_int("bisect-good", bisect.good);
186209
}
187
- p = bisect_shortest_path(bisect.good, bisect.bad);
210
+ p = bisect_shortest_path(bisect.good, bisect.bad, 0);
188211
if( p==0 ){
189212
char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad);
190213
char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good);
191214
fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
192215
zGood, zBad);
@@ -297,5 +320,129 @@
297320
db_finalize(&s);
298321
}else{
299322
usage("bad|good|next|reset|vlist ...");
300323
}
301324
}
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
+}
302449
--- 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

Keyboard Shortcuts

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