tikhomirov@254: package org.tmatesoft.hg.test; tikhomirov@254: tikhomirov@324: import static org.tmatesoft.hg.repo.HgRepository.TIP; tikhomirov@324: tikhomirov@255: import java.io.File; tikhomirov@255: import java.util.ArrayList; tikhomirov@256: import java.util.Arrays; tikhomirov@438: import java.util.Collections; tikhomirov@255: import java.util.HashMap; tikhomirov@255: import java.util.LinkedList; tikhomirov@255: import java.util.List; tikhomirov@255: import java.util.Map; tikhomirov@254: tikhomirov@324: import org.junit.Assert; tikhomirov@423: import org.tmatesoft.hg.core.HgCallbackTargetException; tikhomirov@255: import org.tmatesoft.hg.core.HgChangeset; tikhomirov@255: import org.tmatesoft.hg.core.HgChangesetHandler; tikhomirov@255: import org.tmatesoft.hg.core.HgException; tikhomirov@255: import org.tmatesoft.hg.core.HgLogCommand; tikhomirov@255: import org.tmatesoft.hg.core.Nodeid; tikhomirov@326: import org.tmatesoft.hg.internal.IntMap; tikhomirov@255: import org.tmatesoft.hg.repo.HgChangelog; tikhomirov@326: import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; tikhomirov@255: import org.tmatesoft.hg.repo.HgDataFile; tikhomirov@255: import org.tmatesoft.hg.repo.HgLookup; tikhomirov@255: import org.tmatesoft.hg.repo.HgManifest; tikhomirov@326: import org.tmatesoft.hg.repo.HgManifest.Flags; tikhomirov@255: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@255: import org.tmatesoft.hg.repo.HgTags; tikhomirov@254: import org.tmatesoft.hg.repo.HgTags.TagInfo; tikhomirov@433: import org.tmatesoft.hg.repo.HgRevisionMap; tikhomirov@255: import org.tmatesoft.hg.util.CancelledException; tikhomirov@255: import org.tmatesoft.hg.util.Path; tikhomirov@254: tikhomirov@254: /** tikhomirov@254: * @author Marc Strapetz tikhomirov@254: */ tikhomirov@254: public class MapTagsToFileRevisions { tikhomirov@254: tikhomirov@254: // Static ================================================================= tikhomirov@254: tikhomirov@255: public static void main(String[] args) throws Exception { tikhomirov@255: MapTagsToFileRevisions m = new MapTagsToFileRevisions(); tikhomirov@255: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@436: // m.measurePatchAffectsArbitraryRevisionRead(); tikhomirov@329: // m.collectTagsPerFile(); tikhomirov@307: // m.manifestWalk(); tikhomirov@307: // m.changelogWalk(); tikhomirov@324: // m.revisionMap(); tikhomirov@436: m.buildFile2ChangelogRevisionMap(".hgtags", "README", "configure.in", "Misc/NEWS"); tikhomirov@255: m = null; tikhomirov@255: System.gc(); tikhomirov@255: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@255: } tikhomirov@324: tikhomirov@329: tikhomirov@329: // revision == 2406 - 5 ms per run (baseRevision == 2406) tikhomirov@329: // revision == 2405 - 69 ms per run (baseRevision == 1403) tikhomirov@438: public void measurePatchAffectsArbitraryRevisionRead() throws Exception { tikhomirov@329: final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); tikhomirov@329: final DoNothingManifestInspector insp = new DoNothingManifestInspector(); tikhomirov@329: final int revision = 2405; tikhomirov@329: // warm-up. tikhomirov@329: repository.getManifest().walk(revision, revision, insp); tikhomirov@329: final int runs = 10; tikhomirov@329: final long start = System.nanoTime(); tikhomirov@329: for (int i = 0; i < runs; i++) { tikhomirov@329: repository.getManifest().walk(revision, revision, insp); tikhomirov@329: } tikhomirov@329: final long end = System.nanoTime(); tikhomirov@329: System.out.printf("%d ms per run\n", (end - start)/ (runs*1000000)); tikhomirov@329: } tikhomirov@329: tikhomirov@324: /* tikhomirov@324: * .hgtags, 261 revisions tikhomirov@324: * Approach 1: total 83, init: 0, iteration: 82 tikhomirov@324: * Approach 2: total 225, init: 206, iteration: 19 tikhomirov@324: * README, 465 revisions tikhomirov@324: * Approach 1: total 162, init: 0, iteration: 161 tikhomirov@324: * Approach 2: total 231, init: 198, iteration: 32 tikhomirov@324: * configure.in, 1109 revisions tikhomirov@324: * Approach 1: total 409, init: 1, iteration: 407 tikhomirov@324: * Approach 2: total 277, init: 203, iteration: 74 tikhomirov@324: */ tikhomirov@436: /* New data, 0.9.0v (another CPU!) tikhomirov@436: *.hgtags, 306 revisions tikhomirov@436: * Approach 0: total 136 tikhomirov@436: * Approach 1: total 53, init: 1, iteration: 52 tikhomirov@436: * Approach 2: total 95, init: 78, iteration: 17 tikhomirov@436: * Approach 3: total 17 tikhomirov@436: * tikhomirov@436: * README, 499 revisions tikhomirov@436: * Approach 0: total 149 tikhomirov@436: * Approach 1: total 43, init: 0, iteration: 43 tikhomirov@436: * Approach 2: total 102, init: 86, iteration: 16 tikhomirov@436: * Approach 3: total 18 tikhomirov@436: * tikhomirov@436: * configure.in, 1170 revisions tikhomirov@436: * Approach 0: total 321 tikhomirov@436: * Approach 1: total 116, init: 0, iteration: 115 tikhomirov@436: * Approach 2: total 140, init: 79, iteration: 60 tikhomirov@436: * Approach 3: total 30 tikhomirov@436: * tikhomirov@436: * Misc/NEWS, 10863 revisions tikhomirov@436: * Approach 0: total 4946 tikhomirov@436: * Approach 1: total 309, init: 6, iteration: 302 tikhomirov@436: * Approach 2: total 213, init: 63, iteration: 150 tikhomirov@436: * Approach 3: total 140 tikhomirov@436: */ tikhomirov@436: private void buildFile2ChangelogRevisionMap(String... fileNames) throws Exception { tikhomirov@436: final HgRepository repository = new HgLookup().detect(new File("/home/artem/hg/cpython")); tikhomirov@324: final HgChangelog clog = repository.getChangelog(); tikhomirov@324: // warm-up tikhomirov@433: HgRevisionMap clogMap = new HgRevisionMap(clog).init(); tikhomirov@324: tikhomirov@436: for (String fname : fileNames) { tikhomirov@436: HgDataFile fileNode = repository.getFileNode(fname); tikhomirov@436: // warm-up tikhomirov@436: HgRevisionMap fileMap = new HgRevisionMap(fileNode).init(); tikhomirov@436: // tikhomirov@436: final int latestRevision = fileNode.getLastRevision(); tikhomirov@436: // tikhomirov@436: final long start_0 = System.nanoTime(); tikhomirov@436: final Map changesetToNodeid_0 = new HashMap(); tikhomirov@436: for (int fileRevisionIndex = 0; fileRevisionIndex <= latestRevision; fileRevisionIndex++) { tikhomirov@436: Nodeid fileRevision = fileNode.getRevision(fileRevisionIndex); tikhomirov@436: Nodeid changesetRevision = fileNode.getChangesetRevision(fileRevision); tikhomirov@436: changesetToNodeid_0.put(changesetRevision, fileRevision); tikhomirov@324: } tikhomirov@436: final long end_0 = System.nanoTime(); tikhomirov@436: // tikhomirov@436: final long start_1 = System.nanoTime(); tikhomirov@436: fileMap = new HgRevisionMap(fileNode).init(); tikhomirov@436: final long start_1a = System.nanoTime(); tikhomirov@436: final Map changesetToNodeid_1 = new HashMap(); tikhomirov@436: for (int revision = 0; revision <= latestRevision; revision++) { tikhomirov@436: final Nodeid nodeId = fileMap.revision(revision); tikhomirov@436: int localCset = fileNode.getChangesetRevisionIndex(revision); tikhomirov@436: final Nodeid changesetId = clog.getRevision(localCset); tikhomirov@436: // final Nodeid changesetId = fileNode.getChangesetRevision(nodeId); tikhomirov@436: changesetToNodeid_1.put(changesetId, nodeId); tikhomirov@436: } tikhomirov@436: final long end_1 = System.nanoTime(); tikhomirov@436: // tikhomirov@436: final long start_2 = System.nanoTime(); tikhomirov@436: clogMap = new HgRevisionMap(clog).init(); tikhomirov@436: fileMap = new HgRevisionMap(fileNode).init(); tikhomirov@436: final Map changesetToNodeid_2 = new HashMap(); tikhomirov@436: final long start_2a = System.nanoTime(); tikhomirov@436: for (int revision = 0; revision <= latestRevision; revision++) { tikhomirov@436: Nodeid nidFile = fileMap.revision(revision); tikhomirov@436: int localCset = fileNode.getChangesetRevisionIndex(revision); tikhomirov@436: Nodeid nidCset = clogMap.revision(localCset); tikhomirov@436: changesetToNodeid_2.put(nidCset, nidFile); tikhomirov@436: } tikhomirov@436: final long end_2 = System.nanoTime(); tikhomirov@436: Assert.assertEquals(changesetToNodeid_1, changesetToNodeid_2); tikhomirov@436: // tikhomirov@436: final long start_3 = System.nanoTime(); tikhomirov@436: final Map changesetToNodeid_3 = new HashMap(); tikhomirov@436: fileNode.indexWalk(0, TIP, new HgDataFile.RevisionInspector() { tikhomirov@436: tikhomirov@436: public void next(int fileRevisionIndex, Nodeid revision, int linkedRevisionIndex) { tikhomirov@436: changesetToNodeid_3.put(clog.getRevision(linkedRevisionIndex), revision); tikhomirov@436: } tikhomirov@436: }); tikhomirov@436: final long end_3 = System.nanoTime(); tikhomirov@436: Assert.assertEquals(changesetToNodeid_1, changesetToNodeid_3); tikhomirov@436: System.out.printf("%s, %d revisions\n", fname, 1+latestRevision); tikhomirov@436: System.out.printf("Approach 0: total %d\n", (end_0 - start_0)/1000000); tikhomirov@436: System.out.printf("Approach 1: total %d, init: %d, iteration: %d\n", (end_1 - start_1)/1000000, (start_1a - start_1)/1000000, (end_1 - start_1a)/1000000); tikhomirov@436: System.out.printf("Approach 2: total %d, init: %d, iteration: %d\n", (end_2 - start_2)/1000000, (start_2a - start_2)/1000000, (end_2 - start_2a)/1000000); tikhomirov@436: System.out.printf("Approach 3: total %d\n", (end_3 - start_3)/1000000); tikhomirov@436: } tikhomirov@324: } tikhomirov@263: tikhomirov@307: /* tikhomirov@307: * Each 5000 revisions from cpython, total 15 revisions tikhomirov@367: * Direct clog.getRevisionIndex: ~260 ms tikhomirov@367: * RevisionMap.revisionIndex: ~265 ms (almost 100% in #init()) tikhomirov@307: * each 1000'th revision, total 71 revision: 1 230 vs 270 tikhomirov@307: * each 2000'th revision, total 36 revision: 620 vs 270 tikhomirov@307: * each 3000'th revision, total 24 revision: 410 vs 275 tikhomirov@307: */ tikhomirov@438: public void revisionMap() throws Exception { tikhomirov@307: final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); tikhomirov@307: final HgChangelog clog = repository.getChangelog(); tikhomirov@307: ArrayList revisions = new ArrayList(); tikhomirov@307: final int step = 5000; tikhomirov@307: for (int i = 0, top = clog.getLastRevision(); i < top; i += step) { tikhomirov@307: revisions.add(clog.getRevision(i)); tikhomirov@307: } tikhomirov@307: final long s1 = System.nanoTime(); tikhomirov@307: for (Nodeid n : revisions) { tikhomirov@367: int r = clog.getRevisionIndex(n); tikhomirov@307: if (r % step != 0) { tikhomirov@307: throw new IllegalStateException(Integer.toString(r)); tikhomirov@307: } tikhomirov@307: } tikhomirov@307: System.out.printf("Direct lookup of %d revisions took %,d ns\n", revisions.size(), System.nanoTime() - s1); tikhomirov@433: HgRevisionMap rmap = new HgRevisionMap(clog); tikhomirov@307: final long s2 = System.nanoTime(); tikhomirov@307: rmap.init(); tikhomirov@307: final long s3 = System.nanoTime(); tikhomirov@307: for (Nodeid n : revisions) { tikhomirov@367: int r = rmap.revisionIndex(n); tikhomirov@307: if (r % step != 0) { tikhomirov@307: throw new IllegalStateException(Integer.toString(r)); tikhomirov@307: } tikhomirov@307: } tikhomirov@307: System.out.printf("RevisionMap time: %d ms, of that init() %,d ns\n", (System.nanoTime() - s2) / 1000000, s3 - s2); tikhomirov@307: } tikhomirov@307: tikhomirov@438: public void changelogWalk() throws Exception { tikhomirov@307: final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); tikhomirov@263: final long start = System.currentTimeMillis(); tikhomirov@263: repository.getChangelog().all(new HgChangelog.Inspector() { tikhomirov@263: public int xx = 0; tikhomirov@263: tikhomirov@263: public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { tikhomirov@263: if (xx+revisionNumber < 0) { tikhomirov@263: System.out.println(xx); tikhomirov@263: System.out.println(revisionNumber); tikhomirov@263: } tikhomirov@263: xx += revisionNumber; tikhomirov@263: } tikhomirov@263: }); tikhomirov@263: // cpython: 17 seconds, mem 132,9 -> 129,0 -> 131,7 tikhomirov@263: // cpyhton: 13 seconds. Of that, cumulative Patch.apply takes 8.8 seconds, RevlogStream.Inspector.next - 1.8 tikhomirov@263: System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start); tikhomirov@263: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@263: } tikhomirov@263: tikhomirov@438: public void manifestWalk() throws Exception { tikhomirov@268: System.out.println(System.getProperty("java.version")); tikhomirov@263: final long start = System.currentTimeMillis(); tikhomirov@263: final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); tikhomirov@329: repository.getManifest().walk(0, 10000, new DoNothingManifestInspector()); tikhomirov@263: // cpython: 1,1 sec for 0..1000, 43 sec for 0..10000, 115 sec for 0..20000 (Pool with HashMap) tikhomirov@263: // 2,4 sec for 1000..2000 tikhomirov@263: // cpython -r 1000: 484 files, -r 2000: 1015 files. Iteration 1000..2000; fnamePool.size:1019 nodeidPool.size:2989 tikhomirov@263: // nodeidPool for two subsequent revisions only: 840. 37 sec for 0..10000. 99 sec for 0..20k tikhomirov@263: // 0..10000 fnamePool: hits:15989152, misses:3020 tikhomirov@268: // tikhomirov@268: // With Pool for fname and flags, Nodeid's ascii2bin through local array, overall byte[] iteration, tikhomirov@268: // 0..10k is 34 seconds now tikhomirov@304: // Another run, 23 seconds now, seems nothing has been changed. Switched to Pool2 with DirectHashSet: 22,5 seconds tikhomirov@263: System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start); tikhomirov@263: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@263: } tikhomirov@326: tikhomirov@433: private int[] collectLocalTagRevisions(HgRevisionMap clogrmap, TagInfo[] allTags, IntMap> tagLocalRev2TagInfo) { tikhomirov@326: int[] tagLocalRevs = new int[allTags.length]; tikhomirov@326: int x = 0; tikhomirov@326: for (int i = 0; i < allTags.length; i++) { tikhomirov@326: final Nodeid tagRevision = allTags[i].revision(); tikhomirov@368: final int tagRevisionIndex = clogrmap.revisionIndex(tagRevision); tikhomirov@368: if (tagRevisionIndex != HgRepository.BAD_REVISION) { tikhomirov@368: tagLocalRevs[x++] = tagRevisionIndex; tikhomirov@368: List tagsAssociatedWithRevision = tagLocalRev2TagInfo.get(tagRevisionIndex); tikhomirov@326: if (tagsAssociatedWithRevision == null) { tikhomirov@368: tagLocalRev2TagInfo.put(tagRevisionIndex, tagsAssociatedWithRevision = new LinkedList()); tikhomirov@326: } tikhomirov@326: tagsAssociatedWithRevision.add(allTags[i]); tikhomirov@326: } tikhomirov@326: } tikhomirov@326: if (x != allTags.length) { tikhomirov@326: // some tags were removed (recorded Nodeid.NULL tagname) tikhomirov@326: int[] copy = new int[x]; tikhomirov@326: System.arraycopy(tagLocalRevs, 0, copy, 0, x); tikhomirov@326: tagLocalRevs = copy; tikhomirov@326: } tikhomirov@326: return tagLocalRevs; tikhomirov@326: } tikhomirov@263: tikhomirov@438: public void collectTagsPerFile() throws HgException, CancelledException { tikhomirov@254: final long start = System.currentTimeMillis(); tikhomirov@438: final HgRepository repository = new HgLookup().detect(new File("/home/artem/hg/cpython")); tikhomirov@254: final HgTags tags = repository.getTags(); tikhomirov@254: // tikhomirov@254: // build cache tikhomirov@255: // tikhomirov@423: final TagInfo[] allTags = new TagInfo[tags.getAllTags().size()]; tikhomirov@423: tags.getAllTags().values().toArray(allTags); tikhomirov@326: // effective translation of changeset revisions to their local indexes tikhomirov@433: final HgRevisionMap clogrmap = new HgRevisionMap(repository.getChangelog()).init(); tikhomirov@326: // map to look up tag by changeset local number tikhomirov@326: final IntMap> tagLocalRev2TagInfo = new IntMap>(allTags.length); tikhomirov@326: System.out.printf("Collecting manifests for %d tags\n", allTags.length); tikhomirov@326: final int[] tagLocalRevs = collectLocalTagRevisions(clogrmap, allTags, tagLocalRev2TagInfo); tikhomirov@329: System.out.printf("Prepared %d tag revisions to analyze: %d ms\n", tagLocalRevs.length, System.currentTimeMillis() - start); tikhomirov@326: tikhomirov@326: final Path targetPath = Path.create("README"); tikhomirov@326: // tikhomirov@326: collectTagsPerFile_Approach_1(clogrmap, tagLocalRevs, allTags, targetPath); tikhomirov@326: System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start); tikhomirov@326: tikhomirov@326: System.out.println("\nApproach 2"); tikhomirov@438: collectTagsPerFile_Approach_2(repository, tagLocalRevs, tagLocalRev2TagInfo, targetPath); tikhomirov@326: } tikhomirov@326: tikhomirov@326: // Approach 1. Build map with all files, their revisions and corresponding tags tikhomirov@326: // tikhomirov@438: private void collectTagsPerFile_Approach_1(final HgRevisionMap clogrmap, final int[] tagLocalRevs, final TagInfo[] allTags, Path targetPath) throws HgException { tikhomirov@326: HgRepository repository = clogrmap.getRepo(); tikhomirov@326: final long start = System.currentTimeMillis(); tikhomirov@255: // file2rev2tag value is array of revisions, always of allTags.length. Revision index in the array tikhomirov@255: // is index of corresponding TagInfo in allTags; tikhomirov@285: final Map file2rev2tag = new HashMap(); tikhomirov@424: repository.getManifest().walk(new HgManifest.Inspector() { tikhomirov@256: private int[] tagIndexAtRev = new int[4]; // it's unlikely there would be a lot of tags associated with a given cset tikhomirov@254: tikhomirov@254: public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { tikhomirov@326: // may do better here using tagLocalRev2TagInfo, but need to change a lot, too lazy now tikhomirov@254: Nodeid cset = clogrmap.revision(changelogRevision); tikhomirov@256: Arrays.fill(tagIndexAtRev, -1); tikhomirov@256: for (int i = 0, x = 0; i < allTags.length; i++) { tikhomirov@255: if (cset.equals(allTags[i].revision())) { tikhomirov@256: tagIndexAtRev[x++] = i; tikhomirov@256: if (x == tagIndexAtRev.length) { tikhomirov@256: // expand twice as much tikhomirov@256: int[] expanded = new int[x << 1]; tikhomirov@256: System.arraycopy(tagIndexAtRev, 0, expanded, 0, x); tikhomirov@256: expanded[x] = -1; // just in case there'd be no more tags associated with this cset tikhomirov@256: tagIndexAtRev = expanded; tikhomirov@256: } tikhomirov@255: } tikhomirov@255: } tikhomirov@256: if (tagIndexAtRev[0] == -1) { tikhomirov@254: System.out.println("Can't happen, provided we iterate over revisions with tags only"); tikhomirov@254: } tikhomirov@254: return true; tikhomirov@254: } tikhomirov@285: tikhomirov@285: public boolean next(Nodeid nid, Path fname, HgManifest.Flags flags) { tikhomirov@255: Nodeid[] m = file2rev2tag.get(fname); tikhomirov@254: if (m == null) { tikhomirov@255: file2rev2tag.put(fname, m = new Nodeid[allTags.length]); tikhomirov@254: } tikhomirov@255: for (int tagIndex : tagIndexAtRev) { tikhomirov@256: if (tagIndex == -1) { tikhomirov@256: break; tikhomirov@256: } tikhomirov@255: if (m[tagIndex] != null) { tikhomirov@255: System.out.printf("There's another revision (%s) associated with tag %s already while we try to associate %s\n", m[tagIndex].shortNotation(), allTags[tagIndex].name(), nid.shortNotation()); tikhomirov@255: } tikhomirov@255: m[tagIndex] = nid; tikhomirov@254: } tikhomirov@254: return true; tikhomirov@254: } tikhomirov@254: tikhomirov@254: public boolean end(int manifestRevision) { tikhomirov@254: return true; tikhomirov@254: } tikhomirov@254: tikhomirov@254: }, tagLocalRevs); tikhomirov@255: System.out.printf("Cache built: %d ms\n", System.currentTimeMillis() - start); tikhomirov@254: // tikhomirov@254: // look up specific file. This part is fast. tikhomirov@254: HgDataFile fileNode = repository.getFileNode(targetPath); tikhomirov@285: final Nodeid[] allTagsOfTheFile = file2rev2tag.get(targetPath); tikhomirov@254: // TODO if fileNode.isCopy, repeat for each getCopySourceName() tikhomirov@368: for (int fileRevIndex = 0; fileRevIndex < fileNode.getRevisionCount(); fileRevIndex++) { tikhomirov@368: Nodeid fileRev = fileNode.getRevision(fileRevIndex); tikhomirov@368: int changesetRevIndex = fileNode.getChangesetRevisionIndex(fileRevIndex); tikhomirov@254: List associatedTags = new LinkedList(); tikhomirov@255: for (int i = 0; i < allTagsOfTheFile.length; i++) { tikhomirov@255: if (fileRev.equals(allTagsOfTheFile[i])) { tikhomirov@255: associatedTags.add(allTags[i].name()); tikhomirov@254: } tikhomirov@254: } tikhomirov@368: System.out.printf("%3d%7d%s\n", fileRevIndex, changesetRevIndex, associatedTags); tikhomirov@254: } tikhomirov@326: } tikhomirov@326: tikhomirov@438: private void collectTagsPerFile_Approach_2(HgRepository repository, final int[] tagLocalRevs, final IntMap> tagRevIndex2TagInfo, Path targetPath) throws HgException { tikhomirov@326: // tikhomirov@326: // Approach 2. No all-file map. Collect file revisions recorded at the time of tagging, tikhomirov@326: // then for each file revision check if it is among those above, and if yes, take corresponding tags tikhomirov@326: HgDataFile fileNode = repository.getFileNode(targetPath); tikhomirov@326: final long start2 = System.nanoTime(); tikhomirov@426: final Map fileRevisionAtTagRevision = new HashMap(); tikhomirov@438: final Map> fileRev2TagNames = new HashMap>(); tikhomirov@426: HgManifest.Inspector collectFileRevAtCset = new HgManifest.Inspector() { tikhomirov@426: tikhomirov@426: private int csetRevIndex; tikhomirov@426: tikhomirov@426: public boolean next(Nodeid nid, Path fname, Flags flags) { tikhomirov@426: fileRevisionAtTagRevision.put(csetRevIndex, nid); tikhomirov@438: if (tagRevIndex2TagInfo.containsKey(csetRevIndex)) { tikhomirov@438: List tags = fileRev2TagNames.get(nid); tikhomirov@438: if (tags == null) { tikhomirov@438: fileRev2TagNames.put(nid, tags = new ArrayList(3)); tikhomirov@438: } tikhomirov@438: for (TagInfo ti : tagRevIndex2TagInfo.get(csetRevIndex)) { tikhomirov@438: tags.add(ti.name()); tikhomirov@438: } tikhomirov@438: } tikhomirov@426: return true; tikhomirov@426: } tikhomirov@426: tikhomirov@426: public boolean end(int manifestRevision) { tikhomirov@426: return true; tikhomirov@426: } tikhomirov@426: tikhomirov@426: public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { tikhomirov@426: csetRevIndex = changelogRevision; tikhomirov@426: return true; tikhomirov@426: } tikhomirov@426: }; tikhomirov@426: repository.getManifest().walkFileRevisions(targetPath, collectFileRevAtCset,tagLocalRevs); tikhomirov@438: tikhomirov@326: final long start2a = System.nanoTime(); tikhomirov@438: fileNode.indexWalk(0, TIP, new HgDataFile.RevisionInspector() { tikhomirov@326: tikhomirov@368: public void next(int fileRevisionIndex, Nodeid fileRevision, int changesetRevisionIndex) { tikhomirov@326: List associatedTags = new LinkedList(); tikhomirov@438: tikhomirov@438: for (int taggedRevision : tagLocalRevs) { tikhomirov@326: // current file revision can't appear in tags that point to earlier changelog revisions (they got own file revision) tikhomirov@438: if (taggedRevision >= changesetRevisionIndex) { tikhomirov@326: // z points to some changeset with tag tikhomirov@438: Nodeid wasKnownAs = fileRevisionAtTagRevision.get(taggedRevision); tikhomirov@326: if (wasKnownAs.equals(fileRevision)) { tikhomirov@326: // has tag associated with changeset at index z tikhomirov@438: List tagsAtRev = tagRevIndex2TagInfo.get(taggedRevision); tikhomirov@326: assert tagsAtRev != null; tikhomirov@326: for (TagInfo ti : tagsAtRev) { tikhomirov@326: associatedTags.add(ti.name()); tikhomirov@326: } tikhomirov@326: } tikhomirov@326: } tikhomirov@326: } tikhomirov@438: // tikhomirov@368: System.out.printf("%3d%7d%s\n", fileRevisionIndex, changesetRevisionIndex, associatedTags); tikhomirov@326: } tikhomirov@326: }); tikhomirov@438: for (int i = 0, lastRev = fileNode.getLastRevision(); i <= lastRev; i++) { tikhomirov@438: Nodeid fileRevision = fileNode.getRevision(i); tikhomirov@438: List associatedTags2 = fileRev2TagNames.get(fileRevision); tikhomirov@438: int changesetRevIndex = fileNode.getChangesetRevisionIndex(i); tikhomirov@438: System.out.printf("%3d%7d%s\n", i, changesetRevIndex, associatedTags2 == null ? Collections.emptyList() : associatedTags2); tikhomirov@438: } tikhomirov@326: System.out.printf("Alternative total time: %d ms, of that init: %d ms\n", (System.nanoTime() - start2)/1000000, (start2a-start2)/1000000); tikhomirov@255: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@254: } tikhomirov@254: tikhomirov@424: static class DoNothingManifestInspector implements HgManifest.Inspector { tikhomirov@329: public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { tikhomirov@329: return true; tikhomirov@329: } tikhomirov@329: public boolean next(Nodeid nid, Path fname, Flags flags) { tikhomirov@329: return true; tikhomirov@329: } tikhomirov@329: public boolean end(int manifestRevision) { tikhomirov@329: return true; tikhomirov@329: } tikhomirov@329: } tikhomirov@329: tikhomirov@423: public static void main2(String[] args) throws HgCallbackTargetException, HgException, CancelledException { tikhomirov@254: final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); tikhomirov@254: final Path targetPath = Path.create("README"); tikhomirov@254: final HgTags tags = repository.getTags(); tikhomirov@423: final Map tagToInfo = tags.getAllTags(); tikhomirov@254: final HgManifest manifest = repository.getManifest(); tikhomirov@254: final Map> changeSetRevisionToTags = new HashMap>(); tikhomirov@254: final HgDataFile fileNode = repository.getFileNode(targetPath); tikhomirov@254: for (String tagName : tagToInfo.keySet()) { tikhomirov@254: final HgTags.TagInfo info = tagToInfo.get(tagName); tikhomirov@254: final Nodeid nodeId = info.revision(); tikhomirov@254: // TODO: This is not correct as we can't be sure that file at the corresponding revision is actually our target file (which may have been renamed, etc.) tikhomirov@367: final Nodeid fileRevision = manifest.getFileRevision(repository.getChangelog().getRevisionIndex(nodeId), targetPath); tikhomirov@254: if (fileRevision == null) { tikhomirov@254: continue; tikhomirov@254: } tikhomirov@254: tikhomirov@254: final Nodeid changeSetRevision = fileNode.getChangesetRevision(fileRevision); tikhomirov@254: List revisionTags = changeSetRevisionToTags.get(changeSetRevision); tikhomirov@254: if (revisionTags == null) { tikhomirov@254: revisionTags = new ArrayList(); tikhomirov@254: changeSetRevisionToTags.put(changeSetRevision, revisionTags); tikhomirov@254: } tikhomirov@254: revisionTags.add(tagName); tikhomirov@254: } tikhomirov@254: tikhomirov@254: final HgLogCommand logCommand = new HgLogCommand(repository); tikhomirov@254: logCommand.file(targetPath, true); tikhomirov@254: logCommand.execute(new HgChangesetHandler() { tikhomirov@427: public void cset(HgChangeset changeset) { tikhomirov@254: if (changeset.getAffectedFiles().contains(targetPath)) { tikhomirov@423: System.out.println(changeset.getRevisionIndex() + " " + changeSetRevisionToTags.get(changeset.getNodeid())); tikhomirov@254: } tikhomirov@254: } tikhomirov@254: }); tikhomirov@254: } tikhomirov@254: }