view test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java @ 254:a620f0663a37

Collect tags for a file - improve performance of 'sparse' manifest reads
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 16 Aug 2011 04:03:29 +0200
parents
children 5a6ab50b4cbf
line wrap: on
line source
package org.tmatesoft.hg.test;

import java.io.*;
import java.util.*;
import java.util.Map.Entry;

import org.tmatesoft.hg.core.*;
import org.tmatesoft.hg.internal.Pool;
import org.tmatesoft.hg.repo.*;
import org.tmatesoft.hg.repo.HgTags.TagInfo;
import org.tmatesoft.hg.util.*;

/**
 * @author Marc Strapetz
 */
public class MapTagsToFileRevisions {

	// Static =================================================================

	public static void main(String[] args) throws HgException, CancelledException {
		final long start = System.currentTimeMillis();
		final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython"));
		final HgTags tags = repository.getTags();
		//
		// build cache
		final Map<String, Map<TagInfo, Nodeid>> file2tag2rev = new HashMap<String, Map<TagInfo, Nodeid>>();
		System.out.printf("Collecting manifests for %d tags\n", tags.getTags().size());
		// effective translation of changeset revisions to their local indexes
		final HgChangelog.RevisionMap clogrmap = repository.getChangelog().new RevisionMap().init();
		int[] tagLocalRevs = new int[tags.getTags().size()];
		int i = 0;
		for (TagInfo tag : tags.getTags().values()) {
			final Nodeid tagRevision = tag.revision();
			int tagLocalRev = clogrmap.localRevision(tagRevision);
			tagLocalRevs[i++] = tagLocalRev;
		}
		System.out.printf("Found tag revisions to analyze: %d\n", System.currentTimeMillis() - start);
		//
		repository.getManifest().walk(new HgManifest.Inspector() {
			private List<String> tagsAtRev;
			final Pool<String> filenamePool = new Pool<String>();
			final Pool<Nodeid> nodeidPool = new Pool<Nodeid>();

			public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) {
				Nodeid cset = clogrmap.revision(changelogRevision);
				tagsAtRev = tags.tags(cset);
				if (tagsAtRev.isEmpty()) {
					System.out.println("Can't happen, provided we iterate over revisions with tags only");
				}
				return true;
			}
			
			public boolean next(Nodeid nid, String fname, String flags) {
				fname = filenamePool.unify(fname);
				nid = nodeidPool.unify(nid);
				Map<TagInfo, Nodeid> m = file2tag2rev.get(fname);
				if (m == null) {
					file2tag2rev.put(fname, m = new HashMap<TagInfo, Nodeid>());
				}
				for (String tag : tagsAtRev) {
					m.put(tags.getTags().get(tag), nid);
				}
				return true;
			}
			
			public boolean end(int manifestRevision) {
				return true;
			}
			
		}, tagLocalRevs);
		System.out.printf("Cache built: %d\n", System.currentTimeMillis() - start);
		//
		// look up specific file. This part is fast.
		final Path targetPath = Path.create("README");
		HgDataFile fileNode = repository.getFileNode(targetPath);
		// TODO if fileNode.isCopy, repeat for each getCopySourceName()
		for (int localFileRev = 0; localFileRev < fileNode.getRevisionCount(); localFileRev++) {
			Nodeid fileRev = fileNode.getRevision(localFileRev);
			int changesetLocalRev = fileNode.getChangesetLocalRevision(localFileRev);
			List<String> associatedTags = new LinkedList<String>();
			final Map<TagInfo, Nodeid> allTagsOfTheFile = file2tag2rev.get(targetPath.toString());
			for (Entry<TagInfo, Nodeid> e : allTagsOfTheFile.entrySet()) {
				Nodeid fileRevAtTag = e.getValue();
				if (fileRev.equals(fileRevAtTag)) {
					associatedTags.add(e.getKey().name());
				}
			}
			System.out.printf("%3d%7d%s\n", localFileRev, changesetLocalRev, associatedTags);
		}
		System.out.printf("Total time: %d", System.currentTimeMillis() - start);
	}

	public static void main2(String[] args) throws HgException, CancelledException {
		final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython"));
		final Path targetPath = Path.create("README");
		final HgTags tags = repository.getTags();
		final Map<String, HgTags.TagInfo> tagToInfo = tags.getTags();
		final HgManifest manifest = repository.getManifest();
		final Map<Nodeid, List<String>> changeSetRevisionToTags = new HashMap<Nodeid, List<String>>();
		final HgDataFile fileNode = repository.getFileNode(targetPath);
		for (String tagName : tagToInfo.keySet()) {
			final HgTags.TagInfo info = tagToInfo.get(tagName);
			final Nodeid nodeId = info.revision();
			// 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.)
			final Nodeid fileRevision = manifest.getFileRevision(repository.getChangelog().getLocalRevision(nodeId), targetPath);
			if (fileRevision == null) {
				continue;
			}

			final Nodeid changeSetRevision = fileNode.getChangesetRevision(fileRevision);
			List<String> revisionTags = changeSetRevisionToTags.get(changeSetRevision);
			if (revisionTags == null) {
				revisionTags = new ArrayList<String>();
				changeSetRevisionToTags.put(changeSetRevision, revisionTags);
			}
			revisionTags.add(tagName);
		}

		final HgLogCommand logCommand = new HgLogCommand(repository);
		logCommand.file(targetPath, true);
		logCommand.execute(new HgChangesetHandler() {
			public void next(HgChangeset changeset) {
				if (changeset.getAffectedFiles().contains(targetPath)) {
					System.out.println(changeset.getRevision() + " " + changeSetRevisionToTags.get(changeset.getNodeid()));
				}
			}
		});
	}
}