changeset 354:5f9073eabf06

Propagate errors with exceptions up to a end client
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 01 Dec 2011 05:21:40 +0100
parents 0f3687e79f5a
children f2c11fe7f3e9
files cmdline/org/tmatesoft/hg/console/Bundle.java cmdline/org/tmatesoft/hg/console/Main.java src/org/tmatesoft/hg/core/HgCatCommand.java src/org/tmatesoft/hg/core/HgFileInformer.java src/org/tmatesoft/hg/core/HgFileRevision.java src/org/tmatesoft/hg/core/HgInvalidControlFileException.java src/org/tmatesoft/hg/core/HgLogCommand.java src/org/tmatesoft/hg/core/HgOutgoingCommand.java src/org/tmatesoft/hg/core/HgStatus.java src/org/tmatesoft/hg/internal/ChangelogHelper.java src/org/tmatesoft/hg/internal/KeywordFilter.java src/org/tmatesoft/hg/internal/RepositoryComparator.java src/org/tmatesoft/hg/internal/RevlogStream.java src/org/tmatesoft/hg/repo/HgBranches.java src/org/tmatesoft/hg/repo/HgBundle.java src/org/tmatesoft/hg/repo/HgChangelog.java src/org/tmatesoft/hg/repo/HgDataFile.java src/org/tmatesoft/hg/repo/HgManifest.java src/org/tmatesoft/hg/repo/HgStatusCollector.java src/org/tmatesoft/hg/repo/HgTags.java src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java src/org/tmatesoft/hg/repo/Revlog.java test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java test/org/tmatesoft/hg/test/TestAuxUtilities.java
diffstat 24 files changed, 303 insertions(+), 151 deletions(-) [+]
line wrap: on
line diff
--- 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());
 			}
 		});
 	}
--- 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<Nodeid> test = new HashSet<Nodeid>(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
--- 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");
 		}
--- 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 @@
  *   }
  * </pre></code>
  *
+ * 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 <code>true</code> 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;
--- 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<Nodeid, Nodeid> getParents() {
+	public Pair<Nodeid, Nodeid> 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;
--- 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;
+	}
 }
--- 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 <code>this</code> 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 
+				}
 			}
 		}
 	}
--- 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");
 		}
--- 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 <code>null</code> 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());
--- 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<Integer, RawChangeset> cache = new TreeMap<Integer, RawChangeset>();
+	private final TreeMap<Integer, RawChangeset> cache = new TreeMap<Integer, RawChangeset>(); // 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 
-	 * <code>null</code> if none found up to leftBoundary 
+	 * @return changeset where specified file is mentioned among affected files, or <code>null</code> 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;
--- 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/%<tm/%<td %<tH:%<tM:%<tS", getChangeset().date());
+		Date d;
+		try {
+			d = getChangeset().date();
+		} catch (HgException ex) {
+			HgInternals.getContext(repo).getLog().error(getClass(), ex, null);
+			d = new Date(0l);
+		}
+		return String.format("%tY/%<tm/%<td %<tH:%<tM:%<tS", d);
 	}
 	
-	private RawChangeset getChangeset() {
+	private RawChangeset getChangeset() throws HgInvalidControlFileException {
 		if (latestFileCset == null) {
 			// XXX consider use of ChangelogHelper
 			int csetRev = repo.getFileNode(path).getChangesetLocalRevision(HgRepository.TIP);
--- a/src/org/tmatesoft/hg/internal/RepositoryComparator.java	Thu Dec 01 03:05:28 2011 +0100
+++ b/src/org/tmatesoft/hg/internal/RepositoryComparator.java	Thu Dec 01 05:21:40 2011 +0100
@@ -30,6 +30,7 @@
 import java.util.Set;
 
 import org.tmatesoft.hg.core.HgBadStateException;
+import org.tmatesoft.hg.core.HgInvalidControlFileException;
 import org.tmatesoft.hg.core.HgRemoteConnectionException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.repo.HgChangelog;
@@ -90,7 +91,7 @@
 	 * only its revision number. 
 	 * @param inspector delegate to analyze changesets, shall not be <code>null</code>
 	 */
-	public void visitLocalOnlyRevisions(HgChangelog.Inspector inspector) {
+	public void visitLocalOnlyRevisions(HgChangelog.Inspector inspector) throws HgInvalidControlFileException {
 		if (inspector == null) {
 			throw new IllegalArgumentException();
 		}
--- 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() {
--- 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() {
--- 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 {
--- 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<RawChangeset> range(int start, int end) {
+	public List<RawChangeset> 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);
 	}
--- 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"));
--- 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);
+				}
 			}
 		}
 	}
--- 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<Path> originals, int originalChangelogRevision) throws HgDataStreamException {
+	/*package-local*/static Path getOriginIfCopy(HgRepository hgRepo, Path fname, Collection<Path> 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);
--- 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();
--- 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<Path> 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
 				}
--- 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) {
--- 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
--- 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() {