tikhomirov@254: package org.tmatesoft.hg.test; tikhomirov@254: tikhomirov@255: import java.io.File; tikhomirov@255: import java.util.ArrayList; tikhomirov@256: import java.util.Arrays; 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@285: import org.tmatesoft.hg.core.HgBadStateException; 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@307: import org.tmatesoft.hg.internal.ArrayHelper; tikhomirov@255: import org.tmatesoft.hg.repo.HgChangelog; 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@255: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@255: import org.tmatesoft.hg.repo.HgTags; tikhomirov@263: import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; tikhomirov@285: import org.tmatesoft.hg.repo.HgManifest.Flags; tikhomirov@254: import org.tmatesoft.hg.repo.HgTags.TagInfo; 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@304: // m.collectTagsPerFile(); tikhomirov@307: // m.manifestWalk(); tikhomirov@307: // m.changelogWalk(); tikhomirov@307: m.revisionMap(); tikhomirov@255: m = null; tikhomirov@255: System.gc(); tikhomirov@255: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@255: } tikhomirov@263: tikhomirov@307: /* tikhomirov@307: * Each 5000 revisions from cpython, total 15 revisions tikhomirov@307: * Direct clog.getLocalRevision: ~260 ms tikhomirov@307: * RevisionMap.localRevision: ~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@307: private 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@307: int r = clog.getLocalRevision(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@307: HgChangelog.RevisionMap rmap = clog.new RevisionMap(); 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@307: int r = rmap.localRevision(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@263: private 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@263: private 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@285: repository.getManifest().walk(0, 10000, new HgManifest.Inspector2() { tikhomirov@263: public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { tikhomirov@263: return true; tikhomirov@263: } tikhomirov@263: public boolean next(Nodeid nid, String fname, String flags) { tikhomirov@285: throw new HgBadStateException(HgManifest.Inspector2.class.getName()); tikhomirov@285: } tikhomirov@285: public boolean next(Nodeid nid, Path fname, Flags flags) { tikhomirov@263: return true; tikhomirov@263: } tikhomirov@263: public boolean end(int manifestRevision) { tikhomirov@263: return true; tikhomirov@263: } tikhomirov@263: }); 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@263: tikhomirov@263: private void collectTagsPerFile() throws HgException, CancelledException { tikhomirov@254: final long start = System.currentTimeMillis(); tikhomirov@254: final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); tikhomirov@254: final HgTags tags = repository.getTags(); tikhomirov@254: // tikhomirov@254: // build cache tikhomirov@255: // tikhomirov@255: final TagInfo[] allTags = new TagInfo[tags.getTags().size()]; tikhomirov@255: tags.getTags().values().toArray(allTags); 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@255: System.out.printf("Collecting manifests for %d tags\n", allTags.length); tikhomirov@254: // effective translation of changeset revisions to their local indexes tikhomirov@254: final HgChangelog.RevisionMap clogrmap = repository.getChangelog().new RevisionMap().init(); tikhomirov@255: int[] tagLocalRevs = new int[allTags.length]; tikhomirov@259: int x = 0; tikhomirov@255: for (int i = 0; i < allTags.length; i++) { tikhomirov@255: final Nodeid tagRevision = allTags[i].revision(); tikhomirov@259: final int tagLocalRev = clogrmap.localRevision(tagRevision); tikhomirov@259: if (tagLocalRev != HgRepository.BAD_REVISION) { tikhomirov@259: tagLocalRevs[x++] = tagLocalRev; tikhomirov@259: } tikhomirov@259: } tikhomirov@259: if (x != allTags.length) { tikhomirov@259: // some tags were removed (recorded Nodeid.NULL tagname) tikhomirov@259: int[] copy = new int[x]; tikhomirov@259: System.arraycopy(tagLocalRevs, 0, copy, 0, x); tikhomirov@259: tagLocalRevs = copy; tikhomirov@254: } tikhomirov@255: System.out.printf("Prepared tag revisions to analyze: %d ms\n", System.currentTimeMillis() - start); tikhomirov@254: // tikhomirov@285: repository.getManifest().walk(new HgManifest.Inspector2() { 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@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@254: tikhomirov@254: public boolean next(Nodeid nid, String fname, String flags) { tikhomirov@285: throw new HgBadStateException(HgManifest.Inspector2.class.getName()); tikhomirov@285: } 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: final Path targetPath = Path.create("README"); 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@254: for (int localFileRev = 0; localFileRev < fileNode.getRevisionCount(); localFileRev++) { tikhomirov@254: Nodeid fileRev = fileNode.getRevision(localFileRev); tikhomirov@254: int changesetLocalRev = fileNode.getChangesetLocalRevision(localFileRev); 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@254: System.out.printf("%3d%7d%s\n", localFileRev, changesetLocalRev, associatedTags); tikhomirov@254: } tikhomirov@255: System.out.printf("Total time: %d ms\n", System.currentTimeMillis() - start); tikhomirov@255: System.out.printf("Free mem: %,d\n", Runtime.getRuntime().freeMemory()); tikhomirov@254: } tikhomirov@254: tikhomirov@254: public static void main2(String[] args) throws 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@254: final Map tagToInfo = tags.getTags(); 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@254: final Nodeid fileRevision = manifest.getFileRevision(repository.getChangelog().getLocalRevision(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@254: public void next(HgChangeset changeset) { tikhomirov@254: if (changeset.getAffectedFiles().contains(targetPath)) { tikhomirov@254: System.out.println(changeset.getRevision() + " " + changeSetRevisionToTags.get(changeset.getNodeid())); tikhomirov@254: } tikhomirov@254: } tikhomirov@254: }); tikhomirov@254: } tikhomirov@254: }