| | @@ -18,40 +18,41 @@ |
| 18 | 18 | ** directed acyclic graph (DAG) of check-ins. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "path.h" |
| 22 | 22 | #include <assert.h> |
| 23 | +#include <math.h> |
| 23 | 24 | |
| 24 | 25 | #if INTERFACE |
| 25 | 26 | /* Nodes for the paths through the DAG. |
| 26 | 27 | */ |
| 27 | 28 | struct PathNode { |
| 28 | 29 | int rid; /* ID for this node */ |
| 29 | 30 | u8 fromIsParent; /* True if pFrom is the parent of rid */ |
| 30 | 31 | u8 isPrim; /* True if primary side of common ancestor */ |
| 31 | 32 | u8 isHidden; /* Abbreviate output in "fossil bisect ls" */ |
| 32 | | - u8 nDelay; /* Delay this many steps before walking */ |
| 33 | 33 | char *zBranch; /* Branch name for this node. Might be NULL */ |
| 34 | + double mtime; /* Date/time of this check-in */ |
| 34 | 35 | PathNode *pFrom; /* Node we came from */ |
| 35 | 36 | union { |
| 36 | | - PathNode *pPeer; /* List of nodes of the same generation */ |
| 37 | + double rCost; /* Cost of getting to this node from pStart */ |
| 37 | 38 | PathNode *pTo; /* Next on path from beginning to end */ |
| 38 | 39 | } u; |
| 39 | | - PathNode *pAll; /* List of all nodes */ |
| 40 | + PathNode *pAll; /* List of all nodes */ |
| 40 | 41 | }; |
| 41 | 42 | #endif |
| 42 | 43 | |
| 43 | 44 | /* |
| 44 | 45 | ** Local variables for this module |
| 45 | 46 | */ |
| 46 | 47 | static struct { |
| 47 | | - PathNode *pCurrent; /* Current generation of nodes */ |
| 48 | + PQueue pending; /* Nodes pending review for inclusion in the graph */ |
| 48 | 49 | PathNode *pAll; /* All nodes */ |
| 49 | | - Bag seen; /* Nodes seen before */ |
| 50 | 50 | int nStep; /* Number of steps from first to last */ |
| 51 | 51 | int nNotHidden; /* Number of steps not counting hidden nodes */ |
| 52 | | - u8 brCost; /* Extra cost for moving to a different branch */ |
| 52 | + int brCost; /* Extra cost for moving to a different branch */ |
| 53 | + int revCost; /* Extra cost for changing directions */ |
| 53 | 54 | PathNode *pStart; /* Earliest node */ |
| 54 | 55 | PathNode *pEnd; /* Most recent */ |
| 55 | 56 | } path; |
| 56 | 57 | |
| 57 | 58 | /* |
| | @@ -69,31 +70,40 @@ |
| 69 | 70 | ** Return the number of non-hidden steps in the computed path. |
| 70 | 71 | */ |
| 71 | 72 | int path_length_not_hidden(void){ return path.nNotHidden; } |
| 72 | 73 | |
| 73 | 74 | /* |
| 74 | | -** Create a new node |
| 75 | +** Create a new node and insert it into the path.pending queue. |
| 75 | 76 | */ |
| 76 | 77 | static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){ |
| 77 | 78 | PathNode *p; |
| 78 | 79 | |
| 79 | 80 | p = fossil_malloc( sizeof(*p) ); |
| 80 | 81 | memset(p, 0, sizeof(*p)); |
| 82 | + p->pAll = path.pAll; |
| 83 | + path.pAll = p; |
| 81 | 84 | p->rid = rid; |
| 82 | 85 | p->fromIsParent = isParent; |
| 83 | 86 | p->pFrom = pFrom; |
| 87 | + p->u.rCost = pFrom ? pFrom->u.rCost : 0.0; |
| 84 | 88 | if( path.brCost ){ |
| 85 | 89 | p->zBranch = branch_of_rid(rid); |
| 86 | | - if( pFrom && fossil_strcmp(p->zBranch, pFrom->zBranch)!=0 ){ |
| 87 | | - p->nDelay = path.brCost; |
| 88 | | - } |
| 89 | | - } |
| 90 | | - p->u.pPeer = path.pCurrent; |
| 91 | | - path.pCurrent = p; |
| 92 | | - p->pAll = path.pAll; |
| 93 | | - path.pAll = p; |
| 94 | | - bag_insert(&path.seen, rid); |
| 90 | + p->mtime = mtime_of_rid(rid, 0.0); |
| 91 | + if( pFrom ){ |
| 92 | + p->u.rCost += fabs(pFrom->mtime - p->mtime); |
| 93 | + if( fossil_strcmp(p->zBranch, pFrom->zBranch)!=0 ){ |
| 94 | + p->u.rCost += path.brCost; |
| 95 | + } |
| 96 | + } |
| 97 | + }else{ |
| 98 | + /* When brCost==0, we try to minimize the number of nodes |
| 99 | + ** along the path. The cost is just the number of nodes back |
| 100 | + ** to the start. We do not need to know the branch name nor |
| 101 | + ** the mtime */ |
| 102 | + p->u.rCost += 1.0; |
| 103 | + } |
| 104 | + pqueuex_insert_ptr(&path.pending, (void*)p, p->u.rCost); |
| 95 | 105 | return p; |
| 96 | 106 | } |
| 97 | 107 | |
| 98 | 108 | /* |
| 99 | 109 | ** Reset memory used by the shortest path algorithm. |
| | @@ -104,11 +114,11 @@ |
| 104 | 114 | p = path.pAll; |
| 105 | 115 | path.pAll = p->pAll; |
| 106 | 116 | fossil_free(p->zBranch); |
| 107 | 117 | fossil_free(p); |
| 108 | 118 | } |
| 109 | | - bag_clear(&path.seen); |
| 119 | + pqueuex_clear(&path.pending); |
| 110 | 120 | memset(&path, 0, sizeof(path)); |
| 111 | 121 | } |
| 112 | 122 | |
| 113 | 123 | /* |
| 114 | 124 | ** Construct the path from path.pStart to path.pEnd in the u.pTo fields. |
| | @@ -142,13 +152,12 @@ |
| 142 | 152 | int oneWayOnly, /* Parent->child only if true */ |
| 143 | 153 | Bag *pHidden, /* Hidden nodes */ |
| 144 | 154 | int branchCost /* Add extra codes to changing branches */ |
| 145 | 155 | ){ |
| 146 | 156 | Stmt s; |
| 147 | | - PathNode *pPrev; |
| 157 | + Bag seen; |
| 148 | 158 | PathNode *p; |
| 149 | | - int nPriorPeer = 1; |
| 150 | 159 | |
| 151 | 160 | path_reset(); |
| 152 | 161 | path.brCost = branchCost; |
| 153 | 162 | path.pStart = path_new_node(iFrom, 0, 0); |
| 154 | 163 | if( iTo==iFrom ){ |
| | @@ -174,45 +183,33 @@ |
| 174 | 183 | "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 175 | 184 | "UNION ALL " |
| 176 | 185 | "SELECT pid, 0 FROM plink WHERE cid=:pid" |
| 177 | 186 | ); |
| 178 | 187 | } |
| 179 | | - while( path.pCurrent ){ |
| 180 | | - if( nPriorPeer ) path.nStep++; |
| 181 | | - nPriorPeer = 0; |
| 182 | | - pPrev = path.pCurrent; |
| 183 | | - path.pCurrent = 0; |
| 184 | | - while( pPrev ){ |
| 185 | | - if( pPrev->nDelay>0 && (nPriorPeer>0 || pPrev->u.pPeer!=0) ){ |
| 186 | | - PathNode *pThis = pPrev; |
| 187 | | - pPrev = pThis->u.pPeer; |
| 188 | | - pThis->u.pPeer = path.pCurrent; |
| 189 | | - path.pCurrent = pThis; |
| 190 | | - pThis->nDelay--; |
| 191 | | - continue; |
| 192 | | - } |
| 193 | | - nPriorPeer++; |
| 194 | | - db_bind_int(&s, ":pid", pPrev->rid); |
| 195 | | - while( db_step(&s)==SQLITE_ROW ){ |
| 196 | | - int cid = db_column_int(&s, 0); |
| 197 | | - int isParent = db_column_int(&s, 1); |
| 198 | | - if( bag_find(&path.seen, cid) ) continue; |
| 199 | | - p = path_new_node(cid, pPrev, isParent); |
| 200 | | - if( pHidden && bag_find(pHidden,cid) ) p->isHidden = 1; |
| 201 | | - if( cid==iTo ){ |
| 202 | | - db_finalize(&s); |
| 203 | | - path.pEnd = p; |
| 204 | | - path_reverse_path(); |
| 205 | | - for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ |
| 206 | | - if( !p->isHidden ) path.nNotHidden++; |
| 207 | | - } |
| 208 | | - return path.pStart; |
| 209 | | - } |
| 210 | | - } |
| 211 | | - db_reset(&s); |
| 212 | | - pPrev = pPrev->u.pPeer; |
| 213 | | - } |
| 188 | + bag_init(&seen); |
| 189 | + while( (p = pqueuex_extract_ptr(&path.pending))!=0 ){ |
| 190 | + if( p->rid==iTo ){ |
| 191 | + db_finalize(&s); |
| 192 | + path.pEnd = p; |
| 193 | + path_reverse_path(); |
| 194 | + for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ |
| 195 | + if( !p->isHidden ) path.nNotHidden++; |
| 196 | + } |
| 197 | + return path.pStart; |
| 198 | + } |
| 199 | + if( bag_find(&seen, p->rid) ) continue; |
| 200 | + bag_insert(&seen, p->rid); |
| 201 | + db_bind_int(&s, ":pid", p->rid); |
| 202 | + while( db_step(&s)==SQLITE_ROW ){ |
| 203 | + int cid = db_column_int(&s, 0); |
| 204 | + int isParent = db_column_int(&s, 1); |
| 205 | + PathNode *pNew; |
| 206 | + if( bag_find(&seen, cid) ) continue; |
| 207 | + pNew = path_new_node(cid, p, isParent); |
| 208 | + if( pHidden && bag_find(pHidden,cid) ) pNew->isHidden = 1; |
| 209 | + } |
| 210 | + db_reset(&s); |
| 214 | 211 | } |
| 215 | 212 | db_finalize(&s); |
| 216 | 213 | path_reset(); |
| 217 | 214 | return 0; |
| 218 | 215 | } |
| | @@ -333,11 +330,11 @@ |
| 333 | 330 | ** Find the closest common ancestor of two nodes. "Closest" means the |
| 334 | 331 | ** fewest number of arcs. |
| 335 | 332 | */ |
| 336 | 333 | int path_common_ancestor(int iMe, int iYou){ |
| 337 | 334 | Stmt s; |
| 338 | | - PathNode *pPrev; |
| 335 | + PathNode *pThis; |
| 339 | 336 | PathNode *p; |
| 340 | 337 | Bag me, you; |
| 341 | 338 | |
| 342 | 339 | if( iMe==iYou ) return iMe; |
| 343 | 340 | if( iMe==0 || iYou==0 ) return 0; |
| | @@ -348,45 +345,40 @@ |
| 348 | 345 | db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid"); |
| 349 | 346 | bag_init(&me); |
| 350 | 347 | bag_insert(&me, iMe); |
| 351 | 348 | bag_init(&you); |
| 352 | 349 | bag_insert(&you, iYou); |
| 353 | | - while( path.pCurrent ){ |
| 354 | | - pPrev = path.pCurrent; |
| 355 | | - path.pCurrent = 0; |
| 356 | | - while( pPrev ){ |
| 357 | | - db_bind_int(&s, ":cid", pPrev->rid); |
| 358 | | - while( db_step(&s)==SQLITE_ROW ){ |
| 359 | | - int pid = db_column_int(&s, 0); |
| 360 | | - if( bag_find(pPrev->isPrim ? &you : &me, pid) ){ |
| 361 | | - /* pid is the common ancestor */ |
| 362 | | - PathNode *pNext; |
| 363 | | - for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} |
| 364 | | - assert( p!=0 ); |
| 365 | | - pNext = p; |
| 366 | | - while( pNext ){ |
| 367 | | - pNext = p->pFrom; |
| 368 | | - p->pFrom = pPrev; |
| 369 | | - pPrev = p; |
| 370 | | - p = pNext; |
| 371 | | - } |
| 372 | | - if( pPrev==path.pStart ) path.pStart = path.pEnd; |
| 373 | | - path.pEnd = pPrev; |
| 374 | | - path_reverse_path(); |
| 375 | | - db_finalize(&s); |
| 376 | | - return pid; |
| 377 | | - }else if( bag_find(&path.seen, pid) ){ |
| 378 | | - /* pid is just an alternative path on one of the legs */ |
| 379 | | - continue; |
| 380 | | - } |
| 381 | | - p = path_new_node(pid, pPrev, 0); |
| 382 | | - p->isPrim = pPrev->isPrim; |
| 383 | | - bag_insert(pPrev->isPrim ? &me : &you, pid); |
| 384 | | - } |
| 385 | | - db_reset(&s); |
| 386 | | - pPrev = pPrev->u.pPeer; |
| 387 | | - } |
| 350 | + while( (pThis = pqueuex_extract_ptr(&path.pending))!=0 ){ |
| 351 | + db_bind_int(&s, ":cid", pThis->rid); |
| 352 | + while( db_step(&s)==SQLITE_ROW ){ |
| 353 | + int pid = db_column_int(&s, 0); |
| 354 | + if( bag_find(pThis->isPrim ? &you : &me, pid) ){ |
| 355 | + /* pid is the common ancestor */ |
| 356 | + PathNode *pNext; |
| 357 | + for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} |
| 358 | + assert( p!=0 ); |
| 359 | + pNext = p; |
| 360 | + while( pNext ){ |
| 361 | + pNext = p->pFrom; |
| 362 | + p->pFrom = pThis; |
| 363 | + pThis = p; |
| 364 | + p = pNext; |
| 365 | + } |
| 366 | + if( pThis==path.pStart ) path.pStart = path.pEnd; |
| 367 | + path.pEnd = pThis; |
| 368 | + path_reverse_path(); |
| 369 | + db_finalize(&s); |
| 370 | + return pid; |
| 371 | + }else if( bag_find(pThis->isPrim ? &me : &you, pid) ){ |
| 372 | + /* pid is just an alternative path to a node we've already visited */ |
| 373 | + continue; |
| 374 | + } |
| 375 | + p = path_new_node(pid, pThis, 0); |
| 376 | + p->isPrim = pThis->isPrim; |
| 377 | + bag_insert(pThis->isPrim ? &me : &you, pid); |
| 378 | + } |
| 379 | + db_reset(&s); |
| 388 | 380 | } |
| 389 | 381 | db_finalize(&s); |
| 390 | 382 | path_reset(); |
| 391 | 383 | return 0; |
| 392 | 384 | } |
| 393 | 385 | |