Mercurial > hg4j
diff src/org/tmatesoft/hg/repo/HgManifest.java @ 218:047b1dec7a04
Issue 7: Correctly handle manifest and changelog with different number of (or non-matching) revisions
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 17 May 2011 03:42:33 +0200 |
parents | e2115da4cf6a |
children | 8de327242aa0 |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgManifest.java Tue May 17 03:40:52 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgManifest.java Tue May 17 03:42:33 2011 +0200 @@ -16,11 +16,16 @@ */ package org.tmatesoft.hg.repo; +import static org.tmatesoft.hg.repo.HgRepository.TIP; + import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.DataAccess; +import org.tmatesoft.hg.internal.Lifecycle; import org.tmatesoft.hg.internal.Pool; import org.tmatesoft.hg.internal.RevlogStream; @@ -31,18 +36,38 @@ * @author TMate Software Ltd. */ public class HgManifest extends Revlog { + private RevisionMapper revisionMap; /*package-local*/ HgManifest(HgRepository hgRepo, RevlogStream content) { super(hgRepo, content); } + /** + * + * @param start changelog (not manifest!) revision to begin with + * @param end changelog (not manifest!) revision to stop, inclusive. + * @param inspector can't be <code>null</code> + */ public void walk(int start, int end, final Inspector inspector) { if (inspector == null) { throw new IllegalArgumentException(); } - content.iterate(start, end, true, new ManifestParser(inspector)); + int start0 = fromChangelog(start); + int end0 = fromChangelog(end); + content.iterate(start0, end0, true, new ManifestParser(inspector)); } - + + /*package-local*/ int fromChangelog(int revisionNumber) { + if (HgInternals.wrongLocalRevision(revisionNumber)) { + throw new IllegalArgumentException(String.valueOf(revisionNumber)); + } + if (revisionMap == null) { + revisionMap = new RevisionMapper(getRepo()); + content.iterate(0, TIP, false, revisionMap); + } + return revisionMap.at(revisionNumber); + } + public interface Inspector { boolean begin(int revision, Nodeid nid); boolean next(Nodeid nid, String fname, String flags); @@ -103,4 +128,68 @@ } } } + + private static class RevisionMapper implements RevlogStream.Inspector, Lifecycle { + + private final int changelogRevisions; + private int[] changelog2manifest; + private final HgRepository repo; + + public RevisionMapper(HgRepository hgRepo) { + repo = hgRepo; + changelogRevisions = repo.getChangelog().getRevisionCount(); + } + + public int at(int revisionNumber) { + if (changelog2manifest != null) { + return changelog2manifest[revisionNumber]; + } + return revisionNumber; + } + + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { + if (changelog2manifest != null) { + changelog2manifest[linkRevision] = revisionNumber; + } else { + if (revisionNumber != linkRevision) { + changelog2manifest = new int[changelogRevisions]; + Arrays.fill(changelog2manifest, -1); + for (int i = 0; i < revisionNumber; changelog2manifest[i] = i, i++) + ; + changelog2manifest[linkRevision] = revisionNumber; + } + } + } + + public void start(int count, Callback callback, Object token) { + if (count != changelogRevisions) { + assert count < changelogRevisions; // no idea what to do if manifest has more revisions than changelog + // the way how manifest may contain more revisions than changelog, as I can imagine, is a result of + // some kind of an import tool (e.g. from SVN or CVS), that creates manifest and changelog independently. + // Note, it's pure guess, I didn't see such repository yet (although the way manifest revisions + // in cpython repo are numbered makes me think aforementioned way) + changelog2manifest = new int[changelogRevisions]; + Arrays.fill(changelog2manifest, -1); + } + } + + public void finish(Object token) { + if (changelog2manifest == null) { + return; + } + // I assume there'd be not too many revisions we don't know manifest of + ArrayList<Integer> undefinedChangelogRevision = new ArrayList<Integer>(); + for (int i = 0; i < changelog2manifest.length; i++) { + if (changelog2manifest[i] == -1) { + undefinedChangelogRevision.add(i); + } + } + for (int u : undefinedChangelogRevision) { + Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest(); + // FIXME calculate those missing effectively (e.g. cache and sort nodeids to spead lookup + // right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here) + changelog2manifest[u] = repo.getManifest().getLocalRevision(manifest); + } + } + } }