Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgStatusCollector.java @ 248:3fbfce107f94
Issue 8: Means to find out information about given file at specific changeset. Inner ManifestRevisionInspector got promoted to ManifestRevision
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 12 Aug 2011 18:48:57 +0200 |
| parents | 1ec6b327a6ac |
| children | 81e9a3c9bafe |
comparison
equal
deleted
inserted
replaced
| 247:f052f40839ec | 248:3fbfce107f94 |
|---|---|
| 29 import java.util.TreeMap; | 29 import java.util.TreeMap; |
| 30 import java.util.TreeSet; | 30 import java.util.TreeSet; |
| 31 | 31 |
| 32 import org.tmatesoft.hg.core.HgDataStreamException; | 32 import org.tmatesoft.hg.core.HgDataStreamException; |
| 33 import org.tmatesoft.hg.core.Nodeid; | 33 import org.tmatesoft.hg.core.Nodeid; |
| 34 import org.tmatesoft.hg.internal.ManifestRevision; | |
| 34 import org.tmatesoft.hg.internal.Pool; | 35 import org.tmatesoft.hg.internal.Pool; |
| 35 import org.tmatesoft.hg.util.Path; | 36 import org.tmatesoft.hg.util.Path; |
| 36 import org.tmatesoft.hg.util.PathPool; | 37 import org.tmatesoft.hg.util.PathPool; |
| 37 import org.tmatesoft.hg.util.PathRewrite; | 38 import org.tmatesoft.hg.util.PathRewrite; |
| 38 | 39 |
| 44 * @author TMate Software Ltd. | 45 * @author TMate Software Ltd. |
| 45 */ | 46 */ |
| 46 public class HgStatusCollector { | 47 public class HgStatusCollector { |
| 47 | 48 |
| 48 private final HgRepository repo; | 49 private final HgRepository repo; |
| 49 private final SortedMap<Integer, ManifestRevisionInspector> cache; // sparse array, in fact | 50 private final SortedMap<Integer, ManifestRevision> cache; // sparse array, in fact |
| 50 // with cpython repository, ~70 000 changes, complete Log (direct out, no reverse) output | 51 // with cpython repository, ~70 000 changes, complete Log (direct out, no reverse) output |
| 51 // no cache limit, no nodeids and fname caching - OOME on changeset 1035 | 52 // no cache limit, no nodeids and fname caching - OOME on changeset 1035 |
| 52 // no cache limit, but with cached nodeids and filenames - 1730+ | 53 // no cache limit, but with cached nodeids and filenames - 1730+ |
| 53 // cache limit 100 - 19+ minutes to process 10000, and still working (too long, stopped) | 54 // cache limit 100 - 19+ minutes to process 10000, and still working (too long, stopped) |
| 54 private final int cacheMaxSize = 50; // do not keep too much manifest revisions | 55 private final int cacheMaxSize = 50; // do not keep too much manifest revisions |
| 55 private PathPool pathPool; | 56 private PathPool pathPool; |
| 56 private final Pool<Nodeid> cacheNodes; | 57 private final Pool<Nodeid> cacheNodes; |
| 57 private final Pool<String> cacheFilenames; // XXX in fact, need to think if use of PathPool directly instead is better solution | 58 private final Pool<String> cacheFilenames; // XXX in fact, need to think if use of PathPool directly instead is better solution |
| 58 private final ManifestRevisionInspector emptyFakeState; | 59 private final ManifestRevision emptyFakeState; |
| 59 private Path.Matcher scope = new Path.Matcher.Any(); | 60 private Path.Matcher scope = new Path.Matcher.Any(); |
| 60 | 61 |
| 61 | 62 |
| 62 public HgStatusCollector(HgRepository hgRepo) { | 63 public HgStatusCollector(HgRepository hgRepo) { |
| 63 this.repo = hgRepo; | 64 this.repo = hgRepo; |
| 64 cache = new TreeMap<Integer, ManifestRevisionInspector>(); | 65 cache = new TreeMap<Integer, ManifestRevision>(); |
| 65 cacheNodes = new Pool<Nodeid>(); | 66 cacheNodes = new Pool<Nodeid>(); |
| 66 cacheFilenames = new Pool<String>(); | 67 cacheFilenames = new Pool<String>(); |
| 67 | 68 |
| 68 emptyFakeState = new ManifestRevisionInspector(null, null); | 69 emptyFakeState = new ManifestRevision(null, null); |
| 69 emptyFakeState.begin(-1, null, -1); | 70 emptyFakeState.begin(-1, null, -1); |
| 70 emptyFakeState.end(-1); | 71 emptyFakeState.end(-1); |
| 71 } | 72 } |
| 72 | 73 |
| 73 public HgRepository getRepo() { | 74 public HgRepository getRepo() { |
| 74 return repo; | 75 return repo; |
| 75 } | 76 } |
| 76 | 77 |
| 77 private ManifestRevisionInspector get(int rev) { | 78 private ManifestRevision get(int rev) { |
| 78 ManifestRevisionInspector i = cache.get(rev); | 79 ManifestRevision i = cache.get(rev); |
| 79 if (i == null) { | 80 if (i == null) { |
| 80 if (rev == -1) { | 81 if (rev == -1) { |
| 81 return emptyFakeState; | 82 return emptyFakeState; |
| 82 } | 83 } |
| 83 while (cache.size() > cacheMaxSize) { | 84 while (cache.size() > cacheMaxSize) { |
| 84 // assume usually we go from oldest to newest, hence remove oldest as most likely to be no longer necessary | 85 // assume usually we go from oldest to newest, hence remove oldest as most likely to be no longer necessary |
| 85 cache.remove(cache.firstKey()); | 86 cache.remove(cache.firstKey()); |
| 86 } | 87 } |
| 87 i = new ManifestRevisionInspector(cacheNodes, cacheFilenames); | 88 i = new ManifestRevision(cacheNodes, cacheFilenames); |
| 88 cache.put(rev, i); | 89 cache.put(rev, i); |
| 89 repo.getManifest().walk(rev, rev, i); | 90 repo.getManifest().walk(rev, rev, i); |
| 90 } | 91 } |
| 91 return i; | 92 return i; |
| 92 } | 93 } |
| 99 while (cache.size() > cacheMaxSize) { | 100 while (cache.size() > cacheMaxSize) { |
| 100 // assume usually we go from oldest to newest, hence remove oldest as most likely to be no longer necessary | 101 // assume usually we go from oldest to newest, hence remove oldest as most likely to be no longer necessary |
| 101 cache.remove(cache.firstKey()); | 102 cache.remove(cache.firstKey()); |
| 102 } | 103 } |
| 103 repo.getManifest().walk(minRev, maxRev, new HgManifest.Inspector() { | 104 repo.getManifest().walk(minRev, maxRev, new HgManifest.Inspector() { |
| 104 private ManifestRevisionInspector delegate; | 105 private ManifestRevision delegate; |
| 105 private boolean cacheHit; // range may include revisions we already know about, do not re-create them | 106 private boolean cacheHit; // range may include revisions we already know about, do not re-create them |
| 106 | 107 |
| 107 public boolean begin(int manifestRevision, Nodeid nid, int changelogRevision) { | 108 public boolean begin(int manifestRevision, Nodeid nid, int changelogRevision) { |
| 108 assert delegate == null; | 109 assert delegate == null; |
| 109 if (cache.containsKey(changelogRevision)) { // don't need to check emptyFakeState hit as revision never -1 here | 110 if (cache.containsKey(changelogRevision)) { // don't need to check emptyFakeState hit as revision never -1 here |
| 110 cacheHit = true; | 111 cacheHit = true; |
| 111 } else { | 112 } else { |
| 112 cache.put(changelogRevision, delegate = new ManifestRevisionInspector(cacheNodes, cacheFilenames)); | 113 cache.put(changelogRevision, delegate = new ManifestRevision(cacheNodes, cacheFilenames)); |
| 113 // cache may grow bigger than max size here, but it's ok as present simplistic cache clearing mechanism may | 114 // cache may grow bigger than max size here, but it's ok as present simplistic cache clearing mechanism may |
| 114 // otherwise remove entries we just added | 115 // otherwise remove entries we just added |
| 115 delegate.begin(manifestRevision, nid, changelogRevision); | 116 delegate.begin(manifestRevision, nid, changelogRevision); |
| 116 cacheHit = false; | 117 cacheHit = false; |
| 117 } | 118 } |
| 134 return true; | 135 return true; |
| 135 } | 136 } |
| 136 }); | 137 }); |
| 137 } | 138 } |
| 138 | 139 |
| 139 /*package-local*/ ManifestRevisionInspector raw(int rev) { | 140 /*package-local*/ ManifestRevision raw(int rev) { |
| 140 return get(rev); | 141 return get(rev); |
| 141 } | 142 } |
| 142 /*package-local*/ PathPool getPathPool() { | 143 /*package-local*/ PathPool getPathPool() { |
| 143 if (pathPool == null) { | 144 if (pathPool == null) { |
| 144 pathPool = new PathPool(new PathRewrite.Empty()); | 145 pathPool = new PathPool(new PathRewrite.Empty()); |
| 191 if (rev2 == TIP) { | 192 if (rev2 == TIP) { |
| 192 rev2 = lastManifestRevision; | 193 rev2 = lastManifestRevision; |
| 193 } | 194 } |
| 194 // in fact, rev1 and rev2 are often next (or close) to each other, | 195 // in fact, rev1 and rev2 are often next (or close) to each other, |
| 195 // thus, we can optimize Manifest reads here (manifest.walk(rev1, rev2)) | 196 // thus, we can optimize Manifest reads here (manifest.walk(rev1, rev2)) |
| 196 ManifestRevisionInspector r1, r2 ; | 197 ManifestRevision r1, r2 ; |
| 197 boolean need1 = !cached(rev1), need2 = !cached(rev2); | 198 boolean need1 = !cached(rev1), need2 = !cached(rev2); |
| 198 if (need1 || need2) { | 199 if (need1 || need2) { |
| 199 int minRev, maxRev; | 200 int minRev, maxRev; |
| 200 if (need1 && need2 && Math.abs(rev1 - rev2) < 5 /*subjective equivalent of 'close enough'*/) { | 201 if (need1 && need2 && Math.abs(rev1 - rev2) < 5 /*subjective equivalent of 'close enough'*/) { |
| 201 minRev = rev1 < rev2 ? rev1 : rev2; | 202 minRev = rev1 < rev2 ? rev1 : rev2; |
| 420 } | 421 } |
| 421 l.add(p); | 422 l.add(p); |
| 422 return l; | 423 return l; |
| 423 } | 424 } |
| 424 } | 425 } |
| 425 | |
| 426 /*package-local*/ static final class ManifestRevisionInspector implements HgManifest.Inspector { | |
| 427 private final TreeMap<String, Nodeid> idsMap; | |
| 428 private final TreeMap<String, String> flagsMap; | |
| 429 private final Pool<Nodeid> idsPool; | |
| 430 private final Pool<String> namesPool; | |
| 431 | |
| 432 // optional pools for effective management of nodeids and filenames (they are likely | |
| 433 // to be duplicated among different manifest revisions | |
| 434 public ManifestRevisionInspector(Pool<Nodeid> nodeidPool, Pool<String> filenamePool) { | |
| 435 idsPool = nodeidPool; | |
| 436 namesPool = filenamePool; | |
| 437 idsMap = new TreeMap<String, Nodeid>(); | |
| 438 flagsMap = new TreeMap<String, String>(); | |
| 439 } | |
| 440 | |
| 441 public Collection<String> files() { | |
| 442 return idsMap.keySet(); | |
| 443 } | |
| 444 | |
| 445 public Nodeid nodeid(String fname) { | |
| 446 return idsMap.get(fname); | |
| 447 } | |
| 448 | |
| 449 public String flags(String fname) { | |
| 450 return flagsMap.get(fname); | |
| 451 } | |
| 452 | |
| 453 // | |
| 454 | |
| 455 public boolean next(Nodeid nid, String fname, String flags) { | |
| 456 if (namesPool != null) { | |
| 457 fname = namesPool.unify(fname); | |
| 458 } | |
| 459 if (idsPool != null) { | |
| 460 nid = idsPool.unify(nid); | |
| 461 } | |
| 462 idsMap.put(fname, nid); | |
| 463 if (flags != null) { | |
| 464 // TreeMap$Entry takes 32 bytes. No reason to keep null for such price | |
| 465 // Perhaps, Map<String, Pair<Nodeid, String>> might be better solution | |
| 466 flagsMap.put(fname, flags); | |
| 467 } | |
| 468 return true; | |
| 469 } | |
| 470 | |
| 471 public boolean end(int revision) { | |
| 472 // in fact, this class cares about single revision | |
| 473 return false; | |
| 474 } | |
| 475 | |
| 476 public boolean begin(int revision, Nodeid nid, int changelogRevision) { | |
| 477 return true; | |
| 478 } | |
| 479 } | |
| 480 | 426 |
| 481 } | 427 } |
