changeset 237:6e1373b54e9b

Allow access to working copy content through HgDataFile. Give access to repository's working dir
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 10 Jun 2011 04:35:21 +0200
parents 883300108179
children 4817d4ccc50e
files cmdline/org/tmatesoft/hg/console/Main.java src/org/tmatesoft/hg/core/HgCatCommand.java src/org/tmatesoft/hg/core/HgFileRevision.java src/org/tmatesoft/hg/core/HgLogCommand.java src/org/tmatesoft/hg/internal/NewlineFilter.java src/org/tmatesoft/hg/repo/HgDataFile.java src/org/tmatesoft/hg/repo/HgInternals.java src/org/tmatesoft/hg/repo/HgRepository.java src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java
diffstat 9 files changed, 96 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/cmdline/org/tmatesoft/hg/console/Main.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/cmdline/org/tmatesoft/hg/console/Main.java	Fri Jun 10 04:35:21 2011 +0200
@@ -70,12 +70,13 @@
 
 	public static void main(String[] args) throws Exception {
 		Main m = new Main(args);
+		m.testReadWorkingCopy();
 //		m.testParents();
 //		m.testEffectiveFileLog();
 //		m.testCatAtCsetRevision();
 //		m.testMergeState();
 //		m.testFileStatus();
-		m.dumpBranches();
+//		m.dumpBranches();
 //		m.inflaterLengthException();
 //		m.dumpIgnored();
 //		m.dumpDirstate();
@@ -86,6 +87,15 @@
 //		m.bunchOfTests();
 	}
 	
+	private void testReadWorkingCopy() throws Exception {
+		for (String fname : cmdLineOpts.getList("")) {
+			HgDataFile fn = hgRepo.getFileNode(fname);
+			ByteArrayChannel sink = new ByteArrayChannel();
+			fn.workingCopy(sink);
+			System.out.printf("%s: read %d bytes of working copy", fname, sink.toArray().length);
+		}
+	}
+	
 	private void testParents() throws Exception {
 		// hg parents cmd
 		final Pair<Nodeid, Nodeid> wcParents = hgRepo.getWorkingCopyParents();
--- a/src/org/tmatesoft/hg/core/HgCatCommand.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/core/HgCatCommand.java	Fri Jun 10 04:35:21 2011 +0200
@@ -21,7 +21,6 @@
 import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
 import java.io.FileNotFoundException;
-import java.io.IOException;
 
 import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgRepository;
@@ -123,7 +122,7 @@
 	 * @throws HgDataStreamException 
 	 * @throws IllegalArgumentException when command arguments are incomplete or wrong
 	 */
-	public void execute(ByteChannel sink) throws HgDataStreamException, IOException, CancelledException {
+	public void execute(ByteChannel sink) throws HgDataStreamException, CancelledException {
 		if (localRevision == BAD_REVISION && revision == null && cset == null) {
 			throw new IllegalArgumentException("File revision, corresponing local number, or a changset nodeid shall be specified");
 		}
--- a/src/org/tmatesoft/hg/core/HgFileRevision.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/core/HgFileRevision.java	Fri Jun 10 04:35:21 2011 +0200
@@ -16,8 +16,6 @@
  */
 package org.tmatesoft.hg.core;
 
-import java.io.IOException;
-
 import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.util.ByteChannel;
@@ -51,7 +49,7 @@
 	public Nodeid getRevision() {
 		return revision;
 	}
-	public void putContentTo(ByteChannel sink) throws HgDataStreamException, IOException, CancelledException {
+	public void putContentTo(ByteChannel sink) throws HgDataStreamException, CancelledException {
 		HgDataFile fn = repo.getFileNode(path);
 		int localRevision = fn.getLocalRevision(revision);
 		fn.contentWithFilters(localRevision, sink);
--- a/src/org/tmatesoft/hg/core/HgLogCommand.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/core/HgLogCommand.java	Fri Jun 10 04:35:21 2011 +0200
@@ -322,6 +322,6 @@
 	public interface FileRevision {
 		public abstract Path getPath();
 		public abstract Nodeid getRevision();
-		public abstract void putContentTo(ByteChannel sink) throws HgDataStreamException, IOException, CancelledException;
+		public abstract void putContentTo(ByteChannel sink) throws HgDataStreamException, CancelledException;
 	}
 }
--- a/src/org/tmatesoft/hg/internal/NewlineFilter.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/internal/NewlineFilter.java	Fri Jun 10 04:35:21 2011 +0200
@@ -27,7 +27,6 @@
 import java.util.ArrayList;
 import java.util.Map;
 
-import org.tmatesoft.hg.repo.HgInternals;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.util.Path;
 
@@ -167,7 +166,7 @@
 
 		public void initialize(HgRepository hgRepo, ConfigFile cfg) {
 			failIfInconsistent = cfg.getBoolean("eol", "only-consistent", true);
-			File cfgFile = new File(new HgInternals(hgRepo).getRepositoryDir().getParentFile(), ".hgeol");
+			File cfgFile = new File(hgRepo.getWorkingDir(), ".hgeol");
 			if (!cfgFile.canRead()) {
 				return;
 			}
--- a/src/org/tmatesoft/hg/repo/HgDataFile.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgDataFile.java	Fri Jun 10 04:35:21 2011 +0200
@@ -20,8 +20,11 @@
 import static org.tmatesoft.hg.repo.HgRepository.*;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.TreeMap;
@@ -33,8 +36,10 @@
 import org.tmatesoft.hg.internal.FilterByteChannel;
 import org.tmatesoft.hg.internal.RevlogStream;
 import org.tmatesoft.hg.util.ByteChannel;
+import org.tmatesoft.hg.util.CancelSupport;
 import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.Path;
+import org.tmatesoft.hg.util.ProgressSupport;
 
 
 
@@ -77,8 +82,50 @@
 		return content.dataLength(getLocalRevision(nodeid));
 	}
 
-	public void workingCopy(ByteChannel sink) throws IOException, CancelledException {
-		throw HgRepository.notImplemented();
+	/**
+	 * Reads content of the file from working directory. If file present in the working directory, its actual content without
+	 * any filters is supplied through the sink. If file does not exist in the working dir, this method provides content of a file 
+	 * as if it would be refreshed in the working copy, i.e. its corresponding revision 
+	 * (XXX according to dirstate? file tip?) is read from the repository, and filters repo -> working copy get applied.
+	 *     
+	 * @param sink where to pipe content to
+	 * @throws HgDataStreamException to indicate troubles reading repository file
+	 * @throws CancelledException if operation was cancelled
+	 */
+	public void workingCopy(ByteChannel sink) throws HgDataStreamException, CancelledException {
+		File f = getRepo().getFile(this);
+		if (f.exists()) {
+			final CancelSupport cs = CancelSupport.Factory.get(sink);
+			final ProgressSupport progress = ProgressSupport.Factory.get(sink);
+			final long flength = f.length();
+			final int bsize = (int) Math.min(flength, 32*1024);
+			progress.start((int) (flength > Integer.MAX_VALUE ? flength >>> 15 /*32 kb buf size*/ : flength));
+			ByteBuffer buf = ByteBuffer.allocate(bsize);
+			FileChannel fc = null;
+			try {
+				fc = new FileInputStream(f).getChannel();
+				while (fc.read(buf) != -1) {
+					cs.checkCancelled();
+					buf.flip();
+					int consumed = sink.write(buf);
+					progress.worked(flength > Integer.MAX_VALUE ? 1 : consumed);
+					buf.compact();
+				}
+			} catch (IOException ex) {
+				throw new HgDataStreamException(getPath(), ex);
+			} finally {
+				progress.done();
+				if (fc != null) {
+					try {
+						fc.close();
+					} catch (IOException ex) {
+						ex.printStackTrace();
+					}
+				}
+			}
+		} else {
+			contentWithFilters(TIP, sink);
+		}
 	}
 	
 //	public void content(int revision, ByteChannel sink, boolean applyFilters) throws HgDataStreamException, IOException, CancelledException {
@@ -106,17 +153,23 @@
 //	}
 	
 	/*XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves?*/
-	public void contentWithFilters(int revision, ByteChannel sink) throws HgDataStreamException, IOException, CancelledException {
-		content(revision, new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath())));
+	public void contentWithFilters(int revision, ByteChannel sink) throws HgDataStreamException, CancelledException {
+		if (revision == WORKING_COPY) {
+			workingCopy(sink); // pass un-mangled sink
+		} else {
+			content(revision, new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath())));
+		}
 	}
 
 	// for data files need to check heading of the file content for possible metadata
 	// @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8-
-	public void content(int revision, ByteChannel sink) throws HgDataStreamException, IOException, CancelledException {
+	public void content(int revision, ByteChannel sink) throws HgDataStreamException, CancelledException {
 		if (revision == TIP) {
 			revision = getLastRevision();
 		}
 		if (revision == WORKING_COPY) {
+			// sink is supposed to come into workingCopy without filters
+			// thus we shall not get here (into #content) from #contentWithFilters(WC)
 			workingCopy(sink);
 			return;
 		}
@@ -141,9 +194,11 @@
 		insp.checkCancelled();
 		super.content.iterate(revision, revision, true, insp);
 		try {
-			insp.checkFailed();
+			insp.checkFailed(); // XXX is there real need to throw IOException from ContentPipe?
 		} catch (HgDataStreamException ex) {
 			throw ex;
+		} catch (IOException ex) {
+			throw new HgDataStreamException(getPath(), ex);
 		} catch (HgException ex) {
 			// shall not happen, unless we changed ContentPipe or its subclass
 			throw new HgDataStreamException(getPath(), ex.getClass().getName(), ex);
--- a/src/org/tmatesoft/hg/repo/HgInternals.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgInternals.java	Fri Jun 10 04:35:21 2011 +0200
@@ -95,7 +95,7 @@
 	
 	@Experimental(reason="Don't want to expose io.File from HgRepository; need to create FileIterator for working dir. Need a place to keep that code")
 	/*package-local*/ FileIterator createWorkingDirWalker(Path.Matcher workindDirScope) {
-		File repoRoot = repo.getRepositoryRoot().getParentFile();
+		File repoRoot = repo.getWorkingDir();
 		Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(repoRoot), repo.getToRepoPathHelper()));
 		// Impl note: simple source is enough as files in the working dir are all unique
 		// even if they might get reused (i.e. after FileIterator#reset() and walking once again),
--- a/src/org/tmatesoft/hg/repo/HgRepository.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgRepository.java	Fri Jun 10 04:35:21 2011 +0200
@@ -22,7 +22,6 @@
 import java.io.IOException;
 import java.io.StringReader;
 import java.lang.ref.SoftReference;
-import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -64,6 +63,7 @@
 	}
 	
 	private final File repoDir; // .hg folder
+	private final File workingDir; // .hg/../
 	private final String repoLocation;
 	private final DataAccessProvider dataAccess;
 	private final PathRewrite normalizePath;
@@ -85,6 +85,7 @@
 	
 	HgRepository(String repositoryPath) {
 		repoDir = null;
+		workingDir = null;
 		repoLocation = repositoryPath;
 		dataAccess = null;
 		dataPathHelper = repoPathHelper = null;
@@ -96,6 +97,10 @@
 		assert repositoryPath != null; 
 		assert repositoryRoot != null;
 		repoDir = repositoryRoot;
+		workingDir = repoDir.getParentFile();
+		if (workingDir == null) {
+			throw new IllegalArgumentException(repoDir.toString());
+		}
 		repoLocation = repositoryPath;
 		dataAccess = new DataAccessProvider();
 		final boolean runningOnWindows = System.getProperty("os.name").indexOf("Windows") != -1;
@@ -230,7 +235,14 @@
 		return new Pair<Nodeid,Nodeid>(NULL == p[0] ? null : p[0], NULL == p[1] ? null : p[1]);
 	}
 
-	// local to hide use of io.File. 
+	/**
+	 * @return location where user files (shall) reside
+	 */
+	public File getWorkingDir() {
+		return workingDir;
+	}
+
+	// shall be of use only for internal classes 
 	/*package-local*/ File getRepositoryRoot() {
 		return repoDir;
 	}
@@ -307,6 +319,10 @@
 	/*package-local*/ List<Filter> getFiltersFromWorkingDirToRepo(Path p) {
 		return instantiateFilters(p, new Filter.Options(Filter.Direction.ToRepo));
 	}
+	
+	/*package-local*/ File getFile(HgDataFile dataFile) {
+		return new File(getWorkingDir(), dataFile.getPath().toString());
+	}
 
 	private List<Filter> instantiateFilters(Path p, Filter.Options opts) {
 		List<Filter.Factory> factories = impl.getFilters(this, getConfigFile());
--- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Thu Jun 09 06:13:43 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Fri Jun 10 04:35:21 2011 +0200
@@ -422,7 +422,7 @@
 //		final Path[] dirs = f.toArray(new Path[d.size()]);
 		if (d.isEmpty()) {
 			final Path[] files = f.toArray(new Path[f.size()]);
-			FileIterator fi = new FileListIterator(hgRepo.getRepositoryRoot().getParentFile(), files);
+			FileIterator fi = new FileListIterator(hgRepo.getWorkingDir(), files);
 			return new HgWorkingCopyStatusCollector(hgRepo, fi);
 		}
 		//