| | @@ -24,11 +24,12 @@ |
| 24 | 24 | #if INTERFACE |
| 25 | 25 | /* Nodes for the paths through the DAG. |
| 26 | 26 | */ |
| 27 | 27 | struct PathNode { |
| 28 | 28 | int rid; /* ID for this node */ |
| 29 | | - int fromIsParent; /* True if pFrom is the parent of rid */ |
| 29 | + u8 fromIsParent; /* True if pFrom is the parent of rid */ |
| 30 | + u8 isPrim; /* True if primary side of common ancestor */ |
| 30 | 31 | PathNode *pFrom; /* Node we came from */ |
| 31 | 32 | union { |
| 32 | 33 | PathNode *pPeer; /* List of nodes of the same generation */ |
| 33 | 34 | PathNode *pTo; /* Next on path from beginning to end */ |
| 34 | 35 | } u; |
| | @@ -43,10 +44,11 @@ |
| 43 | 44 | PathNode *pCurrent; /* Current generation of nodes */ |
| 44 | 45 | PathNode *pAll; /* All nodes */ |
| 45 | 46 | Bag seen; /* Nodes seen before */ |
| 46 | 47 | int nStep; /* Number of steps from first to last */ |
| 47 | 48 | PathNode *pStart; /* Earliest node */ |
| 49 | + PathNode *pPivot; /* Common ancestor of pStart and pEnd */ |
| 48 | 50 | PathNode *pEnd; /* Most recent */ |
| 49 | 51 | } path; |
| 50 | 52 | |
| 51 | 53 | /* |
| 52 | 54 | ** Return the first (last) element of the computed path. |
| | @@ -64,18 +66,18 @@ |
| 64 | 66 | */ |
| 65 | 67 | static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){ |
| 66 | 68 | PathNode *p; |
| 67 | 69 | |
| 68 | 70 | p = fossil_malloc( sizeof(*p) ); |
| 71 | + memset(p, 0, sizeof(*p)); |
| 69 | 72 | p->rid = rid; |
| 70 | 73 | p->fromIsParent = isParent; |
| 71 | 74 | p->pFrom = pFrom; |
| 72 | 75 | p->u.pPeer = path.pCurrent; |
| 73 | 76 | path.pCurrent = p; |
| 74 | 77 | p->pAll = path.pAll; |
| 75 | 78 | path.pAll = p; |
| 76 | | - path.pEnd = p; |
| 77 | 79 | bag_insert(&path.seen, rid); |
| 78 | 80 | return p; |
| 79 | 81 | } |
| 80 | 82 | |
| 81 | 83 | /* |
| | @@ -92,16 +94,34 @@ |
| 92 | 94 | path.pCurrent = 0; |
| 93 | 95 | path.pAll = 0; |
| 94 | 96 | path.pEnd = 0; |
| 95 | 97 | path.nStep = 0; |
| 96 | 98 | } |
| 99 | + |
| 100 | +/* |
| 101 | +** Construct the path from path.pStart to path.pEnd in the u.pTo fields. |
| 102 | +*/ |
| 103 | +void path_reverse_path(void){ |
| 104 | + PathNode *p; |
| 105 | + for(p=path.pEnd; p && p->pFrom; p = p->pFrom){ |
| 106 | + p->pFrom->u.pTo = p; |
| 107 | + } |
| 108 | + path.pEnd->u.pTo = 0; |
| 109 | + assert( p==path.pStart ); |
| 110 | +} |
| 97 | 111 | |
| 98 | 112 | /* |
| 99 | 113 | ** Compute the shortest path from iFrom to iTo |
| 100 | 114 | ** |
| 101 | 115 | ** If directOnly is true, then use only the "primary" links from parent to |
| 102 | 116 | ** child. In other words, ignore merges. |
| 117 | +** |
| 118 | +** Return a pointer to the beginning of the path (the iFrom node). |
| 119 | +** Elements of the path can be traversed by following the PathNode.u.pTo |
| 120 | +** pointer chain. |
| 121 | +** |
| 122 | +** Return NULL if no path is found. |
| 103 | 123 | */ |
| 104 | 124 | PathNode *path_shortest(int iFrom, int iTo, int directOnly){ |
| 105 | 125 | Stmt s; |
| 106 | 126 | PathNode *pPrev; |
| 107 | 127 | PathNode *p; |
| | @@ -133,34 +153,24 @@ |
| 133 | 153 | int isParent = db_column_int(&s, 1); |
| 134 | 154 | if( bag_find(&path.seen, cid) ) continue; |
| 135 | 155 | p = path_new_node(cid, pPrev, isParent); |
| 136 | 156 | if( cid==iTo ){ |
| 137 | 157 | db_finalize(&s); |
| 138 | | - return p; |
| 158 | + path.pEnd = p; |
| 159 | + path_reverse_path(); |
| 160 | + return path.pStart; |
| 139 | 161 | } |
| 140 | 162 | } |
| 141 | 163 | db_reset(&s); |
| 142 | 164 | pPrev = pPrev->u.pPeer; |
| 143 | 165 | } |
| 144 | 166 | } |
| 167 | + db_finalize(&s); |
| 145 | 168 | path_reset(); |
| 146 | 169 | return 0; |
| 147 | 170 | } |
| 148 | 171 | |
| 149 | | -/* |
| 150 | | -** Construct the path from path.pStart to path.pEnd in the u.pTo fields. |
| 151 | | -*/ |
| 152 | | -PathNode *path_reverse_path(void){ |
| 153 | | - PathNode *p; |
| 154 | | - for(p=path.pEnd; p && p->pFrom; p = p->pFrom){ |
| 155 | | - p->pFrom->u.pTo = p; |
| 156 | | - } |
| 157 | | - path.pEnd->u.pTo = 0; |
| 158 | | - assert( p==path.pStart ); |
| 159 | | - return p; |
| 160 | | -} |
| 161 | | - |
| 162 | 172 | /* |
| 163 | 173 | ** Find the mid-point of the path. If the path contains fewer than |
| 164 | 174 | ** 2 steps, return 0. |
| 165 | 175 | */ |
| 166 | 176 | PathNode *path_midpoint(void){ |
| | @@ -193,11 +203,10 @@ |
| 193 | 203 | iTo = name_to_rid(g.argv[3]); |
| 194 | 204 | p = path_shortest(iFrom, iTo, directOnly); |
| 195 | 205 | if( p==0 ){ |
| 196 | 206 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 197 | 207 | } |
| 198 | | - path_reverse_path(); |
| 199 | 208 | for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ |
| 200 | 209 | char *z; |
| 201 | 210 | z = db_text(0, |
| 202 | 211 | "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)" |
| 203 | 212 | " FROM blob, event" |
| | @@ -210,10 +219,109 @@ |
| 210 | 219 | }else{ |
| 211 | 220 | printf("\n"); |
| 212 | 221 | } |
| 213 | 222 | } |
| 214 | 223 | } |
| 224 | + |
| 225 | +/* |
| 226 | +** Find the closest common ancestor of two nodes. "Closest" means the |
| 227 | +** fewest number of arcs. |
| 228 | +*/ |
| 229 | +int path_common_ancestor(int iMe, int iYou){ |
| 230 | + Stmt s; |
| 231 | + PathNode *pPrev; |
| 232 | + PathNode *p; |
| 233 | + Bag me, you; |
| 234 | + |
| 235 | + if( iMe==iYou ) return iMe; |
| 236 | + if( iMe==0 || iYou==0 ) return 0; |
| 237 | + path_reset(); |
| 238 | + path.pStart = path_new_node(iMe, 0, 0); |
| 239 | + path.pStart->isPrim = 1; |
| 240 | + path.pEnd = path_new_node(iYou, 0, 0); |
| 241 | + db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid"); |
| 242 | + bag_init(&me); |
| 243 | + bag_insert(&me, iMe); |
| 244 | + bag_init(&you); |
| 245 | + bag_insert(&you, iYou); |
| 246 | + while( path.pCurrent ){ |
| 247 | + pPrev = path.pCurrent; |
| 248 | + path.pCurrent = 0; |
| 249 | + while( pPrev ){ |
| 250 | + db_bind_int(&s, ":cid", pPrev->rid); |
| 251 | + while( db_step(&s)==SQLITE_ROW ){ |
| 252 | + int pid = db_column_int(&s, 0); |
| 253 | + if( bag_find(pPrev->isPrim ? &you : &me, pid) ){ |
| 254 | + /* pid is the common ancestor */ |
| 255 | + PathNode *pNext; |
| 256 | + for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} |
| 257 | + assert( p!=0 ); |
| 258 | + pNext = p; |
| 259 | + while( pNext ){ |
| 260 | + pNext = p->pFrom; |
| 261 | + p->pFrom = pPrev; |
| 262 | + pPrev = p; |
| 263 | + p = pNext; |
| 264 | + } |
| 265 | + if( pPrev==path.pStart ) path.pStart = path.pEnd; |
| 266 | + path.pEnd = pPrev; |
| 267 | + path_reverse_path(); |
| 268 | + db_finalize(&s); |
| 269 | + return pid; |
| 270 | + }else if( bag_find(&path.seen, pid) ){ |
| 271 | + /* pid is just an alternative path on one of the legs */ |
| 272 | + continue; |
| 273 | + } |
| 274 | + p = path_new_node(pid, pPrev, 0); |
| 275 | + p->isPrim = pPrev->isPrim; |
| 276 | + bag_insert(pPrev->isPrim ? &me : &you, pid); |
| 277 | + } |
| 278 | + db_reset(&s); |
| 279 | + pPrev = pPrev->u.pPeer; |
| 280 | + } |
| 281 | + } |
| 282 | + db_finalize(&s); |
| 283 | + path_reset(); |
| 284 | + return 0; |
| 285 | +} |
| 286 | + |
| 287 | +/* |
| 288 | +** COMMAND: test-ancestor-path |
| 289 | +** |
| 290 | +** Usage: %fossil test-ancestor-path VERSION1 VERSION2 |
| 291 | +** |
| 292 | +** Report the path from VERSION1 to VERSION2 through their most recent |
| 293 | +** common ancestor. |
| 294 | +*/ |
| 295 | +void ancestor_path_test_cmd(void){ |
| 296 | + int iFrom; |
| 297 | + int iTo; |
| 298 | + int iPivot; |
| 299 | + PathNode *p; |
| 300 | + int n; |
| 301 | + |
| 302 | + db_find_and_open_repository(0,0); |
| 303 | + if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 304 | + iFrom = name_to_rid(g.argv[2]); |
| 305 | + iTo = name_to_rid(g.argv[3]); |
| 306 | + iPivot = path_common_ancestor(iFrom, iTo); |
| 307 | + for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ |
| 308 | + char *z; |
| 309 | + z = db_text(0, |
| 310 | + "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)" |
| 311 | + " FROM blob, event" |
| 312 | + " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'", |
| 313 | + p->rid, p->rid); |
| 314 | + printf("%4d: %s", n, z); |
| 315 | + fossil_free(z); |
| 316 | + if( p->rid==iFrom ) printf(" VERSION1"); |
| 317 | + if( p->rid==iTo ) printf(" VERSION2"); |
| 318 | + if( p->rid==iPivot ) printf(" PIVOT"); |
| 319 | + printf("\n"); |
| 320 | + } |
| 321 | +} |
| 322 | + |
| 215 | 323 | |
| 216 | 324 | /* |
| 217 | 325 | ** A record of a file rename operation. |
| 218 | 326 | */ |
| 219 | 327 | typedef struct NameChange NameChange; |
| 220 | 328 | |