Mercurial > hg4j
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 217:e39cf474ef94 | 218:047b1dec7a04 |
|---|---|
| 14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License |
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.repo.HgRepository.TIP; | |
| 20 | |
| 19 import java.io.IOException; | 21 import java.io.IOException; |
| 22 import java.util.ArrayList; | |
| 23 import java.util.Arrays; | |
| 20 | 24 |
| 21 import org.tmatesoft.hg.core.HgBadStateException; | 25 import org.tmatesoft.hg.core.HgBadStateException; |
| 22 import org.tmatesoft.hg.core.Nodeid; | 26 import org.tmatesoft.hg.core.Nodeid; |
| 23 import org.tmatesoft.hg.internal.DataAccess; | 27 import org.tmatesoft.hg.internal.DataAccess; |
| 28 import org.tmatesoft.hg.internal.Lifecycle; | |
| 24 import org.tmatesoft.hg.internal.Pool; | 29 import org.tmatesoft.hg.internal.Pool; |
| 25 import org.tmatesoft.hg.internal.RevlogStream; | 30 import org.tmatesoft.hg.internal.RevlogStream; |
| 26 | 31 |
| 27 | 32 |
| 28 /** | 33 /** |
| 29 * | 34 * |
| 30 * @author Artem Tikhomirov | 35 * @author Artem Tikhomirov |
| 31 * @author TMate Software Ltd. | 36 * @author TMate Software Ltd. |
| 32 */ | 37 */ |
| 33 public class HgManifest extends Revlog { | 38 public class HgManifest extends Revlog { |
| 39 private RevisionMapper revisionMap; | |
| 34 | 40 |
| 35 /*package-local*/ HgManifest(HgRepository hgRepo, RevlogStream content) { | 41 /*package-local*/ HgManifest(HgRepository hgRepo, RevlogStream content) { |
| 36 super(hgRepo, content); | 42 super(hgRepo, content); |
| 37 } | 43 } |
| 38 | 44 |
| 45 /** | |
| 46 * | |
| 47 * @param start changelog (not manifest!) revision to begin with | |
| 48 * @param end changelog (not manifest!) revision to stop, inclusive. | |
| 49 * @param inspector can't be <code>null</code> | |
| 50 */ | |
| 39 public void walk(int start, int end, final Inspector inspector) { | 51 public void walk(int start, int end, final Inspector inspector) { |
| 40 if (inspector == null) { | 52 if (inspector == null) { |
| 41 throw new IllegalArgumentException(); | 53 throw new IllegalArgumentException(); |
| 42 } | 54 } |
| 43 content.iterate(start, end, true, new ManifestParser(inspector)); | 55 int start0 = fromChangelog(start); |
| 56 int end0 = fromChangelog(end); | |
| 57 content.iterate(start0, end0, true, new ManifestParser(inspector)); | |
| 44 } | 58 } |
| 45 | 59 |
| 60 /*package-local*/ int fromChangelog(int revisionNumber) { | |
| 61 if (HgInternals.wrongLocalRevision(revisionNumber)) { | |
| 62 throw new IllegalArgumentException(String.valueOf(revisionNumber)); | |
| 63 } | |
| 64 if (revisionMap == null) { | |
| 65 revisionMap = new RevisionMapper(getRepo()); | |
| 66 content.iterate(0, TIP, false, revisionMap); | |
| 67 } | |
| 68 return revisionMap.at(revisionNumber); | |
| 69 } | |
| 70 | |
| 46 public interface Inspector { | 71 public interface Inspector { |
| 47 boolean begin(int revision, Nodeid nid); | 72 boolean begin(int revision, Nodeid nid); |
| 48 boolean next(Nodeid nid, String fname, String flags); | 73 boolean next(Nodeid nid, String fname, String flags); |
| 49 boolean end(int revision); | 74 boolean end(int revision); |
| 50 } | 75 } |
| 101 } catch (IOException ex) { | 126 } catch (IOException ex) { |
| 102 throw new HgBadStateException(ex); | 127 throw new HgBadStateException(ex); |
| 103 } | 128 } |
| 104 } | 129 } |
| 105 } | 130 } |
| 131 | |
| 132 private static class RevisionMapper implements RevlogStream.Inspector, Lifecycle { | |
| 133 | |
| 134 private final int changelogRevisions; | |
| 135 private int[] changelog2manifest; | |
| 136 private final HgRepository repo; | |
| 137 | |
| 138 public RevisionMapper(HgRepository hgRepo) { | |
| 139 repo = hgRepo; | |
| 140 changelogRevisions = repo.getChangelog().getRevisionCount(); | |
| 141 } | |
| 142 | |
| 143 public int at(int revisionNumber) { | |
| 144 if (changelog2manifest != null) { | |
| 145 return changelog2manifest[revisionNumber]; | |
| 146 } | |
| 147 return revisionNumber; | |
| 148 } | |
| 149 | |
| 150 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | |
| 151 if (changelog2manifest != null) { | |
| 152 changelog2manifest[linkRevision] = revisionNumber; | |
| 153 } else { | |
| 154 if (revisionNumber != linkRevision) { | |
| 155 changelog2manifest = new int[changelogRevisions]; | |
| 156 Arrays.fill(changelog2manifest, -1); | |
| 157 for (int i = 0; i < revisionNumber; changelog2manifest[i] = i, i++) | |
| 158 ; | |
| 159 changelog2manifest[linkRevision] = revisionNumber; | |
| 160 } | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 public void start(int count, Callback callback, Object token) { | |
| 165 if (count != changelogRevisions) { | |
| 166 assert count < changelogRevisions; // no idea what to do if manifest has more revisions than changelog | |
| 167 // the way how manifest may contain more revisions than changelog, as I can imagine, is a result of | |
| 168 // some kind of an import tool (e.g. from SVN or CVS), that creates manifest and changelog independently. | |
| 169 // Note, it's pure guess, I didn't see such repository yet (although the way manifest revisions | |
| 170 // in cpython repo are numbered makes me think aforementioned way) | |
| 171 changelog2manifest = new int[changelogRevisions]; | |
| 172 Arrays.fill(changelog2manifest, -1); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 public void finish(Object token) { | |
| 177 if (changelog2manifest == null) { | |
| 178 return; | |
| 179 } | |
| 180 // I assume there'd be not too many revisions we don't know manifest of | |
| 181 ArrayList<Integer> undefinedChangelogRevision = new ArrayList<Integer>(); | |
| 182 for (int i = 0; i < changelog2manifest.length; i++) { | |
| 183 if (changelog2manifest[i] == -1) { | |
| 184 undefinedChangelogRevision.add(i); | |
| 185 } | |
| 186 } | |
| 187 for (int u : undefinedChangelogRevision) { | |
| 188 Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest(); | |
| 189 // FIXME calculate those missing effectively (e.g. cache and sort nodeids to spead lookup | |
| 190 // right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here) | |
| 191 changelog2manifest[u] = repo.getManifest().getLocalRevision(manifest); | |
| 192 } | |
| 193 } | |
| 194 } | |
| 106 } | 195 } |
