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