Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgManifest.java @ 326:d42a45a2c9d6
Alternative tag collection approach for a file history
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Tue, 04 Oct 2011 06:28:01 +0200 |
| parents | 283b294d1079 |
| children | 5f9073eabf06 |
comparison
equal
deleted
inserted
replaced
| 325:f05c8b1f08c4 | 326:d42a45a2c9d6 |
|---|---|
| 20 | 20 |
| 21 import java.io.ByteArrayOutputStream; | 21 import java.io.ByteArrayOutputStream; |
| 22 import java.io.IOException; | 22 import java.io.IOException; |
| 23 import java.util.ArrayList; | 23 import java.util.ArrayList; |
| 24 import java.util.Arrays; | 24 import java.util.Arrays; |
| 25 import java.util.HashMap; | |
| 26 import java.util.Map; | |
| 25 | 27 |
| 26 import org.tmatesoft.hg.core.HgBadStateException; | 28 import org.tmatesoft.hg.core.HgBadStateException; |
| 27 import org.tmatesoft.hg.core.Nodeid; | 29 import org.tmatesoft.hg.core.Nodeid; |
| 28 import org.tmatesoft.hg.internal.DataAccess; | 30 import org.tmatesoft.hg.internal.DataAccess; |
| 29 import org.tmatesoft.hg.internal.DigestHelper; | 31 import org.tmatesoft.hg.internal.DigestHelper; |
| 122 */ | 124 */ |
| 123 public void walk(final Inspector inspector, int... localRevisions) { | 125 public void walk(final Inspector inspector, int... localRevisions) { |
| 124 if (inspector == null || localRevisions == null) { | 126 if (inspector == null || localRevisions == null) { |
| 125 throw new IllegalArgumentException(); | 127 throw new IllegalArgumentException(); |
| 126 } | 128 } |
| 127 int[] manifestLocalRevs = new int[localRevisions.length]; | 129 int[] localManifestRevs = toLocalManifestRevisions(localRevisions); |
| 128 boolean needsSort = false; | 130 content.iterate(localManifestRevs, true, new ManifestParser(inspector)); |
| 129 for (int i = 0; i < localRevisions.length; i++) { | |
| 130 final int manifestLocalRev = fromChangelog(localRevisions[i]); | |
| 131 manifestLocalRevs[i] = manifestLocalRev; | |
| 132 if (i > 0 && manifestLocalRevs[i-1] > manifestLocalRev) { | |
| 133 needsSort = true; | |
| 134 } | |
| 135 } | |
| 136 if (needsSort) { | |
| 137 Arrays.sort(manifestLocalRevs); | |
| 138 } | |
| 139 content.iterate(manifestLocalRevs, true, new ManifestParser(inspector)); | |
| 140 } | 131 } |
| 141 | 132 |
| 142 // manifest revision number that corresponds to the given changeset | 133 // manifest revision number that corresponds to the given changeset |
| 143 /*package-local*/ int fromChangelog(int revisionNumber) { | 134 /*package-local*/ int fromChangelog(int revisionNumber) { |
| 144 if (HgInternals.wrongLocalRevision(revisionNumber)) { | 135 if (HgInternals.wrongLocalRevision(revisionNumber)) { |
| 156 } | 147 } |
| 157 | 148 |
| 158 /** | 149 /** |
| 159 * Extracts file revision as it was known at the time of given changeset. | 150 * Extracts file revision as it was known at the time of given changeset. |
| 160 * | 151 * |
| 161 * @param revisionNumber local changeset index | 152 * @param localChangelogRevision local changeset index |
| 162 * @param file path to file in question | 153 * @param file path to file in question |
| 163 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file | 154 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file |
| 164 */ | 155 */ |
| 165 @Experimental(reason="Perhaps, HgDataFile shall own this method") | 156 @Experimental(reason="Perhaps, HgDataFile shall own this method, or get a delegate?") |
| 166 public Nodeid getFileRevision(int revisionNumber, final Path file) { | 157 public Nodeid getFileRevision(int localChangelogRevision, final Path file) { |
| 167 int rev = fromChangelog(revisionNumber); | 158 return getFileRevisions(file, localChangelogRevision).get(localChangelogRevision); |
| 168 final Nodeid[] rv = new Nodeid[] { null }; | 159 } |
| 169 content.iterate(rev, rev, true, new RevlogStream.Inspector() { | 160 |
| 161 // XXX package-local, IntMap, and HgDataFile getFileRevisionAt(int... localChangelogRevisions) | |
| 162 @Experimental(reason="@see #getFileRevision") | |
| 163 public Map<Integer, Nodeid> getFileRevisions(final Path file, int... localChangelogRevisions) { | |
| 164 // FIXME need tests | |
| 165 int[] localManifestRevisions = toLocalManifestRevisions(localChangelogRevisions); | |
| 166 final HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(localChangelogRevisions.length); | |
| 167 content.iterate(localManifestRevisions, true, new RevlogStream.Inspector() { | |
| 170 | 168 |
| 171 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 169 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { |
| 172 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 170 ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| 173 try { | 171 try { |
| 174 byte b; | 172 byte b; |
| 179 String fname = new String(bos.toByteArray()); | 177 String fname = new String(bos.toByteArray()); |
| 180 bos.reset(); | 178 bos.reset(); |
| 181 if (file.toString().equals(fname)) { | 179 if (file.toString().equals(fname)) { |
| 182 byte[] nid = new byte[40]; | 180 byte[] nid = new byte[40]; |
| 183 data.readBytes(nid, 0, 40); | 181 data.readBytes(nid, 0, 40); |
| 184 rv[0] = Nodeid.fromAscii(nid, 0, 40); | 182 rv.put(linkRevision, Nodeid.fromAscii(nid, 0, 40)); |
| 185 break; | 183 break; |
| 184 } else { | |
| 185 data.skip(40); | |
| 186 } | 186 } |
| 187 // else skip to the end of line | 187 // else skip to the end of line |
| 188 while (!data.isEmpty() && (b = data.readByte()) != '\n') | 188 while (!data.isEmpty() && (b = data.readByte()) != '\n') |
| 189 ; | 189 ; |
| 190 } | 190 } |
| 192 } catch (IOException ex) { | 192 } catch (IOException ex) { |
| 193 throw new HgBadStateException(ex); | 193 throw new HgBadStateException(ex); |
| 194 } | 194 } |
| 195 } | 195 } |
| 196 }); | 196 }); |
| 197 return rv[0]; | 197 return rv; |
| 198 } | 198 } |
| 199 | 199 |
| 200 | |
| 201 private int[] toLocalManifestRevisions(int[] localChangelogRevisions) { | |
| 202 int[] localManifestRevs = new int[localChangelogRevisions.length]; | |
| 203 boolean needsSort = false; | |
| 204 for (int i = 0; i < localChangelogRevisions.length; i++) { | |
| 205 final int manifestLocalRev = fromChangelog(localChangelogRevisions[i]); | |
| 206 localManifestRevs[i] = manifestLocalRev; | |
| 207 if (i > 0 && localManifestRevs[i-1] > manifestLocalRev) { | |
| 208 needsSort = true; | |
| 209 } | |
| 210 } | |
| 211 if (needsSort) { | |
| 212 Arrays.sort(localManifestRevs); | |
| 213 } | |
| 214 return localManifestRevs; | |
| 215 } | |
| 216 | |
| 200 public interface Inspector { | 217 public interface Inspector { |
| 201 boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision); | 218 boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision); |
| 202 /** | 219 /** |
| 203 * @deprecated switch to {@link Inspector2#next(Nodeid, Path, Flags)} | 220 * @deprecated switch to {@link Inspector2#next(Nodeid, Path, Flags)} |
| 204 */ | 221 */ |
| 209 | 226 |
| 210 @Experimental(reason="Explore Path alternative for filenames and enum for flags") | 227 @Experimental(reason="Explore Path alternative for filenames and enum for flags") |
| 211 public interface Inspector2 extends Inspector { | 228 public interface Inspector2 extends Inspector { |
| 212 boolean next(Nodeid nid, Path fname, Flags flags); | 229 boolean next(Nodeid nid, Path fname, Flags flags); |
| 213 } | 230 } |
| 214 | 231 |
| 215 /** | 232 /** |
| 216 * When Pool uses Strings directly, | 233 * When Pool uses Strings directly, |
| 217 * ManifestParser creates new String instance with new char[] value, and does byte->char conversion. | 234 * ManifestParser creates new String instance with new char[] value, and does byte->char conversion. |
| 218 * For cpython repo, walk(0..10k), there are over 16 million filenames, of them only 3020 unique. | 235 * For cpython repo, walk(0..10k), there are over 16 million filenames, of them only 3020 unique. |
| 219 * This means there are 15.9 million useless char[] instances and byte->char conversions | 236 * This means there are 15.9 million useless char[] instances and byte->char conversions |
