Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgDataFile.java @ 317:09628675bcee
Rework file history build approach to match rest of the API
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> | 
|---|---|
| date | Thu, 29 Sep 2011 03:20:28 +0200 | 
| parents | 971baa95fb07 | 
| children | d68dcb3b5f49 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 316:ee6b467c1a5f | 317:09628675bcee | 
|---|---|
| 28 import java.util.ArrayList; | 28 import java.util.ArrayList; | 
| 29 import java.util.Arrays; | 29 import java.util.Arrays; | 
| 30 import java.util.Collection; | 30 import java.util.Collection; | 
| 31 import java.util.Collections; | 31 import java.util.Collections; | 
| 32 import java.util.List; | 32 import java.util.List; | 
| 33 import java.util.NoSuchElementException; | |
| 34 | 33 | 
| 35 import org.tmatesoft.hg.core.HgDataStreamException; | 34 import org.tmatesoft.hg.core.HgDataStreamException; | 
| 36 import org.tmatesoft.hg.core.HgException; | 35 import org.tmatesoft.hg.core.HgException; | 
| 37 import org.tmatesoft.hg.core.Nodeid; | 36 import org.tmatesoft.hg.core.Nodeid; | 
| 38 import org.tmatesoft.hg.internal.DataAccess; | 37 import org.tmatesoft.hg.internal.DataAccess; | 
| 39 import org.tmatesoft.hg.internal.Experimental; | |
| 40 import org.tmatesoft.hg.internal.FilterByteChannel; | 38 import org.tmatesoft.hg.internal.FilterByteChannel; | 
| 41 import org.tmatesoft.hg.internal.FilterDataAccess; | 39 import org.tmatesoft.hg.internal.FilterDataAccess; | 
| 42 import org.tmatesoft.hg.internal.IntMap; | 40 import org.tmatesoft.hg.internal.IntMap; | 
| 43 import org.tmatesoft.hg.internal.RevlogStream; | 41 import org.tmatesoft.hg.internal.RevlogStream; | 
| 44 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; | |
| 45 import org.tmatesoft.hg.util.ByteChannel; | 42 import org.tmatesoft.hg.util.ByteChannel; | 
| 46 import org.tmatesoft.hg.util.CancelSupport; | 43 import org.tmatesoft.hg.util.CancelSupport; | 
| 47 import org.tmatesoft.hg.util.CancelledException; | 44 import org.tmatesoft.hg.util.CancelledException; | 
| 48 import org.tmatesoft.hg.util.Pair; | 45 import org.tmatesoft.hg.util.Pair; | 
| 49 import org.tmatesoft.hg.util.Path; | 46 import org.tmatesoft.hg.util.Path; | 
| 229 // shall not happen, unless we changed ContentPipe or its subclass | 226 // shall not happen, unless we changed ContentPipe or its subclass | 
| 230 throw new HgDataStreamException(getPath(), ex.getClass().getName(), ex); | 227 throw new HgDataStreamException(getPath(), ex.getClass().getName(), ex); | 
| 231 } | 228 } | 
| 232 } | 229 } | 
| 233 | 230 | 
| 234 @Experimental(reason="Investigate approaches to build complete file history log") | 231 private static class HistoryNode { | 
| 235 public interface HistoryWalker { | 232 int changeset; | 
| 236 void next(); | 233 Nodeid cset; | 
| 237 boolean hasNext(); | 234 HistoryNode parent1, parent2; | 
| 238 Nodeid changesetRevision(); | 235 List<HistoryNode> children; | 
| 239 RawChangeset changeset(); | 236 | 
| 240 boolean isFork(); | 237 HistoryNode(int cs, HistoryNode p1, HistoryNode p2) { | 
| 241 boolean isJoin(); | 238 changeset = cs; | 
| 242 Pair<Nodeid, Nodeid> parentChangesets(); | 239 parent1 = p1; | 
| 243 Collection<Nodeid> childChangesets(); | 240 parent2 = p2; | 
| 244 } | 241 if (p1 != null) { | 
| 245 | 242 p1.addChild(this); | 
| 246 public HistoryWalker history() { | 243 } | 
| 247 final boolean[] needsSorting = { false }; | 244 if (p2 != null) { | 
| 248 class HistoryNode { | 245 p2.addChild(this); | 
| 249 int changeset; | 246 } | 
| 250 Nodeid cset; | 247 } | 
| 251 HistoryNode parent1, parent2; | 248 | 
| 252 List<HistoryNode> children; | 249 Nodeid changesetRevision() { | 
| 253 | 250 assert cset != null : "we initialize all csets prior to use"; | 
| 254 public HistoryNode(int cs, HistoryNode p1, HistoryNode p2) { | 251 return cset; | 
| 255 changeset = cs; | 252 } | 
| 256 parent1 = p1; | 253 | 
| 257 parent2 = p2; | 254 void addChild(HistoryNode child) { | 
| 258 if (p1 != null) { | 255 if (children == null) { | 
| 259 p1.addChild(this); | 256 children = new ArrayList<HistoryNode>(2); | 
| 260 } | 257 } | 
| 261 if (p2 != null) { | 258 children.add(child); | 
| 262 p2.addChild(this); | 259 } | 
| 263 } | 260 } | 
| 264 } | 261 | 
| 265 | 262 public void history(HgChangelog.TreeInspector inspector) { | 
| 266 public Nodeid changesetRevision() { | 263 final CancelSupport cancelSupport = CancelSupport.Factory.get(inspector); | 
| 267 if (cset == null) { | 264 try { | 
| 268 cset = getRepo().getChangelog().getRevision(changeset); | 265 final boolean[] needsSorting = { false }; | 
| 269 } | 266 final HistoryNode[] completeHistory = new HistoryNode[getRevisionCount()]; | 
| 270 return cset; | 267 final int[] commitRevisions = new int[completeHistory.length]; | 
| 271 } | 268 RevlogStream.Inspector insp = new RevlogStream.Inspector() { | 
| 272 | 269 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 
| 273 public void addChild(HistoryNode child) { | 270 if (revisionNumber > 0) { | 
| 274 if (children == null) { | 271 if (commitRevisions[revisionNumber-1] > linkRevision) { | 
| 275 children = new ArrayList<HistoryNode>(2); | 272 needsSorting[0] = true; | 
| 276 } | 273 } | 
| 277 children.add(child); | 274 } | 
| 278 } | 275 commitRevisions[revisionNumber] = linkRevision; | 
| 279 } | 276 HistoryNode p1 = null, p2 = null; | 
| 280 final HistoryNode[] completeHistory = new HistoryNode[getRevisionCount()]; | 277 if (parent1Revision != -1) { | 
| 281 final int[] commitRevisions = new int[completeHistory.length]; | 278 p1 = completeHistory[parent1Revision]; | 
| 282 RevlogStream.Inspector insp = new RevlogStream.Inspector() { | 279 } | 
| 283 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 280 if (parent2Revision != -1) { | 
| 284 if (revisionNumber > 0) { | 281 p2 = completeHistory[parent2Revision]; | 
| 285 if (commitRevisions[revisionNumber-1] > linkRevision) { | 282 } | 
| 286 needsSorting[0] = true; | 283 completeHistory[revisionNumber] = new HistoryNode(linkRevision, p1, p2); | 
| 287 } | 284 } | 
| 288 } | 285 }; | 
| 289 HistoryNode p1 = null, p2 = null; | 286 content.iterate(0, getLastRevision(), false, insp); | 
| 290 if (parent1Revision != -1) { | 287 cancelSupport.checkCancelled(); | 
| 291 p1 = completeHistory[parent1Revision]; | 288 if (needsSorting[0]) { | 
| 292 } | 289 Arrays.sort(commitRevisions); | 
| 293 if (parent2Revision != -1) { | 290 } | 
| 294 p2 = completeHistory[parent2Revision]; | 291 // read changeset revisions at once (to avoid numerous changelog.getRevision reads) | 
| 295 } | 292 // but just nodeids, not RawChangeset (changelog.iterate(data=false) | 
| 296 completeHistory[revisionNumber] = new HistoryNode(linkRevision, p1, p2); | 293 ArrayList<Nodeid> changesetRevisions = new ArrayList<Nodeid>(commitRevisions.length); | 
| 297 } | 294 getRepo().getChangelog().getRevisionsInternal(changesetRevisions, commitRevisions); | 
| 298 }; | 295 cancelSupport.checkCancelled(); | 
| 299 content.iterate(0, getLastRevision(), false, insp); | 296 // assign them to corresponding HistoryNodes | 
| 300 // XXX may read changeset revisions at once (to avoid numerous changelog.getRevision reads) | 297 for (int i = 0; i < completeHistory.length; i++ ) { | 
| 301 // but perhaps just nodeids, not RawChangeset (changelog.iterate(data=false) | 298 final HistoryNode n = completeHistory[i]; | 
| 302 // XXX sort completeHistory according to changeset numbers? | 299 if (needsSorting[0]) { | 
| 303 return new HistoryWalker() { | 300 int x = Arrays.binarySearch(commitRevisions, n.changeset); | 
| 304 private int index = -1; | 301 assert x >= 0; | 
| 305 | 302 n.cset = changesetRevisions.get(x); | 
| 306 public void next() { | 303 } else { | 
| 307 if (!hasNext()) { | 304 // commit revisions were not sorted, may use original index directly | 
| 308 throw new NoSuchElementException(); | 305 n.cset = changesetRevisions.get(i); | 
| 309 } | 306 } | 
| 310 index++; | 307 } | 
| 311 } | 308 cancelSupport.checkCancelled(); | 
| 312 | 309 // XXX shall sort completeHistory according to changeset numbers? | 
| 313 public boolean hasNext() { | 310 for (int i = 0; i < completeHistory.length; i++ ) { | 
| 314 return index+1 < completeHistory.length; | 311 final HistoryNode n = completeHistory[i]; | 
| 315 } | |
| 316 | |
| 317 public Pair<Nodeid, Nodeid> parentChangesets() { | |
| 318 HistoryNode p; | 312 HistoryNode p; | 
| 319 Nodeid p1, p2; | 313 Nodeid p1, p2; | 
| 320 if ((p = completeHistory[index].parent1) != null) { | 314 if ((p = n.parent1) != null) { | 
| 321 p1 = p.changesetRevision(); | 315 p1 = p.changesetRevision(); | 
| 322 } else { | 316 } else { | 
| 323 p1 = Nodeid.NULL; | 317 p1 = Nodeid.NULL; | 
| 324 } | 318 } | 
| 325 if ((p = completeHistory[index].parent2) != null) { | 319 if ((p= n.parent2) != null) { | 
| 326 p2 = p.changesetRevision(); | 320 p2 = p.changesetRevision(); | 
| 327 } else { | 321 } else { | 
| 328 p2 = Nodeid.NULL; | 322 p2 = Nodeid.NULL; | 
| 329 } | 323 } | 
| 330 return new Pair<Nodeid, Nodeid>(p1, p2); | 324 final Pair<Nodeid, Nodeid> parentChangesets = new Pair<Nodeid, Nodeid>(p1, p2); | 
| 331 } | 325 final List<Nodeid> childChangesets; | 
| 332 | |
| 333 public boolean isJoin() { | |
| 334 return completeHistory[index].parent1 != null && completeHistory[index].parent2 != null; | |
| 335 } | |
| 336 | |
| 337 public boolean isFork() { | |
| 338 HistoryNode n = completeHistory[index]; | |
| 339 return n.children != null && n.children.size() > 1; | |
| 340 } | |
| 341 | |
| 342 public Collection<Nodeid> childChangesets() { | |
| 343 HistoryNode n = completeHistory[index]; | |
| 344 if (n.children == null) { | 326 if (n.children == null) { | 
| 345 return Collections.emptyList(); | 327 childChangesets = Collections.emptyList(); | 
| 346 } | 328 } else { | 
| 347 ArrayList<Nodeid> rv = new ArrayList<Nodeid>(n.children.size()); | 329 Nodeid[] revisions = new Nodeid[n.children.size()]; | 
| 348 for (HistoryNode hn : n.children) { | 330 int j = 0; | 
| 349 rv.add(hn.changesetRevision()); | 331 for (HistoryNode hn : n.children) { | 
| 350 } | 332 revisions[j++] = hn.changesetRevision(); | 
| 351 return rv; | 333 } | 
| 352 } | 334 childChangesets = Arrays.asList(revisions); | 
| 353 | 335 } | 
| 354 public Nodeid changesetRevision() { | 336 inspector.next(n.changesetRevision(), parentChangesets, childChangesets); | 
| 355 return completeHistory[index].changesetRevision(); | 337 cancelSupport.checkCancelled(); | 
| 356 } | 338 } | 
| 357 | 339 } catch (CancelledException ex) { | 
| 358 public RawChangeset changeset() { | 340 return; | 
| 359 final int cs = completeHistory[index].changeset; | 341 } | 
| 360 return getRepo().getChangelog().range(cs, cs).get(0); | |
| 361 } | |
| 362 }; | |
| 363 } | 342 } | 
| 364 | 343 | 
| 365 public void history(HgChangelog.Inspector inspector) { | 344 public void history(HgChangelog.Inspector inspector) { | 
| 366 history(0, getLastRevision(), inspector); | 345 history(0, getLastRevision(), inspector); | 
| 367 } | 346 } | 
