changeset 131:aa1629f36482

Renamed .core classes to start with Hg prefix
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 16 Feb 2011 20:47:56 +0100
parents 7567f4a42fe5
children 6778075cd2b4
files TODO cmdline/org/tmatesoft/hg/console/Log.java cmdline/org/tmatesoft/hg/console/Manifest.java design.txt src/org/tmatesoft/hg/core/CatCommand.java src/org/tmatesoft/hg/core/HgCatCommand.java src/org/tmatesoft/hg/core/HgChangeset.java src/org/tmatesoft/hg/core/HgLogCommand.java src/org/tmatesoft/hg/core/HgManifestCommand.java src/org/tmatesoft/hg/core/HgRepoFacade.java src/org/tmatesoft/hg/core/HgStatus.java src/org/tmatesoft/hg/core/HgStatusCommand.java src/org/tmatesoft/hg/core/LogCommand.java src/org/tmatesoft/hg/core/RepositoryFacade.java src/org/tmatesoft/hg/core/RepositoryTreeWalker.java src/org/tmatesoft/hg/core/StatusCommand.java test/org/tmatesoft/hg/test/TestByteChannel.java test/org/tmatesoft/hg/test/TestHistory.java test/org/tmatesoft/hg/test/TestManifest.java test/org/tmatesoft/hg/test/TestStatus.java
diffstat 20 files changed, 923 insertions(+), 905 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Wed Feb 16 20:33:31 2011 +0100
+++ b/TODO	Wed Feb 16 20:47:56 2011 +0100
@@ -47,7 +47,7 @@
   - HgRepository constants (TIP, BAD, WC) to HgRevisions enum
 
 * defects
-  ConfigFile to strip comments from values (#)
+  - ConfigFile to strip comments from values (#)
 
 Proposed:
 - LogCommand.revision(int... rev)+ to walk selected revisions only (list->sort(array) on execute, binary search)
--- a/cmdline/org/tmatesoft/hg/console/Log.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/cmdline/org/tmatesoft/hg/console/Log.java	Wed Feb 16 20:47:56 2011 +0100
@@ -21,8 +21,8 @@
 import java.util.List;
 
 import org.tmatesoft.hg.core.HgChangeset;
-import org.tmatesoft.hg.core.LogCommand;
-import org.tmatesoft.hg.core.LogCommand.FileRevision;
+import org.tmatesoft.hg.core.HgLogCommand;
+import org.tmatesoft.hg.core.HgLogCommand.FileRevision;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.core.Path;
 import org.tmatesoft.hg.repo.HgChangelog;
@@ -48,7 +48,7 @@
 		dump.complete = true; //cmdLineOpts;
 		dump.verbose = false; //cmdLineOpts;
 		dump.reverseOrder = true;
-		LogCommand cmd = new LogCommand(hgRepo);
+		HgLogCommand cmd = new HgLogCommand(hgRepo);
 		if (cmdLineOpts.users != null) {
 			for (String u : cmdLineOpts.users) {
 				cmd.user(u);
@@ -115,7 +115,7 @@
 		return rv;
 	}
 
-	private static final class Dump implements LogCommand.FileHistoryHandler {
+	private static final class Dump implements HgLogCommand.FileHistoryHandler {
 		// params
 		boolean complete = false; // roughly --debug
 		boolean reverseOrder = false;
--- a/cmdline/org/tmatesoft/hg/console/Manifest.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/cmdline/org/tmatesoft/hg/console/Manifest.java	Wed Feb 16 20:47:56 2011 +0100
@@ -18,10 +18,10 @@
 
 import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
-import org.tmatesoft.hg.core.LogCommand.FileRevision;
+import org.tmatesoft.hg.core.HgLogCommand.FileRevision;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.core.Path;
-import org.tmatesoft.hg.core.RepositoryTreeWalker;
+import org.tmatesoft.hg.core.HgManifestCommand;
 import org.tmatesoft.hg.repo.HgManifest;
 import org.tmatesoft.hg.repo.HgRepository;
 
@@ -43,7 +43,7 @@
 		System.out.println(hgRepo.getLocation());
 		hgRepo.getManifest().walk(0, TIP, new Dump());
 		//
-		new RepositoryTreeWalker(hgRepo).dirs(true).walk(new RepositoryTreeWalker.Handler() {
+		new HgManifestCommand(hgRepo).dirs(true).walk(new HgManifestCommand.Handler() {
 			
 			public void begin(Nodeid manifestRevision) {
 				System.out.println(">> " + manifestRevision);
--- a/design.txt	Wed Feb 16 20:33:31 2011 +0100
+++ b/design.txt	Wed Feb 16 20:47:56 2011 +0100
@@ -81,7 +81,12 @@
   - CommandContext
   - Data access - not bytes, but ByteChannel
   - HgRepository constants (TIP, BAD, WC) to HgRevisions enum
-  - RevisionMap to replace TreeMap<Integer, ?> 
+  - RevisionMap to replace TreeMap<Integer, ?>
+  + .core.* rename to Hg*
+  + RepositoryTreeWalker to ManifestCommand to match other command classes 
+
+* defects
+  - ConfigFile to strip comments from values (#)
 
 <<<<<
 
--- a/src/org/tmatesoft/hg/core/CatCommand.java	Wed Feb 16 20:33:31 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2011 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 static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
-import static org.tmatesoft.hg.repo.HgRepository.TIP;
-
-import java.io.FileNotFoundException;
-
-import org.tmatesoft.hg.repo.HgDataFile;
-import org.tmatesoft.hg.repo.HgRepository;
-import org.tmatesoft.hg.util.ByteChannel;
-
-/**
- * Command to obtain content of a 
- * 
- * @author Artem Tikhomirov
- * @author TMate Software Ltd.
- */
-public class CatCommand {
-
-	private final HgRepository repo;
-	private Path file;
-	private int localRevision = TIP;
-	private Nodeid revision;
-
-	public CatCommand(HgRepository hgRepo) {
-		repo = hgRepo;
-	}
-
-	public CatCommand file(Path fname) {
-		file = fname;
-		return this;
-	}
-
-	// rev can't be WORKING_COPY (if allowed, need to implement in #execute())
-	public CatCommand revision(int rev) {
-		localRevision = rev;
-		revision = null;
-		return this;
-	}
-	
-	public CatCommand revision(Nodeid nodeid) {
-		revision = nodeid;
-		localRevision = BAD_REVISION;
-		return this;
-	}
-
-	public void execute(ByteChannel sink) throws Exception /*TODO own exception type*/ {
-		if (localRevision == BAD_REVISION && revision == null) {
-			throw new IllegalArgumentException("Either local file revision number or nodeid shall be specified");
-		}
-		if (file == null) {
-			throw new IllegalArgumentException("Name of the file is missing");
-		}
-		if (sink == null) {
-			throw new IllegalArgumentException();
-		}
-		HgDataFile dataFile = repo.getFileNode(file);
-		if (!dataFile.exists()) {
-			throw new FileNotFoundException();
-		}
-		int revToExtract;
-		if (revision != null) {
-			revToExtract = dataFile.getLocalRevision(revision);
-		} else {
-			revToExtract = localRevision;
-		}
-		dataFile.content(revToExtract, sink, true);
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgCatCommand.java	Wed Feb 16 20:47:56 2011 +0100
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011 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 static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
+import static org.tmatesoft.hg.repo.HgRepository.TIP;
+
+import java.io.FileNotFoundException;
+
+import org.tmatesoft.hg.repo.HgDataFile;
+import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.util.ByteChannel;
+
+/**
+ * Command to obtain content of a file, 'hg cat' counterpart. 
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class HgCatCommand {
+
+	private final HgRepository repo;
+	private Path file;
+	private int localRevision = TIP;
+	private Nodeid revision;
+
+	public HgCatCommand(HgRepository hgRepo) {
+		repo = hgRepo;
+	}
+
+	public HgCatCommand file(Path fname) {
+		file = fname;
+		return this;
+	}
+
+	// rev can't be WORKING_COPY (if allowed, need to implement in #execute())
+	public HgCatCommand revision(int rev) {
+		localRevision = rev;
+		revision = null;
+		return this;
+	}
+	
+	public HgCatCommand revision(Nodeid nodeid) {
+		revision = nodeid;
+		localRevision = BAD_REVISION;
+		return this;
+	}
+
+	public void execute(ByteChannel sink) throws Exception /*TODO own exception type*/ {
+		if (localRevision == BAD_REVISION && revision == null) {
+			throw new IllegalArgumentException("Either local file revision number or nodeid shall be specified");
+		}
+		if (file == null) {
+			throw new IllegalArgumentException("Name of the file is missing");
+		}
+		if (sink == null) {
+			throw new IllegalArgumentException();
+		}
+		HgDataFile dataFile = repo.getFileNode(file);
+		if (!dataFile.exists()) {
+			throw new FileNotFoundException();
+		}
+		int revToExtract;
+		if (revision != null) {
+			revToExtract = dataFile.getLocalRevision(revision);
+		} else {
+			revToExtract = localRevision;
+		}
+		dataFile.content(revToExtract, sink, true);
+	}
+}
--- a/src/org/tmatesoft/hg/core/HgChangeset.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/src/org/tmatesoft/hg/core/HgChangeset.java	Wed Feb 16 20:47:56 2011 +0100
@@ -20,7 +20,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.tmatesoft.hg.core.LogCommand.FileRevision;
+import org.tmatesoft.hg.core.HgLogCommand.FileRevision;
 import org.tmatesoft.hg.repo.HgChangelog.Changeset;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgStatusCollector;
@@ -28,7 +28,8 @@
 
 
 /**
- * TODO rename to Changeset along with original Changeset moved to .repo and renamed to HgChangeset?
+ * Record in the Mercurial changelog, describing single commit.
+ * 
  * Not thread-safe, don't try to read from different threads
  * 
  * @author Artem Tikhomirov
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgLogCommand.java	Wed Feb 16 20:47:56 2011 +0100
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2011 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 static org.tmatesoft.hg.repo.HgRepository.TIP;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.tmatesoft.hg.repo.HgChangelog.Changeset;
+import org.tmatesoft.hg.repo.HgChangelog;
+import org.tmatesoft.hg.repo.HgDataFile;
+import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgStatusCollector;
+import org.tmatesoft.hg.util.PathPool;
+
+
+/**
+ * Access to changelog, 'hg log' command counterpart.
+ * 
+ * <pre>
+ * Usage:
+ *   new LogCommand().limit(20).branch("maintenance-2.1").user("me").execute(new MyHandler());
+ * </pre>
+ * Not thread-safe (each thread has to use own {@link HgLogCommand} instance).
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class HgLogCommand implements HgChangelog.Inspector {
+
+	private final HgRepository repo;
+	private Set<String> users;
+	private Set<String> branches;
+	private int limit = 0, count = 0;
+	private int startRev = 0, endRev = TIP;
+	private Handler delegate;
+	private Calendar date;
+	private Path file;
+	private boolean followHistory; // makes sense only when file != null
+	private HgChangeset changeset;
+	
+	public HgLogCommand(HgRepository hgRepo) {
+		repo = hgRepo;
+	}
+
+	/**
+	 * Limit search to specified user. Multiple user names may be specified.
+	 * @param user - full or partial name of the user, case-insensitive, non-null.
+	 * @return <code>this</code> instance for convenience
+	 */
+	public HgLogCommand user(String user) {
+		if (user == null) {
+			throw new IllegalArgumentException();
+		}
+		if (users == null) {
+			users = new TreeSet<String>();
+		}
+		users.add(user.toLowerCase());
+		return this;
+	}
+
+	/**
+	 * Limit search to specified branch. Multiple branch specification possible (changeset from any of these 
+	 * would be included in result). If unspecified, all branches are considered.
+	 * @param branch - branch name, case-sensitive, non-null.
+	 * @return <code>this</code> instance for convenience
+	 */
+	public HgLogCommand branch(String branch) {
+		if (branch == null) {
+			throw new IllegalArgumentException();
+		}
+		if (branches == null) {
+			branches = new TreeSet<String>();
+		}
+		branches.add(branch);
+		return this;
+	}
+	
+	// limit search to specific date
+	// multiple?
+	public HgLogCommand date(Calendar date) {
+		this.date = date;
+		// FIXME implement
+		// isSet(field) - false => don't use in detection of 'same date'
+		throw HgRepository.notImplemented();
+	}
+	
+	/**
+	 * 
+	 * @param num - number of changeset to produce. Pass 0 to clear the limit. 
+	 * @return <code>this</code> instance for convenience
+	 */
+	public HgLogCommand limit(int num) {
+		limit = num;
+		return this;
+	}
+
+	/**
+	 * Limit to specified subset of Changelog, [min(rev1,rev2), max(rev1,rev2)], inclusive.
+	 * Revision may be specified with {@link HgRepository#TIP}  
+	 * @param rev1
+	 * @param rev2
+	 * @return <code>this</code> instance for convenience
+	 */
+	public HgLogCommand range(int rev1, int rev2) {
+		if (rev1 != TIP && rev2 != TIP) {
+			startRev = rev2 < rev1 ? rev2 : rev1;
+			endRev = startRev == rev2 ? rev1 : rev2;
+		} else if (rev1 == TIP && rev2 != TIP) {
+			startRev = rev2;
+			endRev = rev1;
+		} else {
+			startRev = rev1;
+			endRev = rev2;
+		}
+		return this;
+	}
+	
+	/**
+	 * Visit history of a given file only.
+	 * @param file path relative to repository root. Pass <code>null</code> to reset.
+	 * @param followCopyRename true to report changesets of the original file(-s), if copy/rename ever occured to the file. 
+	 */
+	public HgLogCommand file(Path file, boolean followCopyRename) {
+		// multiple? Bad idea, would need to include extra method into Handler to tell start of next file
+		this.file = file;
+		followHistory = followCopyRename;
+		return this;
+	}
+
+	/**
+	 * Similar to {@link #execute(org.tmatesoft.hg.repo.Changeset.Inspector)}, collects and return result as a list.
+	 */
+	public List<HgChangeset> execute() {
+		CollectHandler collector = new CollectHandler();
+		execute(collector);
+		return collector.getChanges();
+	}
+
+	/**
+	 * 
+	 * @param inspector
+	 * @throws IllegalArgumentException when inspector argument is null
+	 * @throws ConcurrentModificationException if this log command instance is already running
+	 */
+	public void execute(Handler handler) {
+		if (handler == null) {
+			throw new IllegalArgumentException();
+		}
+		if (delegate != null) {
+			throw new ConcurrentModificationException();
+		}
+		try {
+			delegate = handler;
+			count = 0;
+			changeset = new HgChangeset(new HgStatusCollector(repo), new PathPool(repo.getPathHelper()));
+			if (file == null) {
+				repo.getChangelog().range(startRev, endRev, this);
+			} else {
+				HgDataFile fileNode = repo.getFileNode(file);
+				fileNode.history(startRev, endRev, this);
+				if (fileNode.isCopy()) {
+					// even if we do not follow history, report file rename
+					do {
+						if (handler instanceof FileHistoryHandler) {
+							FileRevision src = new FileRevision(repo, fileNode.getCopySourceRevision(), fileNode.getCopySourceName());
+							FileRevision dst = new FileRevision(repo, fileNode.getRevision(0), fileNode.getPath());
+							((FileHistoryHandler) handler).copy(src, dst);
+						}
+						if (limit > 0 && count >= limit) {
+							// if limit reach, follow is useless.
+							break;
+						}
+						if (followHistory) {
+							fileNode = repo.getFileNode(fileNode.getCopySourceName());
+							fileNode.history(this);
+						}
+					} while (followHistory && fileNode.isCopy());
+				}
+			}
+		} finally {
+			delegate = null;
+			changeset = null;
+		}
+	}
+
+	//
+	
+	public void next(int revisionNumber, Nodeid nodeid, Changeset cset) {
+		if (limit > 0 && count >= limit) {
+			return;
+		}
+		if (branches != null && !branches.contains(cset.branch())) {
+			return;
+		}
+		if (users != null) {
+			String csetUser = cset.user().toLowerCase();
+			boolean found = false;
+			for (String u : users) {
+				if (csetUser.indexOf(u) != -1) {
+					found = true;
+					break;
+				}
+			}
+			if (!found) {
+				return;
+			}
+		}
+		if (date != null) {
+			// FIXME
+		}
+		count++;
+		changeset.init(revisionNumber, nodeid, cset);
+		delegate.next(changeset);
+	}
+
+	public interface Handler {
+		/**
+		 * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy.
+		 */
+		void next(HgChangeset changeset);
+	}
+	
+	/**
+	 * When {@link HgLogCommand} is executed against file, handler passed to {@link HgLogCommand#execute(Handler)} may optionally
+	 * implement this interface to get information about file renames. Method {@link #copy(FileRevision, FileRevision)} 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(FileRevision, FileRevision)} 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 Handler {
+		// XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them?
+		void copy(FileRevision from, FileRevision to);
+	}
+	
+	public static class CollectHandler implements Handler {
+		private final List<HgChangeset> result = new LinkedList<HgChangeset>();
+
+		public List<HgChangeset> getChanges() {
+			return Collections.unmodifiableList(result);
+		}
+
+		public void next(HgChangeset changeset) {
+			result.add(changeset.clone());
+		}
+	}
+
+	public static final class FileRevision {
+		private final HgRepository repo;
+		private final Nodeid revision;
+		private final Path path;
+		
+		/*package-local*/FileRevision(HgRepository hgRepo, Nodeid rev, Path p) {
+			if (hgRepo == null || rev == null || p == null) {
+				throw new IllegalArgumentException();
+			}
+			repo = hgRepo;
+			revision = rev;
+			path = p;
+		}
+		
+		public Path getPath() {
+			return path;
+		}
+		public Nodeid getRevision() {
+			return revision;
+		}
+		public byte[] getContent() {
+			// XXX Content wrapper, to allow formats other than byte[], e.g. Stream, DataAccess, etc?
+			return repo.getFileNode(path).content(revision);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgManifestCommand.java	Wed Feb 16 20:47:56 2011 +0100
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2011 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 static org.tmatesoft.hg.repo.HgRepository.TIP;
+
+import java.util.ConcurrentModificationException;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.tmatesoft.hg.core.HgLogCommand.FileRevision;
+import org.tmatesoft.hg.repo.HgManifest;
+import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.util.PathPool;
+
+
+/**
+ * Gives access to list of files in each revision (Mercurial manifest information), 'hg manifest' counterpart.
+ *  
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class HgManifestCommand {
+	
+	private final HgRepository repo;
+	private Path.Matcher matcher;
+	private int startRev = 0, endRev = TIP;
+	private Handler visitor;
+	private boolean needDirs = false;
+	
+	private final Mediator mediator = new Mediator();
+
+	public HgManifestCommand(HgRepository hgRepo) {
+		repo = hgRepo;
+	}
+
+	public HgManifestCommand range(int rev1, int rev2) {
+		// if manifest range is different from that of changelog, need conversion utils (external?)
+		throw HgRepository.notImplemented();
+	}
+	
+	public HgManifestCommand revision(int rev) {
+		startRev = endRev = rev;
+		return this;
+	}
+	
+	public HgManifestCommand dirs(boolean include) {
+		// XXX whether directories with directories only are include or not
+		// now lists only directories with files
+		needDirs = include;
+		return this;
+	}
+	
+	/**
+	 * Limit manifest walk to a subset of files. 
+	 * @param pathMatcher - filter, pass <code>null</code> to clear.
+	 * @return <code>this</code> instance for convenience
+	 */
+	public HgManifestCommand match(Path.Matcher pathMatcher) {
+		matcher = pathMatcher;
+		return this;
+	}
+	
+	public void walk(Handler handler) {
+		if (handler == null) {
+			throw new IllegalArgumentException();
+		}
+		if (visitor != null) {
+			throw new ConcurrentModificationException();
+		}
+		try {
+			visitor = handler;
+			mediator.start();
+			repo.getManifest().walk(startRev, endRev, mediator);
+		} finally {
+			mediator.done();
+			visitor = null;
+		}
+	}
+
+	/**
+	 * Callback to walk file/directory tree of a revision
+	 */
+	public interface Handler {
+		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(FileRevision fileRevision); // XXX allow to check p is invalid (df.exists())
+		void end(Nodeid manifestRevision);
+	}
+
+	// I'd rather let RepositoryTreeWalker implement HgManifest.Inspector directly, but this pollutes API alot
+	private class Mediator implements HgManifest.Inspector {
+		private PathPool pathPool;
+		private List<FileRevision> manifestContent;
+		private Nodeid manifestNodeid;
+		
+		public void start() {
+			pathPool = new PathPool(repo.getPathHelper());
+		}
+		
+		public void done() {
+			manifestContent = null;
+			pathPool = null;
+		}
+	
+		public boolean begin(int revision, Nodeid nid) {
+			if (needDirs && manifestContent == null) {
+				manifestContent = new LinkedList<FileRevision>();
+			}
+			visitor.begin(manifestNodeid = nid);
+			return true;
+		}
+		public boolean end(int revision) {
+			if (needDirs) {
+				LinkedHashMap<Path, LinkedList<FileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<FileRevision>>();
+				for (FileRevision fr : manifestContent) {
+					Path filePath = fr.getPath();
+					Path dirPath = pathPool.parent(filePath);
+					LinkedList<FileRevision> revs = breakDown.get(dirPath);
+					if (revs == null) {
+						revs = new LinkedList<FileRevision>();
+						breakDown.put(dirPath, revs);
+					}
+					revs.addLast(fr);
+				}
+				for (Path dir : breakDown.keySet()) {
+					visitor.dir(dir);
+					for (FileRevision fr : breakDown.get(dir)) {
+						visitor.file(fr);
+					}
+				}
+				manifestContent.clear();
+			}
+			visitor.end(manifestNodeid);
+			manifestNodeid = null;
+			return true;
+		}
+		public boolean next(Nodeid nid, String fname, String flags) {
+			Path p = pathPool.path(fname);
+			if (matcher != null && !matcher.accept(p)) {
+				return true;
+			}
+			FileRevision fr = new FileRevision(repo, nid, p);
+			if (needDirs) {
+				manifestContent.add(fr);
+			} else {
+				visitor.file(fr);
+			}
+			return true;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgRepoFacade.java	Wed Feb 16 20:47:56 2011 +0100
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2011 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 java.io.File;
+
+import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgLookup;
+
+/**
+ * Starting point for the library.
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class HgRepoFacade {
+	private HgRepository repo;
+
+	public HgRepoFacade() {
+	}
+
+	public boolean init() throws Exception /*FIXME RepoInitException*/ {
+		repo = new HgLookup().detectFromWorkingDir();
+		return repo != null && !repo.isInvalid();
+	}
+	
+	public boolean initFrom(File repoLocation) throws Exception {
+		repo = new HgLookup().detect(repoLocation.getCanonicalPath());
+		return repo != null && !repo.isInvalid();
+	}
+	
+	public HgRepository getRepository() {
+		if (repo == null) {
+			throw new IllegalStateException("Call any of #init*() methods first first");
+		}
+		return repo;
+	}
+
+	public HgLogCommand createLogCommand() {
+		return new HgLogCommand(repo/*, getCommandContext()*/);
+	}
+
+	public HgStatusCommand createStatusCommand() {
+		return new HgStatusCommand(repo/*, getCommandContext()*/);
+	}
+
+	public HgCatCommand createCatCommand() {
+		return new HgCatCommand(repo);
+	}
+
+	public HgManifestCommand createManifestCommand() {
+		return new HgManifestCommand(repo);
+	}
+}
--- a/src/org/tmatesoft/hg/core/HgStatus.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/src/org/tmatesoft/hg/core/HgStatus.java	Wed Feb 16 20:47:56 2011 +0100
@@ -21,6 +21,12 @@
 import org.tmatesoft.hg.internal.ChangelogHelper;
 import org.tmatesoft.hg.repo.HgChangelog.Changeset;
 
+/**
+ * Repository file status and extra handy information.
+ *
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
 public class HgStatus {
 
 	public enum Kind {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/core/HgStatusCommand.java	Wed Feb 16 20:47:56 2011 +0100
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2011 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 static org.tmatesoft.hg.core.HgStatus.Kind.*;
+import static org.tmatesoft.hg.repo.HgRepository.*;
+
+import java.util.ConcurrentModificationException;
+
+import org.tmatesoft.hg.core.Path.Matcher;
+import org.tmatesoft.hg.internal.ChangelogHelper;
+import org.tmatesoft.hg.repo.HgRepository;
+import org.tmatesoft.hg.repo.HgStatusCollector;
+import org.tmatesoft.hg.repo.HgStatusInspector;
+import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector;
+
+/**
+ * Command to obtain file status information, 'hg status' counterpart. 
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class HgStatusCommand {
+	private final HgRepository repo;
+
+	private int startRevision = TIP;
+	private int endRevision = WORKING_COPY; 
+	private boolean visitSubRepo = true;
+	
+	private final Mediator mediator = new Mediator();
+
+	public HgStatusCommand(HgRepository hgRepo) { 
+		repo = hgRepo;
+		defaults();
+	}
+
+	public HgStatusCommand defaults() {
+		final Mediator m = mediator;
+		m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true;
+		m.needCopies = m.needClean = m.needIgnored = false;
+		return this;
+	}
+	public HgStatusCommand all() {
+		final Mediator m = mediator;
+		m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true;
+		m.needCopies = m.needClean = m.needIgnored = true;
+		return this;
+	}
+	
+
+	public HgStatusCommand modified(boolean include) {
+		mediator.needModified = include;
+		return this;
+	}
+	public HgStatusCommand added(boolean include) {
+		mediator.needAdded = include;
+		return this;
+	}
+	public HgStatusCommand removed(boolean include) {
+		mediator.needRemoved = include;
+		return this;
+	}
+	public HgStatusCommand deleted(boolean include) {
+		mediator.needMissing = include;
+		return this;
+	}
+	public HgStatusCommand unknown(boolean include) {
+		mediator.needUnknown = include;
+		return this;
+	}
+	public HgStatusCommand clean(boolean include) {
+		mediator.needClean = include;
+		return this;
+	}
+	public HgStatusCommand ignored(boolean include) {
+		mediator.needIgnored = include;
+		return this;
+	}
+	
+	/**
+	 * if set, either base:revision or base:workingdir
+	 * to unset, pass {@link HgRepository#TIP} or {@link HgRepository#BAD_REVISION}
+	 * @param revision
+	 * @return
+	 */
+	
+	public HgStatusCommand base(int revision) {
+		if (revision == WORKING_COPY) {
+			throw new IllegalArgumentException();
+		}
+		if (revision == BAD_REVISION) {
+			revision = TIP;
+		}
+		startRevision = revision;
+		return this;
+	}
+	
+	/**
+	 * Revision without base == --change
+	 * Pass {@link HgRepository#WORKING_COPY} or {@link HgRepository#BAD_REVISION} to reset
+	 * @param revision
+	 * @return
+	 */
+	public HgStatusCommand revision(int revision) {
+		if (revision == BAD_REVISION) {
+			revision = WORKING_COPY;
+		}
+		// XXX negative values, except for predefined constants, shall throw IAE.
+		endRevision = revision;
+		return this;
+	}
+	
+	// pass null to reset
+	public HgStatusCommand match(Path.Matcher pathMatcher) {
+		mediator.matcher = pathMatcher;
+		return this;
+	}
+
+	public HgStatusCommand subrepo(boolean visit) {
+		visitSubRepo  = visit;
+		throw HgRepository.notImplemented();
+	}
+
+	/**
+	 * Perform status operation according to parameters set.
+	 *  
+	 * @param handler callback to get status information
+	 * @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(Handler statusHandler) {
+		if (statusHandler == null) {
+			throw new IllegalArgumentException();
+		}
+		if (mediator.busy()) {
+			throw new ConcurrentModificationException();
+		}
+		HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext
+//		PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext
+		try {
+			// XXX if I need a rough estimation (for ProgressMonitor) of number of work units,
+			// I may use number of files in either rev1 or rev2 manifest edition
+			mediator.start(statusHandler, new ChangelogHelper(repo, startRevision));
+			if (endRevision == WORKING_COPY) {
+				HgWorkingCopyStatusCollector wcsc = new HgWorkingCopyStatusCollector(repo);
+				wcsc.setBaseRevisionCollector(sc);
+				wcsc.walk(startRevision, mediator);
+			} else {
+				if (startRevision == TIP) {
+					sc.change(endRevision, mediator);
+				} else {
+					sc.walk(startRevision, endRevision, mediator);
+				}
+			}
+		} finally {
+			mediator.done();
+		}
+	}
+
+	public interface Handler {
+		void handleStatus(HgStatus s);
+	}
+
+	private class Mediator implements HgStatusInspector {
+		boolean needModified;
+		boolean needAdded;
+		boolean needRemoved;
+		boolean needUnknown;
+		boolean needMissing;
+		boolean needClean;
+		boolean needIgnored;
+		boolean needCopies;
+		Matcher matcher;
+		Handler handler;
+		private ChangelogHelper logHelper;
+
+		Mediator() {
+		}
+		
+		public void start(Handler h, ChangelogHelper changelogHelper) {
+			handler = h;
+			logHelper = changelogHelper;
+		}
+
+		public void done() {
+			handler = null;
+			logHelper = null;
+		}
+		
+		public boolean busy() {
+			return handler != null;
+		}
+
+		public void modified(Path fname) {
+			if (needModified) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Modified, fname, logHelper));
+				}
+			}
+		}
+		public void added(Path fname) {
+			if (needAdded) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Added, fname, logHelper));
+				}
+			}
+		}
+		public void removed(Path fname) {
+			if (needRemoved) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Removed, fname, logHelper));
+				}
+			}
+		}
+		public void copied(Path fnameOrigin, Path fnameAdded) {
+			if (needCopies) {
+				if (matcher == null || matcher.accept(fnameAdded)) {
+					handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper));
+				}
+			}
+		}
+		public void missing(Path fname) {
+			if (needMissing) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Missing, fname, logHelper));
+				}
+			}
+		}
+		public void unknown(Path fname) {
+			if (needUnknown) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Unknown, fname, logHelper));
+				}
+			}
+		}
+		public void clean(Path fname) {
+			if (needClean) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Clean, fname, logHelper));
+				}
+			}
+		}
+		public void ignored(Path fname) {
+			if (needIgnored) {
+				if (matcher == null || matcher.accept(fname)) {
+					handler.handleStatus(new HgStatus(Ignored, fname, logHelper));
+				}
+			}
+		}
+	}
+}
--- a/src/org/tmatesoft/hg/core/LogCommand.java	Wed Feb 16 20:33:31 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) 2011 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 static org.tmatesoft.hg.repo.HgRepository.TIP;
-
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.tmatesoft.hg.repo.HgChangelog.Changeset;
-import org.tmatesoft.hg.repo.HgChangelog;
-import org.tmatesoft.hg.repo.HgDataFile;
-import org.tmatesoft.hg.repo.HgRepository;
-import org.tmatesoft.hg.repo.HgStatusCollector;
-import org.tmatesoft.hg.util.PathPool;
-
-
-/**
- * <pre>
- *   new LogCommand().limit(20).branch("maintenance-2.1").user("me").execute(new MyHandler());
- * </pre>
- * Not thread-safe (each thread has to use own {@link LogCommand} instance).
- * 
- * @author Artem Tikhomirov
- * @author TMate Software Ltd.
- */
-public class LogCommand implements HgChangelog.Inspector {
-
-	private final HgRepository repo;
-	private Set<String> users;
-	private Set<String> branches;
-	private int limit = 0, count = 0;
-	private int startRev = 0, endRev = TIP;
-	private Handler delegate;
-	private Calendar date;
-	private Path file;
-	private boolean followHistory; // makes sense only when file != null
-	private HgChangeset changeset;
-	
-	public LogCommand(HgRepository hgRepo) {
-		repo = hgRepo;
-	}
-
-	/**
-	 * Limit search to specified user. Multiple user names may be specified.
-	 * @param user - full or partial name of the user, case-insensitive, non-null.
-	 * @return <code>this</code> instance for convenience
-	 */
-	public LogCommand user(String user) {
-		if (user == null) {
-			throw new IllegalArgumentException();
-		}
-		if (users == null) {
-			users = new TreeSet<String>();
-		}
-		users.add(user.toLowerCase());
-		return this;
-	}
-
-	/**
-	 * Limit search to specified branch. Multiple branch specification possible (changeset from any of these 
-	 * would be included in result). If unspecified, all branches are considered.
-	 * @param branch - branch name, case-sensitive, non-null.
-	 * @return <code>this</code> instance for convenience
-	 */
-	public LogCommand branch(String branch) {
-		if (branch == null) {
-			throw new IllegalArgumentException();
-		}
-		if (branches == null) {
-			branches = new TreeSet<String>();
-		}
-		branches.add(branch);
-		return this;
-	}
-	
-	// limit search to specific date
-	// multiple?
-	public LogCommand date(Calendar date) {
-		this.date = date;
-		// FIXME implement
-		// isSet(field) - false => don't use in detection of 'same date'
-		throw HgRepository.notImplemented();
-	}
-	
-	/**
-	 * 
-	 * @param num - number of changeset to produce. Pass 0 to clear the limit. 
-	 * @return <code>this</code> instance for convenience
-	 */
-	public LogCommand limit(int num) {
-		limit = num;
-		return this;
-	}
-
-	/**
-	 * Limit to specified subset of Changelog, [min(rev1,rev2), max(rev1,rev2)], inclusive.
-	 * Revision may be specified with {@link HgRepository#TIP}  
-	 * @param rev1
-	 * @param rev2
-	 * @return <code>this</code> instance for convenience
-	 */
-	public LogCommand range(int rev1, int rev2) {
-		if (rev1 != TIP && rev2 != TIP) {
-			startRev = rev2 < rev1 ? rev2 : rev1;
-			endRev = startRev == rev2 ? rev1 : rev2;
-		} else if (rev1 == TIP && rev2 != TIP) {
-			startRev = rev2;
-			endRev = rev1;
-		} else {
-			startRev = rev1;
-			endRev = rev2;
-		}
-		return this;
-	}
-	
-	/**
-	 * Visit history of a given file only.
-	 * @param file path relative to repository root. Pass <code>null</code> to reset.
-	 * @param followCopyRename true to report changesets of the original file(-s), if copy/rename ever occured to the file. 
-	 */
-	public LogCommand file(Path file, boolean followCopyRename) {
-		// multiple? Bad idea, would need to include extra method into Handler to tell start of next file
-		this.file = file;
-		followHistory = followCopyRename;
-		return this;
-	}
-
-	/**
-	 * Similar to {@link #execute(org.tmatesoft.hg.repo.Changeset.Inspector)}, collects and return result as a list.
-	 */
-	public List<HgChangeset> execute() {
-		CollectHandler collector = new CollectHandler();
-		execute(collector);
-		return collector.getChanges();
-	}
-
-	/**
-	 * 
-	 * @param inspector
-	 * @throws IllegalArgumentException when inspector argument is null
-	 * @throws ConcurrentModificationException if this log command instance is already running
-	 */
-	public void execute(Handler handler) {
-		if (handler == null) {
-			throw new IllegalArgumentException();
-		}
-		if (delegate != null) {
-			throw new ConcurrentModificationException();
-		}
-		try {
-			delegate = handler;
-			count = 0;
-			changeset = new HgChangeset(new HgStatusCollector(repo), new PathPool(repo.getPathHelper()));
-			if (file == null) {
-				repo.getChangelog().range(startRev, endRev, this);
-			} else {
-				HgDataFile fileNode = repo.getFileNode(file);
-				fileNode.history(startRev, endRev, this);
-				if (fileNode.isCopy()) {
-					// even if we do not follow history, report file rename
-					do {
-						if (handler instanceof FileHistoryHandler) {
-							FileRevision src = new FileRevision(repo, fileNode.getCopySourceRevision(), fileNode.getCopySourceName());
-							FileRevision dst = new FileRevision(repo, fileNode.getRevision(0), fileNode.getPath());
-							((FileHistoryHandler) handler).copy(src, dst);
-						}
-						if (limit > 0 && count >= limit) {
-							// if limit reach, follow is useless.
-							break;
-						}
-						if (followHistory) {
-							fileNode = repo.getFileNode(fileNode.getCopySourceName());
-							fileNode.history(this);
-						}
-					} while (followHistory && fileNode.isCopy());
-				}
-			}
-		} finally {
-			delegate = null;
-			changeset = null;
-		}
-	}
-
-	//
-	
-	public void next(int revisionNumber, Nodeid nodeid, Changeset cset) {
-		if (limit > 0 && count >= limit) {
-			return;
-		}
-		if (branches != null && !branches.contains(cset.branch())) {
-			return;
-		}
-		if (users != null) {
-			String csetUser = cset.user().toLowerCase();
-			boolean found = false;
-			for (String u : users) {
-				if (csetUser.indexOf(u) != -1) {
-					found = true;
-					break;
-				}
-			}
-			if (!found) {
-				return;
-			}
-		}
-		if (date != null) {
-			// FIXME
-		}
-		count++;
-		changeset.init(revisionNumber, nodeid, cset);
-		delegate.next(changeset);
-	}
-
-	public interface Handler {
-		/**
-		 * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy.
-		 */
-		void next(HgChangeset changeset);
-	}
-	
-	/**
-	 * When {@link LogCommand} is executed against file, handler passed to {@link LogCommand#execute(Handler)} may optionally
-	 * implement this interface to get information about file renames. Method {@link #copy(FileRevision, FileRevision)} would
-	 * get invoked prior any changeset of the original file (if file history being followed) is reported via {@link #next(HgChangeset)}.
-	 * 
-	 * For {@link LogCommand#file(Path, boolean)} with renamed file path and follow argument set to false, 
-	 * {@link #copy(FileRevision, FileRevision)} 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 Handler {
-		// XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them?
-		void copy(FileRevision from, FileRevision to);
-	}
-	
-	public static class CollectHandler implements Handler {
-		private final List<HgChangeset> result = new LinkedList<HgChangeset>();
-
-		public List<HgChangeset> getChanges() {
-			return Collections.unmodifiableList(result);
-		}
-
-		public void next(HgChangeset changeset) {
-			result.add(changeset.clone());
-		}
-	}
-
-	public static final class FileRevision {
-		private final HgRepository repo;
-		private final Nodeid revision;
-		private final Path path;
-		
-		/*package-local*/FileRevision(HgRepository hgRepo, Nodeid rev, Path p) {
-			if (hgRepo == null || rev == null || p == null) {
-				throw new IllegalArgumentException();
-			}
-			repo = hgRepo;
-			revision = rev;
-			path = p;
-		}
-		
-		public Path getPath() {
-			return path;
-		}
-		public Nodeid getRevision() {
-			return revision;
-		}
-		public byte[] getContent() {
-			// XXX Content wrapper, to allow formats other than byte[], e.g. Stream, DataAccess, etc?
-			return repo.getFileNode(path).content(revision);
-		}
-	}
-}
--- a/src/org/tmatesoft/hg/core/RepositoryFacade.java	Wed Feb 16 20:33:31 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2011 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 java.io.File;
-
-import org.tmatesoft.hg.repo.HgRepository;
-import org.tmatesoft.hg.repo.HgLookup;
-
-/**
- *
- * @author Artem Tikhomirov
- * @author TMate Software Ltd.
- */
-public class RepositoryFacade {
-	private HgRepository repo;
-
-	public RepositoryFacade() {
-	}
-
-	public boolean init() throws Exception /*FIXME RepoInitException*/ {
-		repo = new HgLookup().detectFromWorkingDir();
-		return repo != null && !repo.isInvalid();
-	}
-	
-	public boolean initFrom(File repoLocation) throws Exception {
-		repo = new HgLookup().detect(repoLocation.getCanonicalPath());
-		return repo != null && !repo.isInvalid();
-	}
-	
-	public HgRepository getRepository() {
-		if (repo == null) {
-			throw new IllegalStateException("Call any of #init*() methods first first");
-		}
-		return repo;
-	}
-
-	public LogCommand createLogCommand() {
-		return new LogCommand(repo/*, getCommandContext()*/);
-	}
-
-	public StatusCommand createStatusCommand() {
-		return new StatusCommand(repo/*, getCommandContext()*/);
-	}
-
-	public CatCommand createCatCommand() {
-		return new CatCommand(repo);
-	}
-
-	public RepositoryTreeWalker createManifestCommand() {
-		return new RepositoryTreeWalker(repo);
-	}
-}
--- a/src/org/tmatesoft/hg/core/RepositoryTreeWalker.java	Wed Feb 16 20:33:31 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-/*
- * Copyright (c) 2011 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 static org.tmatesoft.hg.repo.HgRepository.TIP;
-
-import java.util.ConcurrentModificationException;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.tmatesoft.hg.core.LogCommand.FileRevision;
-import org.tmatesoft.hg.repo.HgManifest;
-import org.tmatesoft.hg.repo.HgRepository;
-import org.tmatesoft.hg.util.PathPool;
-
-
-/**
- *
- * @author Artem Tikhomirov
- * @author TMate Software Ltd.
- */
-public class RepositoryTreeWalker {
-	
-	private final HgRepository repo;
-	private Path.Matcher matcher;
-	private int startRev = 0, endRev = TIP;
-	private Handler visitor;
-	private boolean needDirs = false;
-	
-	private final Mediator mediator = new Mediator();
-
-	public RepositoryTreeWalker(HgRepository hgRepo) {
-		repo = hgRepo;
-	}
-
-	public RepositoryTreeWalker range(int rev1, int rev2) {
-		// if manifest range is different from that of changelog, need conversion utils (external?)
-		throw HgRepository.notImplemented();
-	}
-	
-	public RepositoryTreeWalker revision(int rev) {
-		startRev = endRev = rev;
-		return this;
-	}
-	
-	public RepositoryTreeWalker dirs(boolean include) {
-		// XXX whether directories with directories only are include or not
-		// now lists only directories with files
-		needDirs = include;
-		return this;
-	}
-	
-	/**
-	 * Limit manifest walk to a subset of files. 
-	 * @param pathMatcher - filter, pass <code>null</code> to clear.
-	 * @return <code>this</code> instance for convenience
-	 */
-	public RepositoryTreeWalker match(Path.Matcher pathMatcher) {
-		matcher = pathMatcher;
-		return this;
-	}
-	
-	public void walk(Handler handler) {
-		if (handler == null) {
-			throw new IllegalArgumentException();
-		}
-		if (visitor != null) {
-			throw new ConcurrentModificationException();
-		}
-		try {
-			visitor = handler;
-			mediator.start();
-			repo.getManifest().walk(startRev, endRev, mediator);
-		} finally {
-			mediator.done();
-			visitor = null;
-		}
-	}
-
-	/**
-	 * Callback to walk file/directory tree of a revision
-	 */
-	public interface Handler {
-		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(FileRevision fileRevision); // XXX allow to check p is invalid (df.exists())
-		void end(Nodeid manifestRevision);
-	}
-
-	// I'd rather let RepositoryTreeWalker implement HgManifest.Inspector directly, but this pollutes API alot
-	private class Mediator implements HgManifest.Inspector {
-		private PathPool pathPool;
-		private List<FileRevision> manifestContent;
-		private Nodeid manifestNodeid;
-		
-		public void start() {
-			pathPool = new PathPool(repo.getPathHelper());
-		}
-		
-		public void done() {
-			manifestContent = null;
-			pathPool = null;
-		}
-	
-		public boolean begin(int revision, Nodeid nid) {
-			if (needDirs && manifestContent == null) {
-				manifestContent = new LinkedList<FileRevision>();
-			}
-			visitor.begin(manifestNodeid = nid);
-			return true;
-		}
-		public boolean end(int revision) {
-			if (needDirs) {
-				LinkedHashMap<Path, LinkedList<FileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<FileRevision>>();
-				for (FileRevision fr : manifestContent) {
-					Path filePath = fr.getPath();
-					Path dirPath = pathPool.parent(filePath);
-					LinkedList<FileRevision> revs = breakDown.get(dirPath);
-					if (revs == null) {
-						revs = new LinkedList<FileRevision>();
-						breakDown.put(dirPath, revs);
-					}
-					revs.addLast(fr);
-				}
-				for (Path dir : breakDown.keySet()) {
-					visitor.dir(dir);
-					for (FileRevision fr : breakDown.get(dir)) {
-						visitor.file(fr);
-					}
-				}
-				manifestContent.clear();
-			}
-			visitor.end(manifestNodeid);
-			manifestNodeid = null;
-			return true;
-		}
-		public boolean next(Nodeid nid, String fname, String flags) {
-			Path p = pathPool.path(fname);
-			if (matcher != null && !matcher.accept(p)) {
-				return true;
-			}
-			FileRevision fr = new FileRevision(repo, nid, p);
-			if (needDirs) {
-				manifestContent.add(fr);
-			} else {
-				visitor.file(fr);
-			}
-			return true;
-		}
-	}
-}
--- a/src/org/tmatesoft/hg/core/StatusCommand.java	Wed Feb 16 20:33:31 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-/*
- * Copyright (c) 2011 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 static org.tmatesoft.hg.core.HgStatus.Kind.*;
-import static org.tmatesoft.hg.repo.HgRepository.*;
-
-import java.util.ConcurrentModificationException;
-
-import org.tmatesoft.hg.core.Path.Matcher;
-import org.tmatesoft.hg.internal.ChangelogHelper;
-import org.tmatesoft.hg.repo.HgRepository;
-import org.tmatesoft.hg.repo.HgStatusCollector;
-import org.tmatesoft.hg.repo.HgStatusInspector;
-import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector;
-
-/**
- *
- * @author Artem Tikhomirov
- * @author TMate Software Ltd.
- */
-public class StatusCommand {
-	private final HgRepository repo;
-
-	private int startRevision = TIP;
-	private int endRevision = WORKING_COPY; 
-	private boolean visitSubRepo = true;
-	
-	private final Mediator mediator = new Mediator();
-
-	public StatusCommand(HgRepository hgRepo) { 
-		repo = hgRepo;
-		defaults();
-	}
-
-	public StatusCommand defaults() {
-		final Mediator m = mediator;
-		m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true;
-		m.needCopies = m.needClean = m.needIgnored = false;
-		return this;
-	}
-	public StatusCommand all() {
-		final Mediator m = mediator;
-		m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true;
-		m.needCopies = m.needClean = m.needIgnored = true;
-		return this;
-	}
-	
-
-	public StatusCommand modified(boolean include) {
-		mediator.needModified = include;
-		return this;
-	}
-	public StatusCommand added(boolean include) {
-		mediator.needAdded = include;
-		return this;
-	}
-	public StatusCommand removed(boolean include) {
-		mediator.needRemoved = include;
-		return this;
-	}
-	public StatusCommand deleted(boolean include) {
-		mediator.needMissing = include;
-		return this;
-	}
-	public StatusCommand unknown(boolean include) {
-		mediator.needUnknown = include;
-		return this;
-	}
-	public StatusCommand clean(boolean include) {
-		mediator.needClean = include;
-		return this;
-	}
-	public StatusCommand ignored(boolean include) {
-		mediator.needIgnored = include;
-		return this;
-	}
-	
-	/**
-	 * if set, either base:revision or base:workingdir
-	 * to unset, pass {@link HgRepository#TIP} or {@link HgRepository#BAD_REVISION}
-	 * @param revision
-	 * @return
-	 */
-	
-	public StatusCommand base(int revision) {
-		if (revision == WORKING_COPY) {
-			throw new IllegalArgumentException();
-		}
-		if (revision == BAD_REVISION) {
-			revision = TIP;
-		}
-		startRevision = revision;
-		return this;
-	}
-	
-	/**
-	 * Revision without base == --change
-	 * Pass {@link HgRepository#WORKING_COPY} or {@link HgRepository#BAD_REVISION} to reset
-	 * @param revision
-	 * @return
-	 */
-	public StatusCommand revision(int revision) {
-		if (revision == BAD_REVISION) {
-			revision = WORKING_COPY;
-		}
-		// XXX negative values, except for predefined constants, shall throw IAE.
-		endRevision = revision;
-		return this;
-	}
-	
-	// pass null to reset
-	public StatusCommand match(Path.Matcher pathMatcher) {
-		mediator.matcher = pathMatcher;
-		return this;
-	}
-
-	public StatusCommand subrepo(boolean visit) {
-		visitSubRepo  = visit;
-		throw HgRepository.notImplemented();
-	}
-
-	/**
-	 * Perform status operation according to parameters set.
-	 *  
-	 * @param handler callback to get status information
-	 * @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(Handler statusHandler) {
-		if (statusHandler == null) {
-			throw new IllegalArgumentException();
-		}
-		if (mediator.busy()) {
-			throw new ConcurrentModificationException();
-		}
-		HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext
-//		PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext
-		try {
-			// XXX if I need a rough estimation (for ProgressMonitor) of number of work units,
-			// I may use number of files in either rev1 or rev2 manifest edition
-			mediator.start(statusHandler, new ChangelogHelper(repo, startRevision));
-			if (endRevision == WORKING_COPY) {
-				HgWorkingCopyStatusCollector wcsc = new HgWorkingCopyStatusCollector(repo);
-				wcsc.setBaseRevisionCollector(sc);
-				wcsc.walk(startRevision, mediator);
-			} else {
-				if (startRevision == TIP) {
-					sc.change(endRevision, mediator);
-				} else {
-					sc.walk(startRevision, endRevision, mediator);
-				}
-			}
-		} finally {
-			mediator.done();
-		}
-	}
-
-	public interface Handler {
-		void handleStatus(HgStatus s);
-	}
-
-	private class Mediator implements HgStatusInspector {
-		boolean needModified;
-		boolean needAdded;
-		boolean needRemoved;
-		boolean needUnknown;
-		boolean needMissing;
-		boolean needClean;
-		boolean needIgnored;
-		boolean needCopies;
-		Matcher matcher;
-		Handler handler;
-		private ChangelogHelper logHelper;
-
-		Mediator() {
-		}
-		
-		public void start(Handler h, ChangelogHelper changelogHelper) {
-			handler = h;
-			logHelper = changelogHelper;
-		}
-
-		public void done() {
-			handler = null;
-			logHelper = null;
-		}
-		
-		public boolean busy() {
-			return handler != null;
-		}
-
-		public void modified(Path fname) {
-			if (needModified) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Modified, fname, logHelper));
-				}
-			}
-		}
-		public void added(Path fname) {
-			if (needAdded) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Added, fname, logHelper));
-				}
-			}
-		}
-		public void removed(Path fname) {
-			if (needRemoved) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Removed, fname, logHelper));
-				}
-			}
-		}
-		public void copied(Path fnameOrigin, Path fnameAdded) {
-			if (needCopies) {
-				if (matcher == null || matcher.accept(fnameAdded)) {
-					handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper));
-				}
-			}
-		}
-		public void missing(Path fname) {
-			if (needMissing) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Missing, fname, logHelper));
-				}
-			}
-		}
-		public void unknown(Path fname) {
-			if (needUnknown) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Unknown, fname, logHelper));
-				}
-			}
-		}
-		public void clean(Path fname) {
-			if (needClean) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Clean, fname, logHelper));
-				}
-			}
-		}
-		public void ignored(Path fname) {
-			if (needIgnored) {
-				if (matcher == null || matcher.accept(fname)) {
-					handler.handleStatus(new HgStatus(Ignored, fname, logHelper));
-				}
-			}
-		}
-	}
-}
--- a/test/org/tmatesoft/hg/test/TestByteChannel.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/test/org/tmatesoft/hg/test/TestByteChannel.java	Wed Feb 16 20:47:56 2011 +0100
@@ -19,7 +19,7 @@
 import java.util.Arrays;
 
 import org.junit.Assert;
-import org.tmatesoft.hg.core.RepositoryFacade;
+import org.tmatesoft.hg.core.HgRepoFacade;
 import org.tmatesoft.hg.internal.ByteArrayChannel;
 import org.tmatesoft.hg.repo.HgDataFile;
 
@@ -31,7 +31,7 @@
 public class TestByteChannel {
 
 	public static void main(String[] args) throws Exception {
-		RepositoryFacade rf = new RepositoryFacade();
+		HgRepoFacade rf = new HgRepoFacade();
 		rf.init();
 		HgDataFile file = rf.getRepository().getFileNode("src/org/tmatesoft/hg/internal/KeywordFilter.java");
 		for (int i = file.getRevisionCount() - 1; i >= 0; i--) {
--- a/test/org/tmatesoft/hg/test/TestHistory.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/test/org/tmatesoft/hg/test/TestHistory.java	Wed Feb 16 20:47:56 2011 +0100
@@ -29,10 +29,10 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.tmatesoft.hg.core.HgChangeset;
-import org.tmatesoft.hg.core.LogCommand;
-import org.tmatesoft.hg.core.LogCommand.CollectHandler;
-import org.tmatesoft.hg.core.LogCommand.FileHistoryHandler;
-import org.tmatesoft.hg.core.LogCommand.FileRevision;
+import org.tmatesoft.hg.core.HgLogCommand;
+import org.tmatesoft.hg.core.HgLogCommand.CollectHandler;
+import org.tmatesoft.hg.core.HgLogCommand.FileHistoryHandler;
+import org.tmatesoft.hg.core.HgLogCommand.FileRevision;
 import org.tmatesoft.hg.core.Path;
 import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRepository;
@@ -74,7 +74,7 @@
 	public void testCompleteLog() throws Exception {
 		changelogParser.reset();
 		eh.run("hg", "log", "--debug");
-		List<HgChangeset> r = new LogCommand(repo).execute();
+		List<HgChangeset> r = new HgLogCommand(repo).execute();
 		report("hg log - COMPLETE REPO HISTORY", r, true); 
 	}
 	
@@ -95,7 +95,7 @@
 					}
 				};
 				H h = new H();
-				new LogCommand(repo).file(f, true).execute(h);
+				new HgLogCommand(repo).file(f, true).execute(h);
 				String what = "hg log - FOLLOW FILE HISTORY";
 				errorCollector.checkThat(what + "#copyReported ", h.copyReported, is(true));
 				errorCollector.checkThat(what + "#copyFromMatched", h.fromMatched, is(true));
@@ -144,7 +144,7 @@
 		}
 		final long start2 = System.currentTimeMillis();
 		for (int i = 0; i < runs; i++) {
-			new LogCommand(repo).execute();
+			new HgLogCommand(repo).execute();
 		}
 		final long end = System.currentTimeMillis();
 		System.out.printf("'hg log --debug', %d runs: Native client total %d (%d per run), Java client %d (%d)\n", runs, start2-start1, (start2-start1)/runs, end-start2, (end-start2)/runs);
--- a/test/org/tmatesoft/hg/test/TestManifest.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/test/org/tmatesoft/hg/test/TestManifest.java	Wed Feb 16 20:47:56 2011 +0100
@@ -30,10 +30,10 @@
 import org.junit.Assume;
 import org.junit.Rule;
 import org.junit.Test;
-import org.tmatesoft.hg.core.LogCommand.FileRevision;
+import org.tmatesoft.hg.core.HgLogCommand.FileRevision;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.core.Path;
-import org.tmatesoft.hg.core.RepositoryTreeWalker;
+import org.tmatesoft.hg.core.HgManifestCommand;
 import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRepository;
 
@@ -52,7 +52,7 @@
 	private ManifestOutputParser manifestParser;
 	private ExecHelper eh;
 	final LinkedList<FileRevision> revisions = new LinkedList<FileRevision>();
-	private RepositoryTreeWalker.Handler handler  = new RepositoryTreeWalker.Handler() {
+	private HgManifestCommand.Handler handler  = new HgManifestCommand.Handler() {
 		
 		public void file(FileRevision fileRevision) {
 			revisions.add(fileRevision);
@@ -104,7 +104,7 @@
 		manifestParser.reset();
 		eh.run("hg", "manifest", "--debug", "--rev", String.valueOf(rev));
 		revisions.clear();
-		new RepositoryTreeWalker(repo).revision(rev).walk(handler);
+		new HgManifestCommand(repo).revision(rev).walk(handler);
 		report("manifest " + (rev == TIP ? "TIP:" : "--rev " + rev));
 	}
 
--- a/test/org/tmatesoft/hg/test/TestStatus.java	Wed Feb 16 20:33:31 2011 +0100
+++ b/test/org/tmatesoft/hg/test/TestStatus.java	Wed Feb 16 20:47:56 2011 +0100
@@ -34,7 +34,7 @@
 import org.junit.Test;
 import org.tmatesoft.hg.core.HgStatus;
 import org.tmatesoft.hg.core.Path;
-import org.tmatesoft.hg.core.StatusCommand;
+import org.tmatesoft.hg.core.HgStatusCommand;
 import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgStatusCollector;
@@ -104,7 +104,7 @@
 	
 	@Test
 	public void testStatusCommand() throws Exception {
-		final StatusCommand sc = new StatusCommand(repo).all();
+		final HgStatusCommand sc = new HgStatusCommand(repo).all();
 		StatusCollector r;
 		statusParser.reset();
 		eh.run("hg", "status", "-A");
@@ -125,7 +125,7 @@
 		// TODO check not -A, but defaults()/custom set of modifications 
 	}
 	
-	private static class StatusCollector implements StatusCommand.Handler {
+	private static class StatusCollector implements HgStatusCommand.Handler {
 		private final Map<HgStatus.Kind, List<Path>> map = new TreeMap<HgStatus.Kind, List<Path>>();
 
 		public void handleStatus(HgStatus s) {
@@ -170,7 +170,7 @@
 		final long start2 = System.currentTimeMillis();
 		for (int i = 0; i < runs; i++) {
 			StatusCollector r = new StatusCollector();
-			new StatusCommand(repo).all().base(3).revision(80).execute(r);
+			new HgStatusCommand(repo).all().base(3).revision(80).execute(r);
 		}
 		final long end = System.currentTimeMillis();
 		System.out.printf("'hg status -A --rev 3:80', %d runs:  Native client total %d (%d per run), Java client %d (%d)\n", runs, start2-start1, (start2-start1)/runs, end-start2, (end-start2)/runs);