# HG changeset patch # User Artem Tikhomirov # Date 1322713300 -3600 # Node ID 5f9073eabf06e33a837b2627cac1ae4e61f2738b # Parent 0f3687e79f5a730b0a510354d1e8dccf1ccd0b90 Propagate errors with exceptions up to a end client diff -r 0f3687e79f5a -r 5f9073eabf06 cmdline/org/tmatesoft/hg/console/Bundle.java --- a/cmdline/org/tmatesoft/hg/console/Bundle.java Thu Dec 01 03:05:28 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Bundle.java Thu Dec 01 05:21:40 2011 +0100 @@ -19,6 +19,8 @@ import java.io.File; import java.util.Collections; +import org.tmatesoft.hg.core.HgCallbackTargetException; +import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgBundle; import org.tmatesoft.hg.repo.HgChangelog; @@ -57,12 +59,16 @@ private final HgChangelog changelog = hgRepo.getChangelog(); public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { - if (changelog.isKnown(nodeid)) { - System.out.print("+"); - } else { - System.out.print("-"); + try { + if (changelog.isKnown(nodeid)) { + System.out.print("+"); + } else { + System.out.print("-"); + } + System.out.printf("%d:%s\n%s\n", revisionNumber, nodeid.shortNotation(), cset.toString()); + } catch (HgException ex) { + throw new HgCallbackTargetException.Wrap(ex); } - System.out.printf("%d:%s\n%s\n", revisionNumber, nodeid.shortNotation(), cset.toString()); } }); } diff -r 0f3687e79f5a -r 5f9073eabf06 cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Thu Dec 01 03:05:28 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Thu Dec 01 05:21:40 2011 +0100 @@ -29,10 +29,12 @@ import org.junit.Assert; import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgCatCommand; import org.tmatesoft.hg.core.HgChangeset; import org.tmatesoft.hg.core.HgChangesetTreeHandler; import org.tmatesoft.hg.core.HgDataStreamException; +import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.HgFileInformer; import org.tmatesoft.hg.core.HgFileRevision; import org.tmatesoft.hg.core.HgLogCommand; @@ -117,7 +119,7 @@ HgLogCommand cmd = new HgLogCommand(hgRepo); cmd.file("file1", false); cmd.execute(new HgChangesetTreeHandler() { - public void next(org.tmatesoft.hg.core.HgChangesetTreeHandler.TreeElement entry) { + public void next(HgChangesetTreeHandler.TreeElement entry) { StringBuilder sb = new StringBuilder(); HashSet test = new HashSet(entry.childRevisions()); for (HgChangeset cc : entry.children()) { @@ -508,11 +510,15 @@ System.out.println(p); } public void file(HgFileRevision fileRevision) { - System.out.print(fileRevision.getRevision());; - System.out.print(" "); - System.out.printf("%s %s", fileRevision.getParents().first().shortNotation(), fileRevision.getParents().second().shortNotation()); - System.out.print(" "); - System.out.println(fileRevision.getPath()); + try { + System.out.print(fileRevision.getRevision());; + System.out.print(" "); + System.out.printf("%s %s", fileRevision.getParents().first().shortNotation(), fileRevision.getParents().second().shortNotation()); + System.out.print(" "); + System.out.println(fileRevision.getPath()); + } catch (HgException ex) { + throw new HgCallbackTargetException.Wrap(ex); + } } public void end(Nodeid manifestRevision) { @@ -570,7 +576,7 @@ } - private void testStatusInternals() throws HgDataStreamException { + private void testStatusInternals() throws HgException { HgDataFile n = hgRepo.getFileNode(Path.create("design.txt")); for (String s : new String[] {"011dfd44417c72bd9e54cf89b82828f661b700ed", "e5529faa06d53e06a816e56d218115b42782f1ba", "c18e7111f1fc89a80a00f6a39d51288289a382fc"}) { // expected: 359, 2123, 3079 diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgCatCommand.java --- a/src/org/tmatesoft/hg/core/HgCatCommand.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgCatCommand.java Thu Dec 01 05:21:40 2011 +0100 @@ -136,7 +136,7 @@ * @throws HgDataStreamException * @throws IllegalArgumentException when command arguments are incomplete or wrong */ - public void execute(ByteChannel sink) throws HgDataStreamException, CancelledException { + public void execute(ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException { if (localRevision == BAD_REVISION && revision == null && cset == null) { throw new IllegalArgumentException("File revision, corresponing local number, or a changset nodeid shall be specified"); } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgFileInformer.java --- a/src/org/tmatesoft/hg/core/HgFileInformer.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgFileInformer.java Thu Dec 01 05:21:40 2011 +0100 @@ -36,6 +36,8 @@ * } * * + * FIXME need better name. It's more about manifest of specific changeset, rather than informing (about) files + * * @author Artem Tikhomirov * @author TMate Software Ltd. */ @@ -87,7 +89,7 @@ * @return true if file is known at the selected changeset. * @throws IllegalArgumentException if {@link #changeset(Nodeid)} not specified or file argument is bad. */ - public boolean check(Path file) { // XXX IStatus instead of boolean? + public boolean check(Path file) throws HgInvalidControlFileException { // XXX IStatus instead of boolean? If status, shall it handle exceptions as well? fileRevision = null; checked = false; renamed = false; diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgFileRevision.java --- a/src/org/tmatesoft/hg/core/HgFileRevision.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgFileRevision.java Thu Dec 01 05:21:40 2011 +0100 @@ -84,7 +84,7 @@ * * @return parent revisions of this file revision, with {@link Nodeid#NULL} for missing values. */ - public Pair getParents() { + public Pair getParents() throws HgInvalidControlFileException { if (parents == null) { HgDataFile fn = repo.getFileNode(path); int localRevision = fn.getLocalRevision(revision); @@ -98,7 +98,7 @@ return parents; } - public void putContentTo(ByteChannel sink) throws HgDataStreamException, CancelledException { + public void putContentTo(ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException { HgDataFile fn = repo.getFileNode(path); int localRevision = fn.getLocalRevision(revision); fn.contentWithFilters(localRevision, sink); @@ -116,6 +116,9 @@ } } } catch (HgDataStreamException ex) { + // FIXME rather throw an exception than log silently + HgInternals.getContext(repo).getLog().error(getClass(), ex, null); + } catch (HgInvalidControlFileException ex) { HgInternals.getContext(repo).getLog().error(getClass(), ex, null); } isCopy = Boolean.FALSE; diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgInvalidControlFileException.java --- a/src/org/tmatesoft/hg/core/HgInvalidControlFileException.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgInvalidControlFileException.java Thu Dec 01 05:21:40 2011 +0100 @@ -41,4 +41,10 @@ super.setFile(file); return this; } + + @Override + public HgInvalidControlFileException setRevision(Nodeid r) { + super.setRevision(r); + return this; + } } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgLogCommand.java --- a/src/org/tmatesoft/hg/core/HgLogCommand.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgLogCommand.java Thu Dec 01 05:21:40 2011 +0100 @@ -34,6 +34,7 @@ import org.tmatesoft.hg.repo.HgChangelog; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; import org.tmatesoft.hg.util.CancelSupport; @@ -154,8 +155,10 @@ * * @param nid changeset revision * @return this for convenience + * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog + * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public HgLogCommand changeset(Nodeid nid) { + public HgLogCommand changeset(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException { // XXX perhaps, shall support multiple (...) arguments and extend #execute to handle not only range, but also set of revisions. final int csetLocal = repo.getChangelog().getLocalRevision(nid); return range(csetLocal, csetLocal); @@ -203,7 +206,7 @@ * @throws IllegalArgumentException when inspector argument is null * @throws ConcurrentModificationException if this log command instance is already running */ - public void execute(HgChangesetHandler handler) throws HgDataStreamException, HgCallbackTargetException, CancelledException { + public void execute(HgChangesetHandler handler) throws HgDataStreamException, HgInvalidControlFileException, HgCallbackTargetException, CancelledException { if (handler == null) { throw new IllegalArgumentException(); } @@ -594,7 +597,13 @@ if (cs != null) { return cs.getNodeid(); } else { - return repo.getChangelog().getRevision(changelogRevisionNumber); + try { + return repo.getChangelog().getRevision(changelogRevisionNumber); + } catch (HgException ex) { + HgInternals.getContext(repo).getLog().error(getClass(), ex, null); + // FIXME propagate, perhaps? + return Nodeid.NULL; // FIXME this is quick-n-dirty hack to move forward with introducing exceptions + } } } } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgOutgoingCommand.java --- a/src/org/tmatesoft/hg/core/HgOutgoingCommand.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgOutgoingCommand.java Thu Dec 01 05:21:40 2011 +0100 @@ -109,7 +109,7 @@ * * @param handler delegate to process changes */ - public void executeFull(final HgChangesetHandler handler) throws HgRemoteConnectionException, HgCallbackTargetException, CancelledException { + public void executeFull(final HgChangesetHandler handler) throws HgRemoteConnectionException, HgInvalidControlFileException, HgCallbackTargetException, CancelledException { if (handler == null) { throw new IllegalArgumentException("Delegate can't be null"); } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/core/HgStatus.java --- a/src/org/tmatesoft/hg/core/HgStatus.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgStatus.java Thu Dec 01 05:21:40 2011 +0100 @@ -71,7 +71,7 @@ /** * @return null if author for the change can't be deduced (e.g. for clean files it's senseless) */ - public String getModificationAuthor() { + public String getModificationAuthor() throws HgInvalidControlFileException { RawChangeset cset = logHelper.findLatestChangeWith(path); if (cset == null) { if (kind == Kind.Modified || kind == Kind.Added || kind == Kind.Removed /*&& RightBoundary is TIP*/) { @@ -84,7 +84,7 @@ return null; } - public Date getModificationDate() { + public Date getModificationDate() throws HgInvalidControlFileException { RawChangeset cset = logHelper.findLatestChangeWith(path); if (cset == null) { File localFile = new File(logHelper.getRepo().getWorkingDir(), path.toString()); diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/internal/ChangelogHelper.java --- a/src/org/tmatesoft/hg/internal/ChangelogHelper.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/ChangelogHelper.java Thu Dec 01 05:21:40 2011 +0100 @@ -18,6 +18,7 @@ import java.util.TreeMap; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgInternals; @@ -32,7 +33,7 @@ public class ChangelogHelper { private final int leftBoundary; private final HgRepository repo; - private final TreeMap cache = new TreeMap(); + private final TreeMap cache = new TreeMap(); // FIXME use IntMap instead private String nextCommitAuthor; /** @@ -55,10 +56,9 @@ /** * Walks changelog in reverse order * @param file - * @return changeset where specified file is mentioned among affected files, or - * null if none found up to leftBoundary + * @return changeset where specified file is mentioned among affected files, or null if none found up to leftBoundary */ - public RawChangeset findLatestChangeWith(Path file) { + public RawChangeset findLatestChangeWith(Path file) throws HgInvalidControlFileException { HgDataFile df = repo.getFileNode(file); if (!df.exists()) { return null; diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/internal/KeywordFilter.java --- a/src/org/tmatesoft/hg/internal/KeywordFilter.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/KeywordFilter.java Thu Dec 01 05:21:40 2011 +0100 @@ -18,9 +18,14 @@ import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Date; import java.util.TreeMap; +import org.tmatesoft.hg.core.HgException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; +import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; +import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; @@ -253,20 +258,37 @@ } private String revision() { - // FIXME add cset's nodeid into Changeset class - int csetRev = repo.getFileNode(path).getChangesetLocalRevision(HgRepository.TIP); - return repo.getChangelog().getRevision(csetRev).shortNotation(); + try { + // FIXME add cset's nodeid into Changeset class + int csetRev = repo.getFileNode(path).getChangesetLocalRevision(HgRepository.TIP); + return repo.getChangelog().getRevision(csetRev).shortNotation(); + } catch (HgException ex) { + HgInternals.getContext(repo).getLog().error(getClass(), ex, null); + return Nodeid.NULL.shortNotation(); // XXX perhaps, might return anything better? Not sure how hg approaches this. + } } private String username() { - return getChangeset().user(); + try { + return getChangeset().user(); + } catch (HgException ex) { + HgInternals.getContext(repo).getLog().error(getClass(), ex, null); + return ""; + } } private String date() { - return String.format("%tY/%null */ - public void visitLocalOnlyRevisions(HgChangelog.Inspector inspector) { + public void visitLocalOnlyRevisions(HgChangelog.Inspector inspector) throws HgInvalidControlFileException { if (inspector == null) { throw new IllegalArgumentException(); } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/internal/RevlogStream.java --- a/src/org/tmatesoft/hg/internal/RevlogStream.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/RevlogStream.java Thu Dec 01 05:21:40 2011 +0100 @@ -24,6 +24,7 @@ import java.util.zip.Inflater; import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgInternals; @@ -80,50 +81,43 @@ } /** - * @throws HgBadStateException if internal read operation failed + * @throws HgInvalidControlFileException if attempt to read index file failed + * @throws HgInvalidRevisionException if revisionIndex argument doesn't represent a valid record in the revlog */ - public int dataLength(int revision) { + public int dataLength(int revisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { // XXX in fact, use of iterate() instead of this implementation may be quite reasonable. // - final int indexSize = revisionCount(); + revisionIndex = checkRevisionIndex(revisionIndex); DataAccess daIndex = getIndexStream(); - if (revision == TIP) { - revision = indexSize - 1; - } try { - int recordOffset = getIndexOffsetInt(revision); + int recordOffset = getIndexOffsetInt(revisionIndex); daIndex.seek(recordOffset + 12); // 6+2+4 int actualLen = daIndex.readInt(); return actualLen; } catch (IOException ex) { - ex.printStackTrace(); // log error. FIXME better handling - throw new HgBadStateException(ex); + throw new HgInvalidControlFileException(null, ex, indexFile); } finally { daIndex.done(); } } /** - * @throws HgBadStateException if internal read operation failed + * Read nodeid at given index + * + * @throws HgInvalidControlFileException if attempt to read index file failed + * @throws HgInvalidRevisionException if revisionIndex argument doesn't represent a valid record in the revlog */ - public byte[] nodeid(int revision) throws HgInvalidRevisionException { - final int indexSize = revisionCount(); - if (revision == TIP) { - revision = indexSize - 1; - } - if (revision < 0 || revision >= indexSize) { - throw new HgInvalidRevisionException(revision).setRevisionIndex(revision, 0, indexSize); - } + public byte[] nodeid(int revisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { + revisionIndex = checkRevisionIndex(revisionIndex); DataAccess daIndex = getIndexStream(); try { - int recordOffset = getIndexOffsetInt(revision); + int recordOffset = getIndexOffsetInt(revisionIndex); daIndex.seek(recordOffset + 32); byte[] rv = new byte[20]; daIndex.readBytes(rv, 0, 20); return rv; } catch (IOException ex) { - ex.printStackTrace(); - throw new HgBadStateException(); + throw new HgInvalidControlFileException(null, ex, indexFile); } finally { daIndex.done(); } @@ -131,25 +125,20 @@ /** * Get link field from the index record. - * @throws HgBadStateException if internal read operation failed + * + * @throws HgInvalidControlFileException if attempt to read index file failed + * @throws HgInvalidRevisionException if revisionIndex argument doesn't represent a valid record in the revlog */ - public int linkRevision(int revision) { - final int last = revisionCount() - 1; - if (revision == TIP) { - revision = last; - } - if (revision < 0 || revision > last) { - throw new IllegalArgumentException(Integer.toString(revision)); - } + public int linkRevision(int revisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { + revisionIndex = checkRevisionIndex(revisionIndex); DataAccess daIndex = getIndexStream(); try { - int recordOffset = getIndexOffsetInt(revision); + int recordOffset = getIndexOffsetInt(revisionIndex); daIndex.seek(recordOffset + 20); int linkRev = daIndex.readInt(); return linkRev; } catch (IOException ex) { - ex.printStackTrace(); - throw new HgBadStateException(); + throw new HgInvalidControlFileException(null, ex, indexFile); } finally { daIndex.done(); } @@ -161,8 +150,9 @@ // Unlike its counterpart, {@link Revlog#getLocalRevisionNumber()}, doesn't fail with exception if node not found, /** * @return integer in [0..revisionCount()) or {@link HgRepository#BAD_REVISION} if not found + * @throws HgInvalidControlFileException if attempt to read index file failed */ - public int findLocalRevisionNumber(Nodeid nodeid) { + public int findLocalRevisionNumber(Nodeid nodeid) throws HgInvalidControlFileException { // XXX this one may be implemented with iterate() once there's mechanism to stop iterations final int indexSize = revisionCount(); DataAccess daIndex = getIndexStream(); @@ -179,8 +169,7 @@ daIndex.skip(inline ? 12 + compressedLen : 12); } } catch (IOException ex) { - ex.printStackTrace(); // log error. FIXME better handling. Perhaps, shall return BAD_REVISION here as well? - throw new IllegalStateException(ex); + throw new HgInvalidControlFileException("Failed", ex, indexFile).setRevision(nodeid); } finally { daIndex.done(); } @@ -267,10 +256,23 @@ } /** + * @param revisionIndex shall be valid index, [0..baseRevisions.length-1]. + * It's advised to use {@link #checkRevisionIndex(int)} to ensure argument is correct. * @return offset of the revision's record in the index (.i) stream */ - private int getIndexOffsetInt(int revision) { - return inline ? indexRecordOffset[revision] : revision * REVLOGV1_RECORD_SIZE; + private int getIndexOffsetInt(int revisionIndex) { + return inline ? indexRecordOffset[revisionIndex] : revisionIndex * REVLOGV1_RECORD_SIZE; + } + + private int checkRevisionIndex(int revisionIndex) throws HgInvalidRevisionException { + final int last = revisionCount() - 1; + if (revisionIndex == TIP) { + revisionIndex = last; + } + if (revisionIndex < 0 || revisionIndex > last) { + throw new HgInvalidRevisionException(revisionIndex).setRevisionIndex(revisionIndex, 0, last); + } + return revisionIndex; } private void initOutline() { diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgBranches.java --- a/src/org/tmatesoft/hg/repo/HgBranches.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgBranches.java Thu Dec 01 05:21:40 2011 +0100 @@ -34,6 +34,9 @@ import java.util.TreeMap; import java.util.regex.Pattern; +import org.tmatesoft.hg.core.HgException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; +import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; @@ -103,6 +106,13 @@ } catch (NumberFormatException ex) { repo.getContext().getLog().warn(getClass(), ex, null); // FALL THROUGH + } catch (HgInvalidControlFileException ex) { + // shall not happen, thus log as error + repo.getContext().getLog().error(getClass(), ex, null); + // FALL THROUGH + } catch (HgInvalidRevisionException ex) { + repo.getContext().getLog().error(getClass(), ex, null); + // FALL THROUGH } finally { if (br != null) { try { @@ -229,40 +239,38 @@ /** * Writes down information about repository branches in a format Mercurial native client can understand. * Cache file gets overwritten only if it is out of date (i.e. misses some branch information) + * @throws IOException if write to cache file failed + * @throws HgException subclass of {@link HgException} in case of repository access issue */ @Experimental(reason="Usage of cache isn't supposed to be public knowledge") - public void writeCache() { + public void writeCache() throws IOException, HgException { if (isCacheActual) { return; } - try { - File branchheadsCache = getCacheFile(); - if (!branchheadsCache.exists()) { - branchheadsCache.getParentFile().mkdirs(); // just in case cache/ doesn't exist jet - branchheadsCache.createNewFile(); - } - if (!branchheadsCache.canWrite()) { - return; + File branchheadsCache = getCacheFile(); + if (!branchheadsCache.exists()) { + branchheadsCache.getParentFile().mkdirs(); // just in case cache/ doesn't exist jet + branchheadsCache.createNewFile(); + } + if (!branchheadsCache.canWrite()) { + return; + } + final int lastRev = repo.getChangelog().getLastRevision(); + final Nodeid lastNid = repo.getChangelog().getRevision(lastRev); + BufferedWriter bw = new BufferedWriter(new FileWriter(branchheadsCache)); + bw.write(lastNid.toString()); + bw.write((int) ' '); + bw.write(Integer.toString(lastRev)); + bw.write("\n"); + for (BranchInfo bi : branches.values()) { + for (Nodeid nid : bi.getHeads()) { + bw.write(nid.toString()); + bw.write((int) ' '); + bw.write(bi.getName()); + bw.write("\n"); } - final int lastRev = repo.getChangelog().getLastRevision(); - final Nodeid lastNid = repo.getChangelog().getRevision(lastRev); - BufferedWriter bw = new BufferedWriter(new FileWriter(branchheadsCache)); - bw.write(lastNid.toString()); - bw.write((int) ' '); - bw.write(Integer.toString(lastRev)); - bw.write("\n"); - for (BranchInfo bi : branches.values()) { - for (Nodeid nid : bi.getHeads()) { - bw.write(nid.toString()); - bw.write((int) ' '); - bw.write(bi.getName()); - bw.write("\n"); - } - } - bw.close(); - } catch (IOException ex) { - repo.getContext().getLog().error(getClass(), ex, "Error writing branch cache file"); } + bw.close(); } private File getCacheFile() { diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgBundle.java --- a/src/org/tmatesoft/hg/repo/HgBundle.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgBundle.java Thu Dec 01 05:21:40 2011 +0100 @@ -21,6 +21,7 @@ import java.util.LinkedList; import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.HgInvalidFileException; import org.tmatesoft.hg.core.Nodeid; @@ -93,7 +94,7 @@ * @param hgRepo repository that shall possess base revision for this bundle * @param inspector callback to get each changeset found */ - public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgInvalidFileException { + public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgCallbackTargetException, HgInvalidFileException { Inspector bundleInsp = new Inspector() { DigestHelper dh = new DigestHelper(); boolean emptyChangelog = true; @@ -177,7 +178,11 @@ public void fileEnd(String name) {} }; - inspectChangelog(bundleInsp); + try { + inspectChangelog(bundleInsp); + } catch (RuntimeException ex) { + throw new HgCallbackTargetException(ex); + } } public void dump() throws HgException { diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgChangelog.java --- a/src/org/tmatesoft/hg/repo/HgChangelog.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgChangelog.java Thu Dec 01 05:21:40 2011 +0100 @@ -32,6 +32,8 @@ import java.util.TimeZone; import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; +import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.DataAccess; import org.tmatesoft.hg.internal.IterateControlMediator; @@ -54,18 +56,18 @@ super(hgRepo, content); } - public void all(final HgChangelog.Inspector inspector) { + public void all(final HgChangelog.Inspector inspector) throws HgInvalidRevisionException { range(0, getLastRevision(), inspector); } - public void range(int start, int end, final HgChangelog.Inspector inspector) { + public void range(int start, int end, final HgChangelog.Inspector inspector) throws HgInvalidRevisionException { if (inspector == null) { throw new IllegalArgumentException(); } content.iterate(start, end, true, new RawCsetParser(inspector)); } - public List range(int start, int end) { + public List range(int start, int end) throws HgInvalidRevisionException { final RawCsetCollector c = new RawCsetCollector(end - start + 1); range(start, end, c); return c.result; @@ -77,7 +79,7 @@ * @param inspector callback to get changesets * @param revisions revisions to read, unrestricted ordering. */ - public void range(final HgChangelog.Inspector inspector, final int... revisions) { + public void range(final HgChangelog.Inspector inspector, final int... revisions) throws HgInvalidRevisionException { Arrays.sort(revisions); rangeInternal(inspector, revisions); } @@ -85,7 +87,7 @@ /** * Friends-only version of {@link #range(Inspector, int...)}, when callers know array is sorted */ - /*package-local*/ void rangeInternal(HgChangelog.Inspector inspector, int[] sortedRevisions) { + /*package-local*/ void rangeInternal(HgChangelog.Inspector inspector, int[] sortedRevisions) throws HgInvalidRevisionException { if (sortedRevisions == null || sortedRevisions.length == 0) { return; } @@ -94,8 +96,12 @@ } content.iterate(sortedRevisions, true, new RawCsetParser(inspector)); } - - public RawChangeset changeset(Nodeid nid) { + + /** + * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + */ + public RawChangeset changeset(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException { int x = getLocalRevision(nid); return range(x, x).get(0); } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgDataFile.java --- a/src/org/tmatesoft/hg/repo/HgDataFile.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDataFile.java Thu Dec 01 05:21:40 2011 +0100 @@ -33,6 +33,7 @@ import org.tmatesoft.hg.core.HgDataStreamException; import org.tmatesoft.hg.core.HgException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.HgLogCommand; import org.tmatesoft.hg.core.Nodeid; @@ -86,17 +87,26 @@ } /** + * Handy shorthand for {@link #length(int) length(getLocalRevision(nodeid))} + * + * @param nodeid revision of the file + * * @return size of the file content at the given revision + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog + * @throws HgDataStreamException if attempt to access file metadata failed + * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public int length(Nodeid nodeid) throws HgDataStreamException { + public int length(Nodeid nodeid) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException { return length(getLocalRevision(nodeid)); - } /** * @return size of the file content at the revision identified by local revision number. + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog + * @throws HgDataStreamException if attempt to access file metadata failed + * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public int length(int localRev) throws HgDataStreamException { + public int length(int localRev) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException { if (metadata == null || !metadata.checked(localRev)) { checkAndRecordMetadata(localRev); } @@ -387,16 +397,36 @@ changelog.rangeInternal(inspector, commitRevisions); } - // for a given local revision of the file, find out local revision in the changelog - public int getChangesetLocalRevision(int revision) { + /** + * For a given local revision of the file, find out local revision in the changelog. + * FIXME rename to getChangesetRevisionIndex() + * + * @return changeset revision index + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + */ + public int getChangesetLocalRevision(int revision) throws HgInvalidControlFileException, HgInvalidRevisionException { return content.linkRevision(revision); } - public Nodeid getChangesetRevision(Nodeid nid) { + /** + * Complements {@link #getChangesetLocalRevision(int)} to get changeset revision that corresponds to supplied file revision + * + * @param nid revision of the file + * @return changeset revision + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + */ + public Nodeid getChangesetRevision(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException { int changelogRevision = getChangesetLocalRevision(getLocalRevision(nid)); return getRepo().getChangelog().getRevision(changelogRevision); } + /** + * + * @return + * @throws HgDataStreamException if attempt to access file metadata failed + */ public boolean isCopy() throws HgDataStreamException { if (metadata == null || !metadata.checked(0)) { checkAndRecordMetadata(0); @@ -407,6 +437,13 @@ return metadata.find(0, "copy") != null; } + /** + * Get name of the file this one was copied from. + * + * @return name of the file origin + * @throws HgDataStreamException if attempt to access file metadata failed + * @throws UnsupportedOperationException if this file doesn't represent a copy ({@link #isCopy()} was false) + */ public Path getCopySourceName() throws HgDataStreamException { if (isCopy()) { return Path.create(metadata.find(0, "copy")); diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgManifest.java --- a/src/org/tmatesoft/hg/repo/HgManifest.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgManifest.java Thu Dec 01 05:21:40 2011 +0100 @@ -26,6 +26,7 @@ import java.util.Map; import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.DataAccess; import org.tmatesoft.hg.internal.DigestHelper; @@ -474,7 +475,12 @@ Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest(); // FIXME calculate those missing effectively (e.g. cache and sort nodeids to speed 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); + try { + changelog2manifest[u] = repo.getManifest().getLocalRevision(manifest); + } catch (HgInvalidControlFileException ex) { + // FIXME need to propagate the error up to client + repo.getContext().getLog().error(getClass(), ex, null); + } } } } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgStatusCollector.java --- a/src/org/tmatesoft/hg/repo/HgStatusCollector.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgStatusCollector.java Thu Dec 01 05:21:40 2011 +0100 @@ -29,6 +29,8 @@ import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.HgDataStreamException; +import org.tmatesoft.hg.core.HgException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.IntMap; import org.tmatesoft.hg.internal.ManifestRevision; @@ -268,7 +270,7 @@ } else { inspector.added(copyTarget); } - } catch (HgDataStreamException ex) { + } catch (HgException ex) { ex.printStackTrace(); // FIXME perhaps, shall record this exception to dedicated mediator and continue // for a single file not to be irresolvable obstacle for a status operation @@ -288,7 +290,7 @@ return rv; } - /*package-local*/static Path getOriginIfCopy(HgRepository hgRepo, Path fname, Collection originals, int originalChangelogRevision) throws HgDataStreamException { + /*package-local*/static Path getOriginIfCopy(HgRepository hgRepo, Path fname, Collection originals, int originalChangelogRevision) throws HgDataStreamException, HgInvalidControlFileException { HgDataFile df = hgRepo.getFileNode(fname); if (!df.exists()) { String msg = String.format("Didn't find file '%s' in the repo. Perhaps, bad storage name conversion?", fname); diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgTags.java --- a/src/org/tmatesoft/hg/repo/HgTags.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgTags.java Thu Dec 01 05:21:40 2011 +0100 @@ -29,6 +29,7 @@ import java.util.Map; import java.util.TreeMap; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; /** @@ -238,7 +239,7 @@ return localFromName.containsKey(name); } - public String branch() { + public String branch() throws HgInvalidControlFileException { if (branch == null) { int x = repo.getChangelog().getLocalRevision(revision()); branch = repo.getChangelog().range(x, x).get(0).branch(); diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java --- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java Thu Dec 01 05:21:40 2011 +0100 @@ -31,7 +31,6 @@ import java.util.TreeSet; import org.tmatesoft.hg.core.HgBadStateException; -import org.tmatesoft.hg.core.HgDataStreamException; import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; @@ -123,19 +122,20 @@ } return mr; } - + + private void initDirstateParentManifest() throws HgInvalidControlFileException { + Nodeid dirstateParent = getDirstateImpl().parents().first(); + if (dirstateParent.isNull()) { + dirstateParentManifest = baseRevisionCollector != null ? baseRevisionCollector.raw(-1) : HgStatusCollector.createEmptyManifestRevision(); + } else { + int changelogLocalRev = repo.getChangelog().getLocalRevision(dirstateParent); + dirstateParentManifest = getManifest(changelogLocalRev); + } + } + + // WC not necessarily points to TIP, but may be result of update to any previous revision. + // In such case, we need to compare local files not to their TIP content, but to specific version at the time of selected revision private ManifestRevision getDirstateParentManifest() { - // WC not necessarily points to TIP, but may be result of update to any previous revision. - // In such case, we need to compare local files not to their TIP content, but to specific version at the time of selected revision - if (dirstateParentManifest == null) { - Nodeid dirstateParent = getDirstateImpl().parents().first(); - if (dirstateParent.isNull()) { - dirstateParentManifest = baseRevisionCollector != null ? baseRevisionCollector.raw(-1) : HgStatusCollector.createEmptyManifestRevision(); - } else { - int changelogLocalRev = repo.getChangelog().getLocalRevision(dirstateParent); - dirstateParentManifest = getManifest(changelogLocalRev); - } - } return dirstateParentManifest; } @@ -145,16 +145,19 @@ if (HgInternals.wrongLocalRevision(baseRevision) || baseRevision == BAD_REVISION) { throw new IllegalArgumentException(String.valueOf(baseRevision)); } - if (getDirstateImpl() == null) { - // XXX this is a hack to avoid declaring throws for the #walk() at the moment - // once I decide whether to have mediator that collects errors or to use exceptions here - // this hack shall be removed in favor of either severe error in mediator or a re-thrown exception. - try { - getDirstate(); - } catch (HgInvalidControlFileException ex) { - repo.getContext().getLog().error(getClass(), ex, "Can't read dirstate"); - return; + try { + if (getDirstateImpl() == null) { + // XXX this is a hack to avoid declaring throws for the #walk() at the moment + // once I decide whether to have mediator that collects errors or to use exceptions here + // this hack shall be removed in favor of either severe error in mediator or a re-thrown exception. + getDirstate(); } + if (getDirstateParentManifest() == null) { + initDirstateParentManifest(); + } + } catch (HgInvalidControlFileException ex) { + repo.getContext().getLog().error(getClass(), ex, "Failed to initialize with dirstate information"); + return; } ManifestRevision collect = null; // non null indicates we compare against base revision Set baseRevFiles = Collections.emptySet(); // files from base revision not affected by status calculation @@ -333,7 +336,7 @@ inspector.copied(getPathPool().path(origin), fname); return; } - } catch (HgDataStreamException ex) { + } catch (HgException ex) { ex.printStackTrace(); // FIXME report to a mediator, continue status collection } diff -r 0f3687e79f5a -r 5f9073eabf06 src/org/tmatesoft/hg/repo/Revlog.java --- a/src/org/tmatesoft/hg/repo/Revlog.java Thu Dec 01 03:05:28 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/Revlog.java Thu Dec 01 05:21:40 2011 +0100 @@ -30,6 +30,7 @@ import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.HgException; +import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; @@ -86,8 +87,17 @@ public final int getLastRevision() { return content.revisionCount() - 1; } - - public final Nodeid getRevision(int revision) throws HgInvalidRevisionException { + + /** + * Map revision index to unique revision identifier (nodeid) + * + * @param revision index of the entry in this revlog + * @return revision nodeid of the entry + * + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + */ + public final Nodeid getRevision(int revision) throws HgInvalidControlFileException, HgInvalidRevisionException { // XXX cache nodeids? Rather, if context.getCache(this).getRevisionMap(create == false) != null, use it return Nodeid.fromBinary(content.nodeid(revision), 0); } @@ -123,8 +133,9 @@ * @param nid revision to look up * @return revision local index in this revlog * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog + * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public final int getLocalRevision(Nodeid nid) throws HgInvalidRevisionException { + public final int getLocalRevision(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException { int revision = content.findLocalRevisionNumber(nid); if (revision == BAD_REVISION) { throw new HgInvalidRevisionException(String.format("Bad revision of %s", this /*XXX HgDataFile.getPath might be more suitable here*/), nid, null); @@ -132,15 +143,21 @@ return revision; } - // Till now, i follow approach that NULL nodeid is never part of revlog - public final boolean isKnown(Nodeid nodeid) { + /** + * Note, {@link Nodeid#NULL} nodeid is not reported as known in any revlog. + * + * @param nodeid + * @return + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + */ + public final boolean isKnown(Nodeid nodeid) throws HgInvalidControlFileException { final int rn = content.findLocalRevisionNumber(nodeid); if (BAD_REVISION == rn) { return false; } if (rn < 0 || rn >= content.revisionCount()) { // Sanity check - throw new IllegalStateException(); + throw new HgBadStateException(String.format("Revision index %d found for nodeid %s is not from the range [0..%d]", rn, nodeid.shortNotation(), content.revisionCount()-1)); } return true; } @@ -155,6 +172,7 @@ /** * @param revision - repo-local index of this file change (not a changelog revision number!) + * FIXME is it necessary to have IOException along with HgException here? */ protected void rawContent(int revision, ByteChannel sink) throws HgException, IOException, CancelledException, HgInvalidRevisionException { if (sink == null) { diff -r 0f3687e79f5a -r 5f9073eabf06 test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java --- a/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java Thu Dec 01 03:05:28 2011 +0100 +++ b/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java Thu Dec 01 05:21:40 2011 +0100 @@ -122,7 +122,11 @@ fileNode.walk(0, TIP, new HgDataFile.RevisionInspector() { public void next(int localRevision, Nodeid revision, int linkedRevision) { - changesetToNodeid_3.put(clog.getRevision(linkedRevision), revision); + try { + changesetToNodeid_3.put(clog.getRevision(linkedRevision), revision); + } catch (HgException ex) { + ex.printStackTrace(); + } } }); final long end_3 = System.nanoTime(); @@ -259,7 +263,7 @@ // Approach 1. Build map with all files, their revisions and corresponding tags // - private void collectTagsPerFile_Approach_1(final HgChangelog.RevisionMap clogrmap, final int[] tagLocalRevs, final TagInfo[] allTags, Path targetPath) { + private void collectTagsPerFile_Approach_1(final HgChangelog.RevisionMap clogrmap, final int[] tagLocalRevs, final TagInfo[] allTags, Path targetPath) throws HgException { HgRepository repository = clogrmap.getRepo(); final long start = System.currentTimeMillis(); // file2rev2tag value is array of revisions, always of allTags.length. Revision index in the array diff -r 0f3687e79f5a -r 5f9073eabf06 test/org/tmatesoft/hg/test/TestAuxUtilities.java --- a/test/org/tmatesoft/hg/test/TestAuxUtilities.java Thu Dec 01 03:05:28 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestAuxUtilities.java Thu Dec 01 05:21:40 2011 +0100 @@ -25,6 +25,7 @@ import org.junit.Ignore; import org.junit.Test; import org.tmatesoft.hg.core.HgCatCommand; +import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; import org.tmatesoft.hg.repo.HgChangelog; @@ -234,9 +235,13 @@ int i = 0; public void next(int localRevision, Nodeid revision, int linkedRevision) { - Assert.assertEquals(i++, localRevision); - Assert.assertEquals(fileNode.getChangesetLocalRevision(localRevision), linkedRevision); - Assert.assertEquals(fileNode.getRevision(localRevision), revision); + try { + Assert.assertEquals(i++, localRevision); + Assert.assertEquals(fileNode.getChangesetLocalRevision(localRevision), linkedRevision); + Assert.assertEquals(fileNode.getRevision(localRevision), revision); + } catch (HgException ex) { + Assert.fail(ex.toString()); + } } }); fileNode.walk(0, TIP, new HgDataFile.ParentInspector() {