changeset 427:31a89587eb04

FIXMEs: consistent names, throws for commands and their handlers. Use of checked exceptions in hi-level api
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 29 Mar 2012 17:14:35 +0200
parents 063b0663495a
children ead6c67f3319
files cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java cmdline/org/tmatesoft/hg/console/Log.java cmdline/org/tmatesoft/hg/console/Main.java cmdline/org/tmatesoft/hg/console/Manifest.java cmdline/org/tmatesoft/hg/console/Status.java design.txt src/org/tmatesoft/hg/core/ChangesetTransformer.java src/org/tmatesoft/hg/core/HgBadArgumentException.java src/org/tmatesoft/hg/core/HgCallbackTargetException.java src/org/tmatesoft/hg/core/HgCatCommand.java src/org/tmatesoft/hg/core/HgChangeset.java src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java src/org/tmatesoft/hg/core/HgChangesetHandler.java src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java src/org/tmatesoft/hg/core/HgException.java src/org/tmatesoft/hg/core/HgFileRevision.java src/org/tmatesoft/hg/core/HgIncomingCommand.java src/org/tmatesoft/hg/core/HgLibraryFailureException.java src/org/tmatesoft/hg/core/HgLogCommand.java src/org/tmatesoft/hg/core/HgManifestCommand.java src/org/tmatesoft/hg/core/HgManifestHandler.java src/org/tmatesoft/hg/core/HgOutgoingCommand.java src/org/tmatesoft/hg/core/HgPathNotFoundException.java src/org/tmatesoft/hg/core/HgStatus.java src/org/tmatesoft/hg/core/HgStatusCommand.java src/org/tmatesoft/hg/core/HgStatusHandler.java src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java src/org/tmatesoft/hg/repo/HgBundle.java src/org/tmatesoft/hg/repo/HgChangelog.java src/org/tmatesoft/hg/repo/HgInvalidDataFormatException.java src/org/tmatesoft/hg/repo/HgRuntimeException.java test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java test/org/tmatesoft/hg/test/TestHistory.java test/org/tmatesoft/hg/test/TestManifest.java test/org/tmatesoft/hg/test/TestStatus.java
diffstat 35 files changed, 497 insertions(+), 232 deletions(-) [+]
line wrap: on
line diff
--- a/cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java	Thu Mar 29 17:14:35 2012 +0200
@@ -63,7 +63,7 @@
 		return this;
 	}
 
-	public void next(HgChangeset changeset) {
+	public void cset(HgChangeset changeset) {
 		try {
 			final String s = print(changeset);
 			if (reverseOrder) {
--- a/cmdline/org/tmatesoft/hg/console/Log.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/cmdline/org/tmatesoft/hg/console/Log.java	Thu Mar 29 17:14:35 2012 +0200
@@ -20,6 +20,7 @@
 
 import java.util.List;
 
+import org.tmatesoft.hg.core.HgChangesetHandler;
 import org.tmatesoft.hg.core.HgFileRevision;
 import org.tmatesoft.hg.core.HgLogCommand;
 import org.tmatesoft.hg.repo.HgDataFile;
@@ -119,7 +120,7 @@
 		return rv;
 	}
 
-	private static final class Dump extends ChangesetDumpHandler implements HgLogCommand.FileHistoryHandler {
+	private static final class Dump extends ChangesetDumpHandler implements HgChangesetHandler.WithCopyHistory {
 
 		public Dump(HgRepository hgRepo) {
 			super(hgRepo);
--- a/cmdline/org/tmatesoft/hg/console/Main.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/cmdline/org/tmatesoft/hg/console/Main.java	Thu Mar 29 17:14:35 2012 +0200
@@ -28,6 +28,7 @@
 import java.util.Map;
 
 import org.junit.Assert;
+import org.tmatesoft.hg.core.HgManifestHandler;
 import org.tmatesoft.hg.core.HgCallbackTargetException;
 import org.tmatesoft.hg.core.HgCatCommand;
 import org.tmatesoft.hg.core.HgChangeset;
@@ -175,7 +176,7 @@
 		HgLogCommand cmd = new HgLogCommand(hgRepo);
 		cmd.file("file1", false);
 		cmd.execute(new HgChangesetTreeHandler() {
-			public void next(HgChangesetTreeHandler.TreeElement entry) {
+			public void treeElement(HgChangesetTreeHandler.TreeElement entry) {
 				StringBuilder sb = new StringBuilder();
 				HashSet<Nodeid> test = new HashSet<Nodeid>(entry.childRevisions());
 				for (HgChangeset cc : entry.children()) {
@@ -558,7 +559,7 @@
 	}
 
 	private void dumpCompleteManifestHigh() throws Exception {
-		new HgManifestCommand(hgRepo).dirs(true).execute(new HgManifestCommand.Handler() {
+		new HgManifestCommand(hgRepo).dirs(true).execute(new HgManifestHandler() {
 			
 			public void begin(Nodeid manifestRevision) {
 				System.out.println(">> " + manifestRevision);
--- a/cmdline/org/tmatesoft/hg/console/Manifest.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/cmdline/org/tmatesoft/hg/console/Manifest.java	Thu Mar 29 17:14:35 2012 +0200
@@ -19,6 +19,7 @@
 import static org.tmatesoft.hg.console.Options.asSet;
 import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
+import org.tmatesoft.hg.core.HgManifestHandler;
 import org.tmatesoft.hg.core.HgFileRevision;
 import org.tmatesoft.hg.core.HgManifestCommand;
 import org.tmatesoft.hg.core.Nodeid;
@@ -45,7 +46,7 @@
 		}
 		final boolean debug = cmdLineOpts.getBoolean("--debug");
 		final boolean verbose = cmdLineOpts.getBoolean("-v", "--verbose");
-		HgManifestCommand.Handler h = new HgManifestCommand.Handler() {
+		HgManifestHandler h = new HgManifestHandler() {
 			
 			public void begin(Nodeid manifestRevision) {
 			}
--- a/cmdline/org/tmatesoft/hg/console/Status.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/cmdline/org/tmatesoft/hg/console/Status.java	Thu Mar 29 17:14:35 2012 +0200
@@ -74,7 +74,7 @@
 			final EnumMap<HgStatus.Kind, List<Path>> data = new EnumMap<HgStatus.Kind, List<Path>>(HgStatus.Kind.class);
 			final Map<Path, Path> copies = showCopies ? new HashMap<Path,Path>() : null;
 			
-			public void handleStatus(HgStatus s) {
+			public void status(HgStatus s) {
 				List<Path> l = data.get(s.getKind());
 				if (l == null) {
 					l = new LinkedList<Path>();
@@ -86,7 +86,7 @@
 				}
 			}
 			
-			public void handleError(Path file, org.tmatesoft.hg.util.Status s) {
+			public void error(Path file, org.tmatesoft.hg.util.Status s) {
 				System.out.printf("FAILURE: %s %s\n", s.getMessage(), file);
 				s.getException().printStackTrace(System.out);
 			}
--- a/design.txt	Wed Mar 28 19:34:37 2012 +0200
+++ b/design.txt	Thu Mar 29 17:14:35 2012 +0200
@@ -139,4 +139,10 @@
 Unfortunately, Revision would be a nice name for a class <int, Nodeid>. As long as I don't want to keep methods to access int/nodeid separately
 and not to stick to Revision struct only (to avoid massive instances of Revision<int,Nodeid> when only one is sufficient), I'll need to name
 these separate methods anyway. Present opinion is that I don't need the object right now (will have to live with RevisionObject or RevisionDescriptor
-once change my mind) 
\ No newline at end of file
+once change my mind) 
+
+Handlers (HgStatusHandler, HgManifestHandler, HgChangesetHandler, HgChangesetTreeHandler)
+methods DO NOT throw CancelledException. cancellation is separate from processing logic. handlers can implements CancelSupport to become a source of cancellation, if necessary
+methods DO throw HgCallbackTargetException to propagate own errors/exceptions
+methods are supposed to silently pass HgRuntimeExceptions (although callback implementers may decide to wrap them into HgCallbackTargetException)
+descriptive names for the methods, whenever possible (not bare #next)
\ No newline at end of file
--- a/src/org/tmatesoft/hg/core/ChangesetTransformer.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/ChangesetTransformer.java	Thu Mar 29 17:14:35 2012 +0200
@@ -71,7 +71,7 @@
 
 		HgChangeset changeset = t.handle(revisionNumber, nodeid, cset);
 		try {
-			handler.next(changeset);
+			handler.cset(changeset);
 			cancelHelper.checkCancelled();
 		} catch (HgCallbackTargetException ex) {
 			failure = ex.setRevision(nodeid).setRevisionIndex(revisionNumber);
--- a/src/org/tmatesoft/hg/core/HgBadArgumentException.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgBadArgumentException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -34,4 +34,10 @@
 	public HgBadArgumentException(String message, Throwable cause) {
 		super(message, cause);
 	}
+
+	@Override
+	public HgBadArgumentException setRevision(Nodeid r) {
+		super.setRevision(r);
+		return this;
+	}
 }
--- a/src/org/tmatesoft/hg/core/HgCallbackTargetException.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgCallbackTargetException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -29,9 +29,11 @@
  * library's own {@link HgException} are rather obscure. Suggested approach is to wrap whatever exception user code produces with
  * {@link HgCallbackTargetException}, the only checked exception allowed out from a callback.
  * 
- *  <p>It's intentionally not a subclass of {@link HgException} to avoid get mixed with library own errors and be processed separately.
+ * <p>It's intentionally not a subclass of {@link HgException} to avoid get mixed with library own errors and be processed separately.
  * 
- * FIXME REVISIT shall just throw HgCallbackTargetException from any handler, and do not catch anything in commands at all.
+ * <p>Top-level API handlers ({@link HgStatusHandler}, {@link HgManifestHandler}, {@link HgChangesetHandler}, etc) allow to throw 
+ * HgCallbackTargetException from their methods. Exceptions throws this way are not handled in corresponding commands, except for
+ * revision or file name specification, unless already set. The, these exceptions go straight to the command caller.
  * 
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
--- a/src/org/tmatesoft/hg/core/HgCatCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgCatCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -24,10 +24,8 @@
 import java.nio.ByteBuffer;
 
 import org.tmatesoft.hg.repo.HgDataFile;
-import org.tmatesoft.hg.repo.HgInvalidControlFileException;
-import org.tmatesoft.hg.repo.HgInvalidFileException;
-import org.tmatesoft.hg.repo.HgInvalidRevisionException;
 import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.util.Adaptable;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelSupport;
@@ -136,11 +134,8 @@
 	 * @param sink output channel to write data to.
 	 * 
 	 * @throws HgBadArgumentException if no target file node found 
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
-	 * @throws HgInvalidFileException if access to file in working directory failed
-	 * @throws HgException in case of some other library issue 
-	 * @throws CancelledException if execution of the operation was cancelled
-	 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>)
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
+	 * @throws CancelledException if execution of the command was cancelled
 	 * @throws IllegalArgumentException when command arguments are incomplete or wrong
 	 */
 	public void execute(ByteChannel sink) throws HgException, CancelledException {
@@ -154,49 +149,53 @@
 		if (sink == null) {
 			throw new IllegalArgumentException("Need an output channel");
 		}
-		HgDataFile dataFile = repo.getFileNode(file);
-		if (!dataFile.exists()) {
-			// TODO may benefit from repo.getStoragePath to print revlog location in addition to human-friendly file path 
-			throw new HgBadArgumentException(String.format("File %s not found in the repository", file), null).setFileName(file);
-		}
-		int revToExtract;
-		if (cset != null) {
-			int csetRev = repo.getChangelog().getRevisionIndex(cset);
-			Nodeid toExtract = null;
-			do {
-				// TODO post-1.0 perhaps, HgChangesetFileSneaker may come handy?
-				toExtract = repo.getManifest().getFileRevision(csetRev, file);
+		try {
+			HgDataFile dataFile = repo.getFileNode(file);
+			if (!dataFile.exists()) {
+				// TODO may benefit from repo.getStoragePath to print revlog location in addition to human-friendly file path 
+				throw new HgPathNotFoundException(String.format("File %s not found in the repository", file), file);
+			}
+			int revToExtract;
+			if (cset != null) {
+				int csetRev = repo.getChangelog().getRevisionIndex(cset);
+				Nodeid toExtract = null;
+				do {
+					// TODO post-1.0 perhaps, HgChangesetFileSneaker may come handy?
+					toExtract = repo.getManifest().getFileRevision(csetRev, file);
+					if (toExtract == null) {
+						if (dataFile.isCopy()) {
+							file = dataFile.getCopySourceName();
+							dataFile = repo.getFileNode(file);
+						} else {
+							break;
+						}
+					}
+				} while (toExtract == null);
 				if (toExtract == null) {
-					if (dataFile.isCopy()) {
-						file = dataFile.getCopySourceName();
-						dataFile = repo.getFileNode(file);
-					} else {
-						break;
-					}
+					String m = String.format("File %s nor its origins were known at repository's %s revision", file, cset.shortNotation());
+					throw new HgPathNotFoundException(m, file).setRevision(cset);
 				}
-			} while (toExtract == null);
-			if (toExtract == null) {
-				// TODO explicit FileNotFoundException?
-				throw new HgBadArgumentException(String.format("File %s nor its origins were not known at repository %s revision", file, cset.shortNotation()), null);
+				revToExtract = dataFile.getRevisionIndex(toExtract);
+			} else if (revision != null) {
+				revToExtract = dataFile.getRevisionIndex(revision);
+			} else {
+				revToExtract = revisionIndex;
 			}
-			revToExtract = dataFile.getRevisionIndex(toExtract);
-		} else if (revision != null) {
-			revToExtract = dataFile.getRevisionIndex(revision);
-		} else {
-			revToExtract = revisionIndex;
+			ByteChannel sinkWrap;
+			if (getCancelSupport(null, false) == null) {
+				// no command-specific cancel helper, no need for extra proxy
+				// sink itself still may supply CS
+				sinkWrap = sink;
+			} else {
+				// try CS from sink, if any. at least there is CS from command 
+				CancelSupport cancelHelper = getCancelSupport(sink, true);
+				cancelHelper.checkCancelled();
+				sinkWrap = new ByteChannelProxy(sink, cancelHelper);
+			}
+			dataFile.contentWithFilters(revToExtract, sinkWrap);
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		}
-		ByteChannel sinkWrap;
-		if (getCancelSupport(null, false) == null) {
-			// no command-specific cancel helper, no need for extra proxy
-			// sink itself still may supply CS
-			sinkWrap = sink;
-		} else {
-			// try CS from sink, if any. at least there is CS from command 
-			CancelSupport cancelHelper = getCancelSupport(sink, true);
-			cancelHelper.checkCancelled();
-			sinkWrap = new ByteChannelProxy(sink, cancelHelper);
-		}
-		dataFile.contentWithFilters(revToExtract, sinkWrap);
 	}
 
 	private static class ByteChannelProxy implements ByteChannel, Adaptable {
--- a/src/org/tmatesoft/hg/core/HgChangeset.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgChangeset.java	Thu Mar 29 17:14:35 2012 +0200
@@ -21,10 +21,9 @@
 import java.util.List;
 import java.util.Map;
 
-import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.repo.HgChangelog;
+import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
-import org.tmatesoft.hg.repo.HgInvalidControlFileException;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.repo.HgStatusCollector;
@@ -201,7 +200,7 @@
 		return deletedFiles;
 	}
 
-	public boolean isMerge() throws HgInvalidControlFileException {
+	public boolean isMerge() throws HgRuntimeException {
 		// p1 == -1 and p2 != -1 is legitimate case
 		return !(getFirstParentRevision().isNull() || getSecondParentRevision().isNull()); 
 	}
--- a/src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java	Thu Mar 29 17:14:35 2012 +0200
@@ -18,7 +18,6 @@
 
 import org.tmatesoft.hg.internal.ManifestRevision;
 import org.tmatesoft.hg.repo.HgDataFile;
-import org.tmatesoft.hg.repo.HgInvalidControlFileException;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
 import org.tmatesoft.hg.repo.HgManifest;
 import org.tmatesoft.hg.repo.HgRepository;
@@ -92,16 +91,21 @@
 
 	/**
 	 * Shortcut to perform {@link #check(Path)} and {@link #exists()}. Result of the check may be accessed via {@link #getCheckStatus()}.
+	 * Errors during the check, if any, are reported through exception.
 	 * 
 	 * @param file name of the file in question
 	 * @return <code>true</code> if file is known at the selected changeset.
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws IllegalArgumentException if {@link #changeset(Nodeid)} not specified or file argument is bad.
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
-	public boolean checkExists(Path file) throws HgInvalidControlFileException {
+	public boolean checkExists(Path file) throws HgException {
 		check(file);
-		if (!checkResult.isOk() && checkResult.getException() instanceof HgInvalidControlFileException) {
-			throw (HgInvalidControlFileException) checkResult.getException();
+		// next seems reasonable, however renders boolean return value useless. perhaps void or distinct method?
+//		if (checkResult.isOk() && !exists()) {
+//			throw new HgPathNotFoundException(checkResult.getMessage(), file);
+//		}
+		if (!checkResult.isOk() && checkResult.getException() instanceof HgRuntimeException) {
+			throw new HgLibraryFailureException((HgRuntimeException) checkResult.getException());
 		}
 		return checkResult.isOk() && exists();
 	}
--- a/src/org/tmatesoft/hg/core/HgChangesetHandler.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgChangesetHandler.java	Thu Mar 29 17:14:35 2012 +0200
@@ -17,7 +17,7 @@
 package org.tmatesoft.hg.core;
 
 import org.tmatesoft.hg.internal.Callback;
-import org.tmatesoft.hg.util.CancelledException;
+import org.tmatesoft.hg.util.Path;
 
 /**
  * Callback to process {@link HgChangeset changesets}.
@@ -26,11 +26,31 @@
  * @author TMate Software Ltd.
  */
 @Callback
-public interface HgChangesetHandler/*XXX perhaps, shall parameterize with exception clients can throw, like: <E extends Exception>*/ {
+public interface HgChangesetHandler {
+
 	/**
-	 * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy.
+	 * @param changeset descriptor of a change, not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy.
 	 * @throws HgCallbackTargetException wrapper for any exception user code may produce 
-	 * @throws CancelledException if handler is not interested in more changesets and iteration shall stop
 	 */
-	void next(HgChangeset changeset) throws HgCallbackTargetException, CancelledException;
+	void cset(HgChangeset changeset) throws HgCallbackTargetException;
+
+
+	/**
+	 * When {@link HgLogCommand} is executed against file, handler passed to {@link HgLogCommand#execute(HgChangesetHandler)} may optionally
+	 * implement this interface to get information about file renames. Method {@link #copy(HgFileRevision, HgFileRevision)} would
+	 * get invoked prior any changeset of the original file (if file history being followed) is reported via {@link #cset(HgChangeset)}.
+	 * 
+	 * For {@link HgLogCommand#file(Path, boolean)} with renamed file path and follow argument set to false, 
+	 * {@link #copy(HgFileRevision, HgFileRevision)} would be invoked for the first copy/rename in the history of the file, but not 
+	 * followed by any changesets. 
+	 */
+	@Callback
+	public interface WithCopyHistory extends HgChangesetHandler {
+		// XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them?
+
+		/**
+		 * @throws HgCallbackTargetException wrapper object for any exception user code may produce 
+		 */
+		void copy(HgFileRevision from, HgFileRevision to) throws HgCallbackTargetException;
+	}
 }
--- a/src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java	Thu Mar 29 17:14:35 2012 +0200
@@ -19,7 +19,6 @@
 import java.util.Collection;
 
 import org.tmatesoft.hg.internal.Callback;
-import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.Pair;
 
 /**
@@ -36,9 +35,8 @@
 	 * @param entry access to various pieces of information about current tree node. Instances might be 
 	 * reused across calls and shall not be kept by client's code
 	 * @throws HgCallbackTargetException wrapper for any exception user code may produce 
-	 * @throws CancelledException if execution of the operation was cancelled
 	 */
-	public void next(HgChangesetTreeHandler.TreeElement entry) throws HgCallbackTargetException, CancelledException;
+	public void treeElement(HgChangesetTreeHandler.TreeElement entry) throws HgCallbackTargetException;
 
 	interface TreeElement {
 		/**
--- a/src/org/tmatesoft/hg/core/HgException.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -27,19 +27,19 @@
  * @author TMate Software Ltd.
  */
 @SuppressWarnings("serial")
-public class HgException extends Exception {
+public abstract class HgException extends Exception {
 	
 	protected final ExceptionInfo<HgException> extras = new ExceptionInfo<HgException>(this);
 
-	public HgException(String reason) {
+	protected HgException(String reason) {
 		super(reason);
 	}
 
-	public HgException(String reason, Throwable cause) {
+	protected HgException(String reason, Throwable cause) {
 		super(reason, cause);
 	}
 
-	public HgException(Throwable cause) {
+	protected HgException(Throwable cause) {
 		super(cause);
 	}
 
--- a/src/org/tmatesoft/hg/core/HgFileRevision.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgFileRevision.java	Thu Mar 29 17:14:35 2012 +0200
@@ -17,11 +17,10 @@
 package org.tmatesoft.hg.core;
 
 import org.tmatesoft.hg.repo.HgDataFile;
-import org.tmatesoft.hg.repo.HgInvalidControlFileException;
-import org.tmatesoft.hg.repo.HgInvalidRevisionException;
 import org.tmatesoft.hg.repo.HgManifest;
 import org.tmatesoft.hg.repo.HgManifest.Flags;
 import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.Pair;
@@ -78,10 +77,9 @@
 	
 	/**
 	 * Executable or symbolic link, or <code>null</code> if regular file
-	 * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog  
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
+	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
 	 */
-	public HgManifest.Flags getFileFlags() throws HgInvalidControlFileException, HgInvalidRevisionException {
+	public HgManifest.Flags getFileFlags() throws HgRuntimeException {
 		if (flags == null) {
 			HgDataFile df = repo.getFileNode(path);
 			int revIdx = df.getRevisionIndex(revision);
@@ -112,8 +110,9 @@
 	 * In most cases, only one parent revision would be present, only for merge revisions one can expect both.
 	 * 
 	 * @return parent revisions of this file revision, with {@link Nodeid#NULL} for missing values.
+	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
 	 */
-	public Pair<Nodeid, Nodeid> getParents() throws HgInvalidControlFileException {
+	public Pair<Nodeid, Nodeid> getParents() throws HgRuntimeException {
 		if (parents == null) {
 			HgDataFile fn = repo.getFileNode(path);
 			int revisionIndex = fn.getRevisionIndex(revision);
--- a/src/org/tmatesoft/hg/core/HgIncomingCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgIncomingCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -29,12 +29,12 @@
 import org.tmatesoft.hg.internal.RepositoryComparator.BranchChain;
 import org.tmatesoft.hg.repo.HgBundle;
 import org.tmatesoft.hg.repo.HgChangelog;
+import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.repo.HgInvalidControlFileException;
-import org.tmatesoft.hg.repo.HgInvalidFileException;
-import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
 import org.tmatesoft.hg.repo.HgRemoteRepository;
 import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.ProgressSupport;
 
@@ -103,34 +103,36 @@
 	 *   
 	 * @return list of nodes present at remote and missing locally
 	 * @throws HgRemoteConnectionException when failed to communicate with remote repository
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 */
-	public List<Nodeid> executeLite() throws HgRemoteConnectionException, HgInvalidControlFileException, CancelledException {
-		LinkedHashSet<Nodeid> result = new LinkedHashSet<Nodeid>();
-		RepositoryComparator repoCompare = getComparator();
-		for (BranchChain bc : getMissingBranches()) {
-			List<Nodeid> missing = repoCompare.visitBranches(bc);
-			HashSet<Nodeid> common = new HashSet<Nodeid>(); // ordering is irrelevant  
-			repoCompare.collectKnownRoots(bc, common);
-			// missing could only start with common elements. Once non-common, rest is just distinct branch revision trails.
-			for (Iterator<Nodeid> it = missing.iterator(); it.hasNext() && common.contains(it.next()); it.remove()) ; 
-			result.addAll(missing);
+	public List<Nodeid> executeLite() throws HgException, CancelledException {
+		try {
+			LinkedHashSet<Nodeid> result = new LinkedHashSet<Nodeid>();
+			RepositoryComparator repoCompare = getComparator();
+			for (BranchChain bc : getMissingBranches()) {
+				List<Nodeid> missing = repoCompare.visitBranches(bc);
+				HashSet<Nodeid> common = new HashSet<Nodeid>(); // ordering is irrelevant  
+				repoCompare.collectKnownRoots(bc, common);
+				// missing could only start with common elements. Once non-common, rest is just distinct branch revision trails.
+				for (Iterator<Nodeid> it = missing.iterator(); it.hasNext() && common.contains(it.next()); it.remove()) ; 
+				result.addAll(missing);
+			}
+			ArrayList<Nodeid> rv = new ArrayList<Nodeid>(result);
+			return rv;
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		}
-		ArrayList<Nodeid> rv = new ArrayList<Nodeid>(result);
-		return rv;
 	}
 
 	/**
 	 * Full information about incoming changes
 	 * 
-	 * @throws HgRemoteConnectionException when failed to communicate with remote repository
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
-	 * @throws HgInvalidFileException to indicate failure working with locally downloaded changes in a bundle file
 	 * @throws HgCallbackTargetException to re-throw exception from the handler
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 */
-	public void executeFull(final HgChangesetHandler handler) throws HgRemoteConnectionException, HgInvalidControlFileException, HgInvalidFileException, HgCallbackTargetException, CancelledException {
+	public void executeFull(final HgChangesetHandler handler) throws HgCallbackTargetException, HgException, CancelledException {
 		if (handler == null) {
 			throw new IllegalArgumentException("Delegate can't be null");
 		}
@@ -161,6 +163,8 @@
 				}
 			});
 			transformer.checkFailure();
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		} finally {
 			ps.done();
 		}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgLibraryFailureException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.core;
+
+import org.tmatesoft.hg.repo.HgRuntimeException;
+
+/**
+ * Sole purpose of this exception is to wrap unexpected errors from the library implementation and 
+ * propagate them to clients of hi-level API for graceful (and explicit) processing.
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+@SuppressWarnings("serial")
+public class HgLibraryFailureException extends HgException {
+
+	public HgLibraryFailureException(HgRuntimeException cause) {
+		super(cause);
+		assert cause != null;
+	}
+	
+	@Override
+	public HgRuntimeException getCause() {
+		return (HgRuntimeException) super.getCause();
+	}
+}
--- a/src/org/tmatesoft/hg/core/HgLogCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgLogCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012 TMate Software Ltd
+s * Copyright (c) 2011-2012 TMate Software Ltd
  *  
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -36,7 +36,6 @@
 import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgInternals;
 import org.tmatesoft.hg.repo.HgInvalidControlFileException;
-import org.tmatesoft.hg.repo.HgInvalidRevisionException;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgRuntimeException;
@@ -136,8 +135,9 @@
 	/**
 	 * Limit to specified subset of Changelog, [min(rev1,rev2), max(rev1,rev2)], inclusive.
 	 * Revision may be specified with {@link HgRepository#TIP}  
-	 * @param rev1 - revision local index
-	 * @param rev2 - revision local index
+	 * 
+	 * @param rev1 - local index of start changeset revision
+	 * @param rev2 - index of end changeset revision
 	 * @return <code>this</code> instance for convenience
 	 */
 	public HgLogCommand range(int rev1, int rev2) {
@@ -159,13 +159,16 @@
 	 * 
 	 * @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
+	 * @throws HgBadArgumentException if failed to find supplied changeset revision 
 	 */
-	public HgLogCommand changeset(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException {
+	public HgLogCommand changeset(Nodeid nid) throws HgBadArgumentException {
 		// XXX perhaps, shall support multiple (...) arguments and extend #execute to handle not only range, but also set of revisions.
-		final int csetRevIndex = repo.getChangelog().getRevisionIndex(nid);
-		return range(csetRevIndex, csetRevIndex);
+		try {
+			final int csetRevIndex = repo.getChangelog().getRevisionIndex(nid);
+			return range(csetRevIndex, csetRevIndex);
+		} catch (HgRuntimeException ex) {
+			throw new HgBadArgumentException("Can't find revision", ex).setRevision(nid);
+		}
 	}
 	
 	/**
@@ -189,7 +192,9 @@
 
 	/**
 	 * Similar to {@link #execute(HgChangesetHandler)}, collects and return result as a list.
-	 * @throws HgException FIXME EXCEPTIONS
+	 * 
+	 * @see #execute(HgChangesetHandler)
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 */
 	public List<HgChangeset> execute() throws HgException {
 		CollectHandler collector = new CollectHandler();
@@ -213,9 +218,8 @@
 	 * Iterate over range of changesets configured in the command.
 	 * 
 	 * @param handler callback to process changesets.
-	 * @throws HgCallbackTargetException wrapper for any exception user callback code may produce
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
-	 * @throws HgException in case of some other library issue 
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 * @throws IllegalArgumentException when inspector argument is null
 	 * @throws ConcurrentModificationException if this log command instance is already running
@@ -241,15 +245,18 @@
 			} else {
 				progressHelper.start(-1/*XXX enum const, or a dedicated method startUnspecified(). How about startAtLeast(int)?*/);
 				HgDataFile fileNode = repo.getFileNode(file);
+				if (!fileNode.exists()) {
+					throw new HgPathNotFoundException(String.format("File %s not found in the repository", file), file);
+				}
 				fileNode.history(startRev, endRev, this);
 				csetTransform.checkFailure();
 				if (fileNode.isCopy()) {
 					// even if we do not follow history, report file rename
 					do {
-						if (handler instanceof FileHistoryHandler) {
+						if (handler instanceof HgChangesetHandler.WithCopyHistory) {
 							HgFileRevision src = new HgFileRevision(repo, fileNode.getCopySourceRevision(), null, fileNode.getCopySourceName());
 							HgFileRevision dst = new HgFileRevision(repo, fileNode.getRevision(0), null, fileNode.getPath(), src.getPath());
-							((FileHistoryHandler) handler).copy(src, dst);
+							((HgChangesetHandler.WithCopyHistory) handler).copy(src, dst);
 						}
 						if (limit > 0 && count >= limit) {
 							// if limit reach, follow is useless.
@@ -263,8 +270,8 @@
 					} while (followHistory && fileNode.isCopy());
 				}
 			}
-//		} catch (HgRuntimeException ex) {
-//			FIXME wrap with checked HgRuntime subclass
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		} finally {
 			csetTransform = null;
 			progressHelper.done();
@@ -275,9 +282,8 @@
 	 * Tree-wise iteration of a file history, with handy access to parent-child relations between changesets. 
 	 *  
 	 * @param handler callback to process changesets.
-	 * @throws HgCallbackTargetException to re-throw exception from the handler
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
-	 * @throws HgException in case of some other library issue 
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 * @throws IllegalArgumentException if command is not satisfied with its arguments 
 	 * @throws ConcurrentModificationException if this log command instance is already running
@@ -345,7 +351,7 @@
 		// XXX shall sort completeHistory according to changeset numbers?
 		for (int i = 0; i < completeHistory.length; i++ ) {
 			final HistoryNode n = completeHistory[i];
-			handler.next(ei.init(n));
+			handler.treeElement(ei.init(n));
 			ph2.worked(1);
 			cancelHelper.checkCancelled();
 		}
@@ -390,26 +396,6 @@
 	}
 
 
-	/**
-	 * When {@link HgLogCommand} is executed against file, handler passed to {@link HgLogCommand#execute(HgChangesetHandler)} may optionally
-	 * implement this interface to get information about file renames. Method {@link #copy(HgFileRevision, HgFileRevision)} would
-	 * get invoked prior any changeset of the original file (if file history being followed) is reported via {@link #next(HgChangeset)}.
-	 * 
-	 * For {@link HgLogCommand#file(Path, boolean)} with renamed file path and follow argument set to false, 
-	 * {@link #copy(HgFileRevision, HgFileRevision)} would be invoked for the first copy/rename in the history of the file, but not 
-	 * followed by any changesets. 
-	 *
-	 * @author Artem Tikhomirov
-	 * @author TMate Software Ltd.
-	 */
-	public interface FileHistoryHandler extends HgChangesetHandler { // FIXME move to stanalone class file, perhaps?
-		// XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them?
-		/**
-		 * @throws HgCallbackTargetException wrapper object for any exception user code may produce 
-		 */
-		void copy(HgFileRevision from, HgFileRevision to) throws HgCallbackTargetException;
-	}
-	
 	public static class CollectHandler implements HgChangesetHandler {
 		private final List<HgChangeset> result = new LinkedList<HgChangeset>();
 
@@ -417,7 +403,7 @@
 			return Collections.unmodifiableList(result);
 		}
 
-		public void next(HgChangeset changeset) {
+		public void cset(HgChangeset changeset) {
 			result.add(changeset.clone());
 		}
 	}
--- a/src/org/tmatesoft/hg/core/HgManifestCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgManifestCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -25,10 +25,12 @@
 import java.util.LinkedList;
 import java.util.List;
 
-import org.tmatesoft.hg.internal.Callback;
 import org.tmatesoft.hg.repo.HgManifest;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgManifest.Flags;
+import org.tmatesoft.hg.repo.HgRuntimeException;
+import org.tmatesoft.hg.util.CancelSupport;
+import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.Path;
 import org.tmatesoft.hg.util.PathPool;
 import org.tmatesoft.hg.util.PathRewrite;
@@ -45,7 +47,7 @@
 	private final HgRepository repo;
 	private Path.Matcher matcher;
 	private int startRev = 0, endRev = TIP;
-	private Handler visitor;
+	private HgManifestHandler visitor;
 	private boolean needDirs = false;
 	
 	private final Mediator mediator = new Mediator();
@@ -97,12 +99,16 @@
 	}
 	
 	/**
-	 * Runs the command.
+	 * With all parameters set, execute the command.
+	 * 
 	 * @param handler - callback to get the outcome
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
+	 * @throws CancelledException if execution of the command was cancelled
 	 * @throws IllegalArgumentException if handler is <code>null</code>
 	 * @throws ConcurrentModificationException if this command is already in use (running)
 	 */
-	public void execute(Handler handler) throws HgException {
+	public void execute(HgManifestHandler handler) throws HgCallbackTargetException, HgException, CancelledException {
 		if (handler == null) {
 			throw new IllegalArgumentException();
 		}
@@ -111,25 +117,17 @@
 		}
 		try {
 			visitor = handler;
-			mediator.start();
+			mediator.start(getCancelSupport(handler, true));
 			repo.getManifest().walk(startRev, endRev, mediator);
+			mediator.checkFailure();
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		} finally {
 			mediator.done();
 			visitor = null;
 		}
 	}
 
-	/**
-	 * Callback to walk file/directory tree of a revision
-	 */
-	@Callback
-	public interface Handler { // FIXME TLC
-		void begin(Nodeid manifestRevision);
-		void dir(Path p); // optionally invoked (if walker was configured to spit out directories) prior to any files from this dir and subdirs
-		void file(HgFileRevision fileRevision); // XXX allow to check p is invalid (df.exists())
-		void end(Nodeid manifestRevision);
-	}
-
 	// I'd rather let HgManifestCommand implement HgManifest.Inspector directly, but this pollutes API alot
 	private class Mediator implements HgManifest.Inspector {
 		// file names are likely to repeat in each revision, hence caching of Paths.
@@ -138,61 +136,111 @@
 		private PathPool pathPool;
 		private List<HgFileRevision> manifestContent;
 		private Nodeid manifestNodeid;
+		private Exception failure;
+		private CancelSupport cancelHelper;
 		
-		public void start() {
+		public void start(CancelSupport cs) {
+			assert cs != null;
 			// Manifest keeps normalized paths
 			pathPool = new PathPool(new PathRewrite.Empty());
+			cancelHelper = cs;
 		}
 		
 		public void done() {
 			manifestContent = null;
 			pathPool = null;
 		}
+		
+		private void recordFailure(HgCallbackTargetException ex) {
+			failure = ex;
+		}
+		private void recordCancel(CancelledException ex) {
+			failure = ex;
+		}
+
+		public void checkFailure() throws HgCallbackTargetException, CancelledException {
+			// TODO post-1.0 perhaps, can combine this code (record/checkFailure) for reuse in more classes (e.g. in Revlog)
+			if (failure instanceof HgCallbackTargetException) {
+				HgCallbackTargetException ex = (HgCallbackTargetException) failure;
+				failure = null;
+				throw ex;
+			}
+			if (failure instanceof CancelledException) {
+				CancelledException ex = (CancelledException) failure;
+				failure = null;
+				throw ex;
+			}
+		}
 	
 		public boolean begin(int manifestRevision, Nodeid nid, int changelogRevision) {
 			if (needDirs && manifestContent == null) {
 				manifestContent = new LinkedList<HgFileRevision>();
 			}
-			visitor.begin(manifestNodeid = nid);
-			return true;
+			try {
+				visitor.begin(manifestNodeid = nid);
+				cancelHelper.checkCancelled();
+				return true;
+			} catch (HgCallbackTargetException ex) {
+				recordFailure(ex);
+				return false;
+			} catch (CancelledException ex) {
+				recordCancel(ex);
+				return false;
+			}
 		}
 		public boolean end(int revision) {
-			if (needDirs) {
-				LinkedHashMap<Path, LinkedList<HgFileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<HgFileRevision>>();
-				for (HgFileRevision fr : manifestContent) {
-					Path filePath = fr.getPath();
-					Path dirPath = pathPool.parent(filePath);
-					LinkedList<HgFileRevision> revs = breakDown.get(dirPath);
-					if (revs == null) {
-						revs = new LinkedList<HgFileRevision>();
-						breakDown.put(dirPath, revs);
+			try {
+				if (needDirs) {
+					LinkedHashMap<Path, LinkedList<HgFileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<HgFileRevision>>();
+					for (HgFileRevision fr : manifestContent) {
+						Path filePath = fr.getPath();
+						Path dirPath = pathPool.parent(filePath);
+						LinkedList<HgFileRevision> revs = breakDown.get(dirPath);
+						if (revs == null) {
+							revs = new LinkedList<HgFileRevision>();
+							breakDown.put(dirPath, revs);
+						}
+						revs.addLast(fr);
 					}
-					revs.addLast(fr);
+					for (Path dir : breakDown.keySet()) {
+						visitor.dir(dir);
+						cancelHelper.checkCancelled();
+						for (HgFileRevision fr : breakDown.get(dir)) {
+							visitor.file(fr);
+						}
+					}
+					manifestContent.clear();
 				}
-				for (Path dir : breakDown.keySet()) {
-					visitor.dir(dir);
-					for (HgFileRevision fr : breakDown.get(dir)) {
-						visitor.file(fr);
-					}
-				}
-				manifestContent.clear();
+				visitor.end(manifestNodeid);
+				cancelHelper.checkCancelled();
+				return true;
+			} catch (HgCallbackTargetException ex) {
+				recordFailure(ex);
+				return false;
+			} catch (CancelledException ex) {
+				recordCancel(ex);
+				return false;
+			} finally {
+				manifestNodeid = null;
 			}
-			visitor.end(manifestNodeid);
-			manifestNodeid = null;
-			return true;
 		}
 		
 		public boolean next(Nodeid nid, Path fname, Flags flags) {
 			if (matcher != null && !matcher.accept(fname)) {
 				return true;
 			}
-			HgFileRevision fr = new HgFileRevision(repo, nid, flags, fname);
-			if (needDirs) {
-				manifestContent.add(fr);
-			} else {
-				visitor.file(fr);
+			try {
+				HgFileRevision fr = new HgFileRevision(repo, nid, flags, fname);
+				if (needDirs) {
+					manifestContent.add(fr);
+				} else {
+					visitor.file(fr);
+				}
+				return true;
+			} catch (HgCallbackTargetException ex) {
+				recordFailure(ex);
+				return false;
 			}
-			return true;
 		}
 	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgManifestHandler.java	Thu Mar 29 17:14:35 2012 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011-2012 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.core;
+
+import org.tmatesoft.hg.internal.Callback;
+import org.tmatesoft.hg.util.Path;
+
+/**
+ * Callback to walk file/directory tree of a revision
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+@Callback
+public interface HgManifestHandler {
+	/**
+	 * Indicates start of manifest revision. Subsequent {@link #file(HgFileRevision)} and {@link #dir(Path)} come 
+	 * from the specified manifest revision until {@link #end(Nodeid)} with the matching revision is invoked.
+	 * 
+	 * @param manifestRevision unique identifier of the manifest revision
+	 * @throws HgCallbackTargetException wrapper for any exception user code may produce
+	 */
+	void begin(Nodeid manifestRevision) throws HgCallbackTargetException;
+
+	/**
+	 * If walker is configured to spit out directories, indicates files from specified directories are about to be reported.
+	 * Comes prior to any files from this directory and subdirectories
+	 * 
+	 * @param path directory known in the manifest
+	 * @throws HgCallbackTargetException wrapper for any exception user code may produce
+	 */
+	void dir(Path path) throws HgCallbackTargetException; 
+
+	/**
+	 * Reports a file revision entry in the manifest
+	 * 
+	 * @param fileRevision description of the file revision
+	 * @throws HgCallbackTargetException wrapper for any exception user code may produce
+	 */
+	void file(HgFileRevision fileRevision) throws HgCallbackTargetException;
+
+	/**
+	 * Indicates all files from the manifest revision have been reported.
+	 * Closes {@link #begin(Nodeid)} with the same revision that came before.
+	 * 
+	 * @param manifestRevision unique identifier of the manifest revision 
+	 * @throws HgCallbackTargetException wrapper for any exception user code may produce
+	 */
+	void end(Nodeid manifestRevision) throws HgCallbackTargetException;
+}
\ No newline at end of file
--- a/src/org/tmatesoft/hg/core/HgOutgoingCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgOutgoingCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -25,6 +25,7 @@
 import org.tmatesoft.hg.repo.HgInvalidControlFileException;
 import org.tmatesoft.hg.repo.HgRemoteRepository;
 import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.util.CancelSupport;
 import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.ProgressSupport;
@@ -95,14 +96,16 @@
 	 * 
 	 * @return list on local nodes known to be missing at remote server 
 	 * @throws HgRemoteConnectionException when failed to communicate with remote repository
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 */
-	public List<Nodeid> executeLite() throws HgRemoteConnectionException, HgInvalidControlFileException, CancelledException {
+	public List<Nodeid> executeLite() throws HgRemoteConnectionException, HgException, CancelledException {
 		final ProgressSupport ps = getProgressSupport(null);
 		try {
 			ps.start(10);
 			return getComparator(new ProgressSupport.Sub(ps, 5), getCancelSupport(null, true)).getLocalOnlyRevisions();
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		} finally {
 			ps.done();
 		}
@@ -112,12 +115,12 @@
 	 * Complete information about outgoing changes
 	 * 
 	 * @param handler delegate to process changes
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
 	 * @throws HgRemoteConnectionException when failed to communicate with remote repository
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
-	 * @throws HgCallbackTargetException to re-throw exception from the handler
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 */
-	public void executeFull(final HgChangesetHandler handler) throws HgRemoteConnectionException, HgInvalidControlFileException, HgCallbackTargetException, CancelledException {
+	public void executeFull(final HgChangesetHandler handler) throws HgCallbackTargetException, HgException, CancelledException {
 		if (handler == null) {
 			throw new IllegalArgumentException("Delegate can't be null");
 		}
@@ -129,6 +132,8 @@
 			inspector.limitBranches(branches);
 			getComparator(new ProgressSupport.Sub(ps, 1), cs).visitLocalOnlyRevisions(inspector);
 			inspector.checkFailure();
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		} finally {
 			ps.done();
 		}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgPathNotFoundException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.core;
+
+import org.tmatesoft.hg.util.Path;
+
+/**
+ * Indicates supplied path/location was is missing in the repository or specific revision.
+ * <p>Use {@link #getFileName()} to access name of the missing file
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+@SuppressWarnings("serial")
+public class HgPathNotFoundException extends HgException {
+	
+	public HgPathNotFoundException(String message, Path missingPath) {
+		super(message, null);
+		assert missingPath != null;
+		setFileName(missingPath);
+	}
+}
--- a/src/org/tmatesoft/hg/core/HgStatus.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgStatus.java	Thu Mar 29 17:14:35 2012 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 TMate Software Ltd
+ * Copyright (c) 2011-2012 TMate Software Ltd
  *  
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
 import java.util.Date;
 
 import org.tmatesoft.hg.internal.ChangelogHelper;
-import org.tmatesoft.hg.repo.HgInvalidControlFileException;
 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.util.Path;
 
@@ -72,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() throws HgInvalidControlFileException {
+	public String getModificationAuthor() {
 		RawChangeset cset = logHelper.findLatestChangeWith(path);
 		if (cset == null) {
 			if (kind == Kind.Modified || kind == Kind.Added || kind == Kind.Removed /*&& RightBoundary is TIP*/) {
@@ -85,7 +84,7 @@
 		return null;
 	}
 
-	public Date getModificationDate() throws HgInvalidControlFileException {
+	public Date getModificationDate() {
 		RawChangeset cset = logHelper.findLatestChangeWith(path);
 		if (cset == null) {
 			File localFile = new File(logHelper.getRepo().getWorkingDir(), path.toString());
--- a/src/org/tmatesoft/hg/core/HgStatusCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgStatusCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -25,6 +25,7 @@
 
 import org.tmatesoft.hg.internal.ChangelogHelper;
 import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.repo.HgStatusCollector;
 import org.tmatesoft.hg.repo.HgStatusInspector;
 import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector;
@@ -162,13 +163,14 @@
 	 * Perform status operation according to parameters set.
 	 *  
 	 * @param statusHandler callback to get status information
-	 * @throws HgCallbackTargetException wrapper for any exception user callback code may produce
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 * @throws CancelledException if execution of the command was cancelled
 	 * @throws IOException FIXME EXCEPTIONS WTF it's doing here if there are (further unspecified) errors while walking working copy
 	 * @throws IllegalArgumentException if handler is <code>null</code>
 	 * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread)
 	 */
-	public void execute(HgStatusHandler statusHandler) throws HgCallbackTargetException, CancelledException, HgException, IOException {
+	public void execute(HgStatusHandler statusHandler) throws HgCallbackTargetException, HgException, CancelledException, IOException {
 		if (statusHandler == null) {
 			throw new IllegalArgumentException();
 		}
@@ -197,6 +199,8 @@
 			// this is our exception, thrown from Mediator.
 			// next check shall throw original cause of the stop - either HgCallbackTargetException or original CancelledException
 			mediator.checkFailure();
+		} catch (HgRuntimeException ex) {
+			throw new HgLibraryFailureException(ex);
 		} finally {
 			mediator.done();
 		}
@@ -264,7 +268,7 @@
 
 		private void dispatch(HgStatus s) {
 			try {
-				handler.handleStatus(s);
+				handler.status(s);
 				handlerCancelSupport.checkCancelled();
 			} catch (HgCallbackTargetException ex) {
 				failure = ex;
@@ -317,7 +321,7 @@
 		
 		public void invalid(Path fname, Exception err) {
 			try {
-				handler.handleError(fname, new Status(Status.Kind.ERROR, "Failed to get file status", err));
+				handler.error(fname, new Status(Status.Kind.ERROR, "Failed to get file status", err));
 				handlerCancelSupport.checkCancelled();
 			} catch (HgCallbackTargetException ex) {
 				failure = ex;
--- a/src/org/tmatesoft/hg/core/HgStatusHandler.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgStatusHandler.java	Thu Mar 29 17:14:35 2012 +0200
@@ -28,18 +28,20 @@
 @Callback
 public interface HgStatusHandler {
 
-	/** #next() as in HgChangesetHandler?
-	 * FIXME perhaps, handle() is better name? If yes, rename method in HgChangesetHandler, too, to make them similar.
-	 * void next(HgStatus s);
+	/**
+	 * Report status of the next file
+	 * 
+	 * @param s file status descriptor 
 	 * @throws HgCallbackTargetException wrapper for any exception user code may produce
 	 */
-	void handleStatus(HgStatus s) throws HgCallbackTargetException;
+	void status(HgStatus s) throws HgCallbackTargetException;
 
 	/**
 	 * Report non-critical error processing single file during status operation
+	 * 
 	 * @param file name of the file that caused the trouble
 	 * @param s error description object
 	 * @throws HgCallbackTargetException wrapper for any exception user code may produce
 	 */
-	void handleError(Path file, Status s) throws HgCallbackTargetException;
+	void error(Path file, Status s) throws HgCallbackTargetException;
 }
--- a/src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java	Thu Mar 29 17:14:35 2012 +0200
@@ -27,7 +27,6 @@
 import org.tmatesoft.hg.internal.Experimental;
 import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.repo.HgInternals;
-import org.tmatesoft.hg.repo.HgInvalidFileException;
 import org.tmatesoft.hg.repo.HgRepository;
 
 /**
@@ -112,6 +111,11 @@
 		throw new UnsupportedOperationException();
 	}
 	
+	/**
+	 * Perform config file update
+	 * 
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
+	 */
 	public void execute() throws HgException {
 		try {
 			ConfigFile cfg = new ConfigFile();
@@ -132,7 +136,8 @@
 			}
 			cfg.writeTo(configFile);
 		} catch (IOException ex) {
-			throw new HgInvalidFileException("Failed to update configuration file", ex, configFile);
+			String m = String.format("Failed to update configuration file %s", configFile);
+			throw new HgBadArgumentException(m, ex); // TODO [post-1.0] better exception, it's not bad argument case
 		}
 	}
 
--- a/src/org/tmatesoft/hg/repo/HgBundle.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/repo/HgBundle.java	Thu Mar 29 17:14:35 2012 +0200
@@ -19,7 +19,6 @@
 import java.io.File;
 import java.io.IOException;
 
-import org.tmatesoft.hg.core.HgBadArgumentException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.core.SessionContext;
 import org.tmatesoft.hg.internal.ByteArrayChannel;
@@ -170,7 +169,7 @@
 					return false;
 				} catch (IOException ex) {
 					throw new HgInvalidFileException("Invalid bundle file", ex, bundleFile); // TODO post-1.0 revisit exception handling
-				} catch (HgBadArgumentException ex) {
+				} catch (HgInvalidDataFormatException ex) {
 					throw new HgInvalidControlFileException("Invalid bundle file", ex, bundleFile);
 				}
 				return true;
--- a/src/org/tmatesoft/hg/repo/HgChangelog.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/repo/HgChangelog.java	Thu Mar 29 17:14:35 2012 +0200
@@ -30,7 +30,6 @@
 import java.util.Map;
 import java.util.TimeZone;
 
-import org.tmatesoft.hg.core.HgBadArgumentException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.Callback;
 import org.tmatesoft.hg.internal.DataAccess;
@@ -227,7 +226,7 @@
 			}
 		}
 
-		/*package*/ static RawChangeset parse(DataAccess da) throws IOException, HgBadArgumentException {
+		/*package*/ static RawChangeset parse(DataAccess da) throws IOException, HgInvalidDataFormatException {
 			byte[] data = da.byteArray();
 			RawChangeset rv = new RawChangeset();
 			rv.init(data, 0, data.length, null);
@@ -235,18 +234,17 @@
 		}
 
 		// @param usersPool - it's likely user names get repeated again and again throughout repository. can be null
-		// FIXME replace HgBadArgumentException with HgInvalidDataFormatException or HgInvalidControlFileException 
-		/* package-local */void init(byte[] data, int offset, int length, Pool<String> usersPool) throws HgBadArgumentException {
+		/* package-local */void init(byte[] data, int offset, int length, Pool<String> usersPool) throws HgInvalidDataFormatException {
 			final int bufferEndIndex = offset + length;
 			final byte lineBreak = (byte) '\n';
 			int breakIndex1 = indexOf(data, lineBreak, offset, bufferEndIndex);
 			if (breakIndex1 == -1) {
-				throw new HgBadArgumentException("Bad Changeset data", null);
+				throw new HgInvalidDataFormatException("Bad Changeset data");
 			}
 			Nodeid _nodeid = Nodeid.fromAscii(data, 0, breakIndex1);
 			int breakIndex2 = indexOf(data, lineBreak, breakIndex1 + 1, bufferEndIndex);
 			if (breakIndex2 == -1) {
-				throw new HgBadArgumentException("Bad Changeset data", null);
+				throw new HgInvalidDataFormatException("Bad Changeset data");
 			}
 			String _user = new String(data, breakIndex1 + 1, breakIndex2 - breakIndex1 - 1);
 			if (usersPool != null) {
@@ -254,12 +252,12 @@
 			}
 			int breakIndex3 = indexOf(data, lineBreak, breakIndex2 + 1, bufferEndIndex);
 			if (breakIndex3 == -1) {
-				throw new HgBadArgumentException("Bad Changeset data", null);
+				throw new HgInvalidDataFormatException("Bad Changeset data");
 			}
 			String _timeString = new String(data, breakIndex2 + 1, breakIndex3 - breakIndex2 - 1);
 			int space1 = _timeString.indexOf(' ');
 			if (space1 == -1) {
-				throw new HgBadArgumentException(String.format("Bad Changeset data: %s in [%d..%d]", "time string", breakIndex2+1, breakIndex3), null);
+				throw new HgInvalidDataFormatException(String.format("Bad Changeset data: %s in [%d..%d]", "time string", breakIndex2+1, breakIndex3));
 			}
 			int space2 = _timeString.indexOf(' ', space1 + 1);
 			if (space2 == -1) {
@@ -305,7 +303,7 @@
 					}
 				}
 				if (breakIndex4 == -1 || breakIndex4 >= bufferEndIndex) {
-					throw new HgBadArgumentException("Bad Changeset data", null);
+					throw new HgInvalidDataFormatException("Bad Changeset data");
 				}
 			} else {
 				breakIndex4--;
@@ -317,7 +315,7 @@
 			} catch (UnsupportedEncodingException ex) {
 				_comment = "";
 				// Could hardly happen
-				throw new HgBadArgumentException("Bad Changeset data", ex);
+				throw new HgInvalidDataFormatException("Bad Changeset data", ex);
 			}
 			// change this instance at once, don't leave it partially changes in case of error
 			this.manifest = _nodeid;
@@ -381,9 +379,8 @@
 				// XXX there's no guarantee for Changeset.Callback that distinct instance comes each time, consider instance reuse
 				inspector.next(revisionNumber, Nodeid.fromBinary(nodeid, 0), cset);
 				progressHelper.worked(1);
-			} catch (HgBadArgumentException ex) {
-				// see below about better exception
-				throw new HgInvalidControlFileException("Failed reading changelog", ex, null).setRevisionIndex(revisionNumber);  
+			} catch (HgInvalidDataFormatException ex) {
+				throw ex.setRevisionIndex(revisionNumber);  
 			} catch (IOException ex) {
 				// XXX need better exception, perhaps smth like HgChangelogException (extends HgInvalidControlFileException)
 				throw new HgInvalidControlFileException("Failed reading changelog", ex, null).setRevisionIndex(revisionNumber);  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/repo/HgInvalidDataFormatException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.repo;
+
+/**
+ * Indicates broken, unknown or otherwise bad data structure.
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+@SuppressWarnings("serial")
+public class HgInvalidDataFormatException extends HgRuntimeException {
+	// IMPLEMENTATION NOTE. Perhaps, this might be intermediate class between HgRuntimeException and HgInvalidFileException
+	
+	public HgInvalidDataFormatException(String message) {
+		super(message, null);
+	}
+	
+	public HgInvalidDataFormatException(String message, Throwable cause) {
+		super(message, cause);
+	}
+}
--- a/src/org/tmatesoft/hg/repo/HgRuntimeException.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/src/org/tmatesoft/hg/repo/HgRuntimeException.java	Thu Mar 29 17:14:35 2012 +0200
@@ -17,6 +17,7 @@
 package org.tmatesoft.hg.repo;
 
 import org.tmatesoft.hg.core.HgException;
+import org.tmatesoft.hg.core.HgLibraryFailureException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.ExceptionInfo;
 import org.tmatesoft.hg.util.Path;
@@ -29,9 +30,10 @@
  * exceptions are made runtime, rooting at this single class.
  * 
  * <p>Hi-level api, {@link org.tmatesoft.hg.core}, where interaction with user-supplied values is more explicit,
- * may follow different exception strategy.
+ * follows different exception strategy, namely checked exceptions rooted at {@link HgException}.
  * 
  * @see HgException
+ * @see HgLibraryFailureException
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
--- a/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java	Thu Mar 29 17:14:35 2012 +0200
@@ -427,7 +427,7 @@
 		final HgLogCommand logCommand = new HgLogCommand(repository);
 		logCommand.file(targetPath, true);
 		logCommand.execute(new HgChangesetHandler() {
-			public void next(HgChangeset changeset) {
+			public void cset(HgChangeset changeset) {
 				if (changeset.getAffectedFiles().contains(targetPath)) {
 					System.out.println(changeset.getRevisionIndex() + " " + changeSetRevisionToTags.get(changeset.getNodeid()));
 				}
--- a/test/org/tmatesoft/hg/test/TestHistory.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/test/org/tmatesoft/hg/test/TestHistory.java	Thu Mar 29 17:14:35 2012 +0200
@@ -30,10 +30,11 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.tmatesoft.hg.core.HgChangeset;
+import org.tmatesoft.hg.core.HgChangesetHandler;
+import org.tmatesoft.hg.core.HgChangesetHandler.WithCopyHistory;
 import org.tmatesoft.hg.core.HgFileRevision;
 import org.tmatesoft.hg.core.HgLogCommand;
 import org.tmatesoft.hg.core.HgLogCommand.CollectHandler;
-import org.tmatesoft.hg.core.HgLogCommand.FileHistoryHandler;
 import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.test.LogOutputParser.Record;
@@ -93,7 +94,7 @@
 		changelogParser.reset();
 		eh.run("hg", "log", "--debug", "--follow", f.toString());
 		
-		class H extends CollectHandler implements FileHistoryHandler {
+		class H extends CollectHandler implements HgChangesetHandler.WithCopyHistory {
 			boolean copyReported = false;
 			boolean fromMatched = false;
 			public void copy(HgFileRevision from, HgFileRevision to) {
--- a/test/org/tmatesoft/hg/test/TestManifest.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/test/org/tmatesoft/hg/test/TestManifest.java	Thu Mar 29 17:14:35 2012 +0200
@@ -28,6 +28,7 @@
 
 import org.junit.Rule;
 import org.junit.Test;
+import org.tmatesoft.hg.core.HgManifestHandler;
 import org.tmatesoft.hg.core.HgFileRevision;
 import org.tmatesoft.hg.core.HgManifestCommand;
 import org.tmatesoft.hg.core.Nodeid;
@@ -50,7 +51,7 @@
 	private ManifestOutputParser manifestParser;
 	private ExecHelper eh;
 	final LinkedList<HgFileRevision> revisions = new LinkedList<HgFileRevision>();
-	private HgManifestCommand.Handler handler  = new HgManifestCommand.Handler() {
+	private HgManifestHandler handler  = new HgManifestHandler() {
 		
 		public void file(HgFileRevision fileRevision) {
 			revisions.add(fileRevision);
--- a/test/org/tmatesoft/hg/test/TestStatus.java	Wed Mar 28 19:34:37 2012 +0200
+++ b/test/org/tmatesoft/hg/test/TestStatus.java	Thu Mar 29 17:14:35 2012 +0200
@@ -182,7 +182,7 @@
 		private final Map<Path, List<Kind>> name2kinds = new TreeMap<Path, List<Kind>>();
 		private final Map<Path, Status> name2error = new LinkedHashMap<Path, Status>();
 
-		public void handleStatus(HgStatus s) {
+		public void status(HgStatus s) {
 			List<Path> l = kind2names.get(s.getKind());
 			if (l == null) {
 				kind2names.put(s.getKind(), l = new LinkedList<Path>());
@@ -196,7 +196,7 @@
 			k.add(s.getKind());
 		}
 		
-		public void handleError(Path file, Status s) {
+		public void error(Path file, Status s) {
 			name2error.put(file, s);
 		}