Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgManifest.java @ 415:ee8264d80747
Explicit constant for regular file flags, access to flags for a given file revision
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 22 Mar 2012 18:54:11 +0100 |
parents | bb278ccf9866 |
children | ccd7d25e5aea |
comparison
equal
deleted
inserted
replaced
414:bb278ccf9866 | 415:ee8264d80747 |
---|---|
34 import org.tmatesoft.hg.core.Nodeid; | 34 import org.tmatesoft.hg.core.Nodeid; |
35 import org.tmatesoft.hg.internal.DataAccess; | 35 import org.tmatesoft.hg.internal.DataAccess; |
36 import org.tmatesoft.hg.internal.DigestHelper; | 36 import org.tmatesoft.hg.internal.DigestHelper; |
37 import org.tmatesoft.hg.internal.EncodingHelper; | 37 import org.tmatesoft.hg.internal.EncodingHelper; |
38 import org.tmatesoft.hg.internal.Experimental; | 38 import org.tmatesoft.hg.internal.Experimental; |
39 import org.tmatesoft.hg.internal.IntMap; | |
39 import org.tmatesoft.hg.internal.IterateControlMediator; | 40 import org.tmatesoft.hg.internal.IterateControlMediator; |
40 import org.tmatesoft.hg.internal.Lifecycle; | 41 import org.tmatesoft.hg.internal.Lifecycle; |
41 import org.tmatesoft.hg.internal.Pool2; | 42 import org.tmatesoft.hg.internal.Pool2; |
42 import org.tmatesoft.hg.internal.RevlogStream; | 43 import org.tmatesoft.hg.internal.RevlogStream; |
43 import org.tmatesoft.hg.util.CancelSupport; | 44 import org.tmatesoft.hg.util.CancelSupport; |
52 */ | 53 */ |
53 public class HgManifest extends Revlog { | 54 public class HgManifest extends Revlog { |
54 private RevisionMapper revisionMap; | 55 private RevisionMapper revisionMap; |
55 private EncodingHelper encodingHelper; | 56 private EncodingHelper encodingHelper; |
56 | 57 |
58 /** | |
59 * File flags recorded in manifest | |
60 */ | |
57 public enum Flags { | 61 public enum Flags { |
58 Exec, Link; // FIXME REVISIT consider REGULAR instead of null | 62 /** |
63 * Executable bit set | |
64 */ | |
65 Exec, | |
66 /** | |
67 * Symbolic link | |
68 */ | |
69 Link, | |
70 /** | |
71 * Regular file | |
72 */ | |
73 RegularFile; | |
59 | 74 |
60 static Flags parse(String flags) { | 75 static Flags parse(String flags) { |
61 if ("x".equalsIgnoreCase(flags)) { | 76 if ("x".equalsIgnoreCase(flags)) { |
62 return Exec; | 77 return Exec; |
63 } | 78 } |
64 if ("l".equalsIgnoreCase(flags)) { | 79 if ("l".equalsIgnoreCase(flags)) { |
65 return Link; | 80 return Link; |
66 } | 81 } |
67 if (flags == null) { | 82 if (flags == null) { |
68 return null; | 83 return RegularFile; |
69 } | 84 } |
70 throw new IllegalStateException(flags); | 85 throw new IllegalStateException(flags); |
71 } | 86 } |
72 | 87 |
73 static Flags parse(byte[] data, int start, int length) { | 88 static Flags parse(byte[] data, int start, int length) { |
74 if (length == 0) { | 89 if (length == 0) { |
75 return null; | 90 return RegularFile; |
76 } | 91 } |
77 if (length == 1) { | 92 if (length == 1) { |
78 if (data[start] == 'x') { | 93 if (data[start] == 'x') { |
79 return Exec; | 94 return Exec; |
80 } | 95 } |
90 if (this == Exec) { | 105 if (this == Exec) { |
91 return "x"; | 106 return "x"; |
92 } | 107 } |
93 if (this == Link) { | 108 if (this == Link) { |
94 return "l"; | 109 return "l"; |
110 } | |
111 if (this == RegularFile) { | |
112 return ""; | |
95 } | 113 } |
96 throw new IllegalStateException(toString()); | 114 throw new IllegalStateException(toString()); |
97 } | 115 } |
98 } | 116 } |
99 | 117 |
240 // XXX package-local, IntMap, and HgDataFile getFileRevisionAt(int... localChangelogRevisions) | 258 // XXX package-local, IntMap, and HgDataFile getFileRevisionAt(int... localChangelogRevisions) |
241 @Experimental(reason="@see #getFileRevision") | 259 @Experimental(reason="@see #getFileRevision") |
242 public Map<Integer, Nodeid> getFileRevisions(final Path file, int... changelogRevisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException { | 260 public Map<Integer, Nodeid> getFileRevisions(final Path file, int... changelogRevisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException { |
243 // TODO need tests | 261 // TODO need tests |
244 int[] manifestRevisionIndexes = toManifestRevisionIndexes(changelogRevisionIndexes, null); | 262 int[] manifestRevisionIndexes = toManifestRevisionIndexes(changelogRevisionIndexes, null); |
245 final HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(changelogRevisionIndexes.length); | 263 IntMap<Nodeid> resMap = new IntMap<Nodeid>(changelogRevisionIndexes.length); |
246 content.iterate(manifestRevisionIndexes, true, new RevlogStream.Inspector() { | 264 content.iterate(manifestRevisionIndexes, true, new FileLookupInspector(encodingHelper, file, resMap, null)); |
247 | 265 // IntMap to HashMap, |
248 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException { | 266 HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(); |
249 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 267 resMap.fill(rv); |
250 try { | |
251 byte b; | |
252 while (!data.isEmpty() && (b = data.readByte()) != '\n') { | |
253 if (b != 0) { | |
254 bos.write(b); | |
255 } else { | |
256 String fname = new String(bos.toByteArray()); | |
257 bos.reset(); | |
258 if (file.toString().equals(fname)) { | |
259 byte[] nid = new byte[40]; | |
260 data.readBytes(nid, 0, 40); | |
261 rv.put(linkRevision, Nodeid.fromAscii(nid, 0, 40)); | |
262 break; | |
263 } else { | |
264 data.skip(40); | |
265 } | |
266 // else skip to the end of line | |
267 while (!data.isEmpty() && (b = data.readByte()) != '\n') | |
268 ; | |
269 } | |
270 } | |
271 } catch (IOException ex) { | |
272 throw new HgException(ex); | |
273 } | |
274 } | |
275 }); | |
276 return rv; | 268 return rv; |
269 } | |
270 | |
271 /** | |
272 * {@link HgDataFile#getFlags(int)} is public API | |
273 * | |
274 * @param changesetRevIndex changeset revision index | |
275 * @param file path to look up | |
276 * @return one of predefined enum values, or null if file was not known in the specified revision | |
277 * FIXME EXCEPTIONS | |
278 * @throws HgInvalidControlFileException | |
279 * @throws HgInvalidRevisionException | |
280 */ | |
281 /*package-local*/ Flags extractFlags(int changesetRevIndex, Path file) throws HgInvalidRevisionException, HgInvalidControlFileException { | |
282 int manifestRevIdx = fromChangelog(changesetRevIndex); | |
283 IntMap<Flags> resMap = new IntMap<Flags>(2); | |
284 content.iterate(manifestRevIdx, manifestRevIdx, true, new FileLookupInspector(encodingHelper, file, null, resMap)); | |
285 return resMap.get(changesetRevIndex); | |
277 } | 286 } |
278 | 287 |
279 | 288 |
280 /** | 289 /** |
281 * @param changelogRevisionIndexes non-null | 290 * @param changelogRevisionIndexes non-null |
327 boolean end(int manifestRevision); | 336 boolean end(int manifestRevision); |
328 } | 337 } |
329 | 338 |
330 @Experimental(reason="Explore Path alternative for filenames and enum for flags") | 339 @Experimental(reason="Explore Path alternative for filenames and enum for flags") |
331 public interface Inspector2 extends Inspector { | 340 public interface Inspector2 extends Inspector { |
341 /** | |
342 * @param nid file revision | |
343 * @param fname file name | |
344 * @param flags one of {@link HgManifest.Flags} constants, not <code>null</code> | |
345 * @return <code>true</code> to continue iteration, <code>false</code> to stop | |
346 */ | |
332 boolean next(Nodeid nid, Path fname, Flags flags); | 347 boolean next(Nodeid nid, Path fname, Flags flags); |
333 } | 348 } |
334 | 349 |
335 /** | 350 /** |
336 * When Pool uses Strings directly, | 351 * When Pool uses Strings directly, |
465 // 'x' and 'l' for executable bits and symlinks? | 480 // 'x' and 'l' for executable bits and symlinks? |
466 // hg --debug manifest shows 644 for each regular file in my repo | 481 // hg --debug manifest shows 644 for each regular file in my repo |
467 // for cpython 0..10k, there are 4361062 flag checks, and there's only 1 unique flag | 482 // for cpython 0..10k, there are 4361062 flag checks, and there's only 1 unique flag |
468 flags = Flags.parse(data, x + nodeidLen, i-x-nodeidLen); | 483 flags = Flags.parse(data, x + nodeidLen, i-x-nodeidLen); |
469 } else { | 484 } else { |
470 flags = null; | 485 flags = Flags.RegularFile; |
471 } | 486 } |
472 boolean good2go; | 487 boolean good2go; |
473 if (inspector2 == null) { | 488 if (inspector2 == null) { |
474 String flagString = flags == null ? null : flags.nativeString(); | 489 String flagString = flags == Flags.RegularFile ? null : flags.nativeString(); |
475 good2go = inspector.next(nid, fname.toString(), flagString); | 490 good2go = inspector.next(nid, fname.toString(), flagString); |
476 } else { | 491 } else { |
477 good2go = inspector2.next(nid, fname, flags); | 492 good2go = inspector2.next(nid, fname, flags); |
478 } | 493 } |
479 if (!good2go) { | 494 if (!good2go) { |
604 repo.getContext().getLog().error(getClass(), ex, null); | 619 repo.getContext().getLog().error(getClass(), ex, null); |
605 } | 620 } |
606 } | 621 } |
607 } | 622 } |
608 } | 623 } |
624 | |
625 /** | |
626 * Look up specified file in possibly multiple manifest revisions, collect file revision and flags. | |
627 */ | |
628 private static class FileLookupInspector implements RevlogStream.Inspector { | |
629 | |
630 private final byte[] filenameAsBytes; | |
631 private final IntMap<Nodeid> csetIndex2FileRev; | |
632 private final IntMap<Flags> csetIndex2Flags; | |
633 | |
634 public FileLookupInspector(EncodingHelper eh, Path fileToLookUp, IntMap<Nodeid> csetIndex2FileRevMap, IntMap<Flags> csetIndex2FlagsMap) { | |
635 assert fileToLookUp != null; | |
636 // need at least one map for the inspector to make any sense | |
637 assert csetIndex2FileRevMap != null || csetIndex2FlagsMap != null; | |
638 csetIndex2FileRev = csetIndex2FileRevMap; | |
639 csetIndex2Flags = csetIndex2FlagsMap; | |
640 filenameAsBytes = eh.toManifest(fileToLookUp.toString()); | |
641 } | |
642 | |
643 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException { | |
644 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |
645 try { | |
646 byte b; | |
647 while (!data.isEmpty() && (b = data.readByte()) != '\n') { | |
648 if (b != 0) { | |
649 bos.write(b); | |
650 } else { | |
651 byte[] byteArray = bos.toByteArray(); | |
652 bos.reset(); | |
653 if (Arrays.equals(filenameAsBytes, byteArray)) { | |
654 if (csetIndex2FileRev != null) { | |
655 byte[] nid = new byte[40]; | |
656 data.readBytes(nid, 0, 40); | |
657 csetIndex2FileRev.put(linkRevision, Nodeid.fromAscii(nid, 0, 40)); | |
658 } else { | |
659 data.skip(40); | |
660 } | |
661 if (csetIndex2Flags != null) { | |
662 while (!data.isEmpty() && (b = data.readByte()) != '\n') { | |
663 bos.write(b); | |
664 } | |
665 Flags flags; | |
666 if (bos.size() == 0) { | |
667 flags = Flags.RegularFile; | |
668 } else { | |
669 flags = Flags.parse(bos.toByteArray(), 0, bos.size()); | |
670 } | |
671 csetIndex2Flags.put(linkRevision, flags); | |
672 } | |
673 break; | |
674 } else { | |
675 data.skip(40); | |
676 } | |
677 // else skip to the end of line | |
678 while (!data.isEmpty() && (b = data.readByte()) != '\n') | |
679 ; | |
680 } | |
681 } | |
682 } catch (IOException ex) { | |
683 throw new HgException(ex); // FIXME EXCEPTIONS | |
684 } | |
685 } | |
686 } | |
609 } | 687 } |