changeset 388:b015f3918120

Work on FIXME: correct HgDataFile#workingCopy with tests; BasicSessionContext with property override; platform-specific options to internals
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 15 Feb 2012 22:57:56 +0100
parents cdea37239b01
children 82bec80bb1a4
files src/org/tmatesoft/hg/internal/BasicSessionContext.java src/org/tmatesoft/hg/internal/Internals.java src/org/tmatesoft/hg/repo/HgDataFile.java src/org/tmatesoft/hg/repo/HgDirstate.java src/org/tmatesoft/hg/repo/HgRepository.java src/org/tmatesoft/hg/repo/Revlog.java src/org/tmatesoft/hg/util/Pair.java test/org/tmatesoft/hg/test/Configuration.java test/org/tmatesoft/hg/test/ExecHelper.java test/org/tmatesoft/hg/test/TestByteChannel.java test/org/tmatesoft/hg/test/TestIncoming.java
diffstat 11 files changed, 208 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/BasicSessionContext.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/internal/BasicSessionContext.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -16,6 +16,9 @@
  */
 package org.tmatesoft.hg.internal;
 
+import java.util.Collections;
+import java.util.Map;
+
 import org.tmatesoft.hg.core.SessionContext;
 import org.tmatesoft.hg.util.LogFacility;
 import org.tmatesoft.hg.util.PathPool;
@@ -30,10 +33,17 @@
 
 	private PathPool pathPool;
 	private final LogFacility logFacility;
+	private final Map<String, Object> properties;
 	
 	public BasicSessionContext(PathPool pathFactory, LogFacility log) {
+		this(null, pathFactory, log);
+	}
+	
+	@SuppressWarnings("unchecked")
+	public BasicSessionContext(Map<String,?> propertyOverrides, PathPool pathFactory, LogFacility log) {
 		pathPool = pathFactory;
 		logFacility = log != null ? log : new StreamLogFacility(true, true, true, System.out);
+		properties = propertyOverrides == null ? Collections.<String,Object>emptyMap() : (Map<String, Object>) propertyOverrides;
 	}
 
 	public PathPool getPathPool() {
@@ -49,7 +59,11 @@
 	}
 
 	public Object getProperty(String name, Object defaultValue) {
-		String value = System.getProperty(name);
+		Object value = properties.get(name);
+		if (value != null) {
+			return value;
+		}
+		value = System.getProperty(name);
 		return value == null ? defaultValue : value;
 	}
 }
--- a/src/org/tmatesoft/hg/internal/Internals.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/internal/Internals.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -40,15 +40,31 @@
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
-public class Internals {
+public final class Internals {
 	
+	/**
+	 * Allows to specify Mercurial installation directory to detect installation-wide configurations.
+	 * Without this property set, hg4j would attempt to deduce this value locating hg executable. 
+	 */
 	public static final String CFG_PROPERTY_HG_INSTALL_ROOT = "hg4j.hg.install_root";
+
+	/**
+	 * Tells repository not to cache files/revlogs
+	 * XXX perhaps, need to respect this property not only for data files, but for manifest and changelog as well?
+	 * (@see HgRepository#getChangelog and #getManifest())  
+	 */
+	public static final String CFG_PROPERTY_REVLOG_STREAM_CACHE = "hg4j.repo.disable_revlog_cache";
 	
 	private int requiresFlags = 0;
 	private List<Filter.Factory> filterFactories;
+	private final boolean isCaseSensitiveFileSystem;
+	private final boolean shallCacheRevlogsInRepo;
 	
 
-	public Internals() {
+	public Internals(SessionContext ctx) {
+		isCaseSensitiveFileSystem = !runningOnWindows();
+		Object p = ctx.getProperty(CFG_PROPERTY_REVLOG_STREAM_CACHE, true);
+		shallCacheRevlogsInRepo = p instanceof Boolean ? ((Boolean) p).booleanValue() : Boolean.parseBoolean(String.valueOf(p));
 	}
 	
 	public void parseRequires(HgRepository hgRepo, File requiresFile) {
@@ -63,6 +79,25 @@
 	public/*for tests, otherwise pkg*/ void setStorageConfig(int version, int flags) {
 		requiresFlags = flags;
 	}
+	
+	public PathRewrite buildNormalizePathRewrite() {
+		if (runningOnWindows()) {
+			return new PathRewrite() {
+					
+					public CharSequence rewrite(CharSequence p) {
+						// TODO handle . and .. (although unlikely to face them from GUI client)
+						String path = p.toString();
+						path = path.replace('\\', '/').replace("//", "/");
+						if (path.startsWith("/")) {
+							path = path.substring(1);
+						}
+						return path;
+					}
+				};
+		} else {
+			return new PathRewrite.Empty(); // or strip leading slash, perhaps? 
+		}
+	}
 
 	// XXX perhaps, should keep both fields right here, not in the HgRepository
 	public PathRewrite buildDataFilesHelper() {
@@ -117,6 +152,10 @@
 		requiresFile.close();
 		new File(hgDir, "store").mkdir(); // with that, hg verify says ok.
 	}
+	
+	public boolean isCaseSensitiveFileSystem() {
+		return isCaseSensitiveFileSystem;
+	}
 
 	public static boolean runningOnWindows() {
 		return System.getProperty("os.name").indexOf("Windows") != -1;
@@ -277,4 +316,8 @@
 		// fallback to default, let calling code fail with Exception if can't write
 		return new File(System.getProperty("user.home"), ".hgrc");
 	}
+
+	public boolean shallCacheRevlogs() {
+		return shallCacheRevlogsInRepo;
+	}
 }
--- a/src/org/tmatesoft/hg/repo/HgDataFile.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgDataFile.java	Wed Feb 15 22:57:56 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 TMate Software Ltd
+ * Copyright (c) 2010-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
@@ -45,6 +45,7 @@
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelSupport;
 import org.tmatesoft.hg.util.CancelledException;
+import org.tmatesoft.hg.util.LogFacility;
 import org.tmatesoft.hg.util.Pair;
 import org.tmatesoft.hg.util.Path;
 import org.tmatesoft.hg.util.ProgressSupport;
@@ -161,9 +162,25 @@
 				}
 			}
 		} else {
-			// FIXME not TIP, but revision according to dirstate!!!
-			// add tests for this case
-			contentWithFilters(TIP, sink);
+			final Pair<Nodeid, Nodeid> wcParents = getRepo().getWorkingCopyParents();
+			Nodeid p = wcParents.first().isNull() ? wcParents.second() : wcParents.first();
+			if (p.isNull()) {
+				// no dirstate parents - no content 
+				return;
+			}
+			final HgChangelog clog = getRepo().getChangelog();
+			// common case to avoid searching complete changelog for nodeid match
+			final Nodeid tipRev = clog.getRevision(TIP);
+			final int csetRevIndex;
+			if (tipRev.equals(p)) {
+				csetRevIndex = clog.getLastRevision();
+			} else {
+				// bad luck, need to search honestly
+				csetRevIndex = getRepo().getChangelog().getRevisionIndex(p);
+			}
+			Nodeid fileRev = getRepo().getManifest().getFileRevision(csetRevIndex, getPath());
+			final int fileRevIndex = getRevisionIndex(fileRev);
+			contentWithFilters(fileRevIndex, sink);
 		}
 	}
 	
@@ -231,13 +248,14 @@
 			metadata = new Metadata();
 		}
 		ErrorHandlingInspector insp;
+		final LogFacility lf = getRepo().getContext().getLog();
 		if (metadata.none(fileRevisionIndex)) {
-			insp = new ContentPipe(sink, 0, getRepo().getContext().getLog());
+			insp = new ContentPipe(sink, 0, lf);
 		} else if (metadata.known(fileRevisionIndex)) {
-			insp = new ContentPipe(sink, metadata.dataOffset(fileRevisionIndex), getRepo().getContext().getLog());
+			insp = new ContentPipe(sink, metadata.dataOffset(fileRevisionIndex), lf);
 		} else {
 			// do not know if there's metadata
-			insp = new MetadataInspector(metadata, getPath(), new ContentPipe(sink, 0, getRepo().getContext().getLog()));
+			insp = new MetadataInspector(metadata, lf, getPath(), new ContentPipe(sink, 0, lf));
 		}
 		insp.checkCancelled();
 		super.content.iterate(fileRevisionIndex, fileRevisionIndex, true, insp);
@@ -509,19 +527,26 @@
 	private static final class MetadataEntry {
 		private final String entry;
 		private final int valueStart;
+
+		// key may be null
 		/*package-local*/MetadataEntry(String key, String value) {
-			entry = key + value;
-			valueStart = key.length();
+			if (key == null) {
+				entry = value;
+				valueStart = -1; // not 0 to tell between key == null and key == ""
+			} else {
+				entry = key + value;
+				valueStart = key.length();
+			}
 		}
 		/*package-local*/boolean matchKey(String key) {
-			return key.length() == valueStart && entry.startsWith(key);
+			return key == null ? valueStart == -1 : key.length() == valueStart && entry.startsWith(key);
 		}
 //		uncomment once/if needed
 //		public String key() {
 //			return entry.substring(0, valueStart);
 //		}
 		public String value() {
-			return entry.substring(valueStart);
+			return valueStart == -1 ? entry : entry.substring(valueStart);
 		}
 	}
 
@@ -590,12 +615,14 @@
 
 	private static class MetadataInspector extends ErrorHandlingInspector implements RevlogStream.Inspector {
 		private final Metadata metadata;
-		private final Path fname; // need this only for error reporting
 		private final RevlogStream.Inspector delegate;
+		private final Path fname; // need these only for error reporting
+		private final LogFacility log;
 
-		public MetadataInspector(Metadata _metadata, Path file, RevlogStream.Inspector chain) {
+		public MetadataInspector(Metadata _metadata, LogFacility logFacility, Path file, RevlogStream.Inspector chain) {
 			metadata = _metadata;
 			fname = file;
+			log = logFacility;
 			delegate = chain;
 			setCancelSupport(CancelSupport.Factory.get(chain));
 		}
@@ -647,7 +674,7 @@
 						break;
 					}
 					if (key == null || lastColon == -1 || i <= lastColon) {
-						throw new IllegalStateException(); // FIXME log instead and record null key in the metadata. Ex just to fail fast during dev
+						log.error(getClass(), "Missing key in file revision metadata at index %d", i);
 					}
 					value = new String(bos.toByteArray()).trim();
 					bos.reset();
--- a/src/org/tmatesoft/hg/repo/HgDirstate.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgDirstate.java	Wed Feb 15 22:57:56 2012 +0100
@@ -142,7 +142,7 @@
 				} else if (state == 'm') {
 					merged.put(r.name1, r);
 				} else {
-					// FIXME log error?
+					repo.getContext().getLog().warn(getClass(), "Dirstate record for file %s (size: %d, tstamp:%d) has unknown state '%c'", r.name1, r.size(), r.time, state);
 				}
 			}
 		} catch (IOException ex) {
--- a/src/org/tmatesoft/hg/repo/HgRepository.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgRepository.java	Wed Feb 15 22:57:56 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 TMate Software Ltd
+ * Copyright (c) 2010-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
@@ -34,7 +34,6 @@
 import org.tmatesoft.hg.internal.DataAccessProvider;
 import org.tmatesoft.hg.internal.Experimental;
 import org.tmatesoft.hg.internal.Filter;
-import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.RevlogStream;
 import org.tmatesoft.hg.internal.SubrepoManager;
 import org.tmatesoft.hg.util.CancelledException;
@@ -73,7 +72,6 @@
 	private final PathRewrite normalizePath;
 	private final PathRewrite dataPathHelper;
 	private final PathRewrite repoPathHelper;
-	private final boolean isCaseSensitiveFileSystem; // FIXME keep this inside Internals impl and delegate to Internals all os/fs-specific tasks
 	private final SessionContext sessionContext;
 
 	private HgChangelog changelog;
@@ -86,7 +84,7 @@
 	// XXX perhaps, shall enable caching explicitly
 	private final HashMap<Path, SoftReference<RevlogStream>> streamsCache = new HashMap<Path, SoftReference<RevlogStream>>();
 	
-	private final org.tmatesoft.hg.internal.Internals impl = new org.tmatesoft.hg.internal.Internals();
+	private final org.tmatesoft.hg.internal.Internals impl;
 	private HgIgnore ignore;
 	private HgRepoConfig repoConfig;
 	
@@ -98,7 +96,7 @@
 		dataPathHelper = repoPathHelper = null;
 		normalizePath = null;
 		sessionContext = null;
-		isCaseSensitiveFileSystem = !Internals.runningOnWindows();
+		impl = null;
 	}
 	
 	HgRepository(SessionContext ctx, String repositoryPath, File repositoryRoot) {
@@ -111,28 +109,12 @@
 		if (workingDir == null) {
 			throw new IllegalArgumentException(repoDir.toString());
 		}
+		impl = new org.tmatesoft.hg.internal.Internals(ctx);
 		repoLocation = repositoryPath;
 		sessionContext = ctx;
 		dataAccess = new DataAccessProvider(ctx);
-		final boolean runningOnWindows = Internals.runningOnWindows();
-		isCaseSensitiveFileSystem = !runningOnWindows;
-		if (runningOnWindows) {
-			normalizePath = new PathRewrite() {
-					
-					public CharSequence rewrite(CharSequence p) {
-						// TODO handle . and .. (although unlikely to face them from GUI client)
-						String path = p.toString();
-						path = path.replace('\\', '/').replace("//", "/");
-						if (path.startsWith("/")) {
-							path = path.substring(1);
-						}
-						return path;
-					}
-				};
-		} else {
-			normalizePath = new PathRewrite.Empty(); // or strip leading slash, perhaps? 
-		}
 		impl.parseRequires(this, new File(repoDir, "requires"));
+		normalizePath = impl.buildNormalizePathRewrite(); 
 		dataPathHelper = impl.buildDataFilesHelper();
 		repoPathHelper = impl.buildRepositoryFilesHelper();
 	}
@@ -151,20 +133,20 @@
 	}
 	
 	public HgChangelog getChangelog() {
-		if (this.changelog == null) {
+		if (changelog == null) {
 			CharSequence storagePath = repoPathHelper.rewrite("00changelog.i");
 			RevlogStream content = resolve(Path.create(storagePath), true);
-			this.changelog = new HgChangelog(this, content);
+			changelog = new HgChangelog(this, content);
 		}
-		return this.changelog;
+		return changelog;
 	}
 	
 	public HgManifest getManifest() {
-		if (this.manifest == null) {
+		if (manifest == null) {
 			RevlogStream content = resolve(Path.create(repoPathHelper.rewrite("00manifest.i")), true);
-			this.manifest = new HgManifest(this, content);
+			manifest = new HgManifest(this, content);
 		}
-		return this.manifest;
+		return manifest;
 	}
 	
 	public HgTags getTags() throws HgInvalidControlFileException {
@@ -316,7 +298,7 @@
 	// XXX consider passing Path pool or factory to produce (shared) Path instead of Strings
 	/*package-local*/ final HgDirstate loadDirstate(PathPool pathPool) throws HgInvalidControlFileException {
 		PathRewrite canonicalPath = null;
-		if (!isCaseSensitiveFileSystem) {
+		if (!impl.isCaseSensitiveFileSystem()) {
 			canonicalPath = new PathRewrite() {
 
 				public CharSequence rewrite(CharSequence path) {
@@ -369,7 +351,9 @@
 		File f = new File(repoDir, path.toString());
 		if (f.exists()) {
 			RevlogStream s = new RevlogStream(dataAccess, f);
-			streamsCache.put(path, new SoftReference<RevlogStream>(s));
+			if (impl.shallCacheRevlogs()) {
+				streamsCache.put(path, new SoftReference<RevlogStream>(s));
+			}
 			return s;
 		} else {
 			if (shallFakeNonExistent) {
--- a/src/org/tmatesoft/hg/repo/Revlog.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/Revlog.java	Wed Feb 15 22:57:56 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 TMate Software Ltd
+ * Copyright (c) 2010-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
@@ -91,9 +91,9 @@
 	}
 
 	/**
-	 * Map revision index to unique revision identifier (nodeid)
+	 * Map revision index to unique revision identifier (nodeid).
 	 *  
-	 * @param revision index of the entry in this revlog
+	 * @param revision index of the entry in this revlog, may be {@link HgRepository#TIP}
 	 * @return revision nodeid of the entry
 	 * 
 	 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog
--- a/src/org/tmatesoft/hg/util/Pair.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/src/org/tmatesoft/hg/util/Pair.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -46,5 +46,16 @@
 	public boolean hasSecond() {
 		return value2 != null;
 	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append('<');
+		sb.append(first());
+		sb.append(':');
+		sb.append(second());
+		sb.append('>');
+		return sb.toString();
+	}
 }
 
--- a/test/org/tmatesoft/hg/test/Configuration.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/test/org/tmatesoft/hg/test/Configuration.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -92,6 +92,9 @@
 		return rv;
 	}
 
+	/**
+	 * @return temporary directory to use in tests, may be configured from outside
+	 */
 	public File getTempDir() {
 		if (tempDir == null) {
 			String td = System.getProperty("hg4j.tests.tmpdir", System.getProperty("java.io.tmpdir"));
@@ -100,6 +103,9 @@
 		return tempDir;
 	}
 	
+	/**
+	 * @return location with various files used in tests
+	 */
 	public File getTestDataDir() {
 		if (testDataDir == null) {
 			testDataDir = new File(System.getProperty("user.dir"), "test-data");
--- a/test/org/tmatesoft/hg/test/ExecHelper.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/test/org/tmatesoft/hg/test/ExecHelper.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -50,7 +50,7 @@
 				if (new File(pe, cmd[0] + ".exe").exists()) {
 					break;
 				}
-				// PATHEXT controls precedence of .exe, .bat and .cmd files, ususlly .exe wins
+				// PATHEXT controls precedence of .exe, .bat and .cmd files, usually .exe wins
 				if (new File(pe, cmd[0] + ".bat").exists() || new File(pe, cmd[0] + ".cmd").exists()) {
 					ArrayList<String> command = new ArrayList<String>();
 					command.add("cmd.exe");
--- a/test/org/tmatesoft/hg/test/TestByteChannel.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/test/org/tmatesoft/hg/test/TestByteChannel.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -16,12 +16,21 @@
  */
 package org.tmatesoft.hg.test;
 
-import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.tmatesoft.hg.internal.BasicSessionContext;
 import org.tmatesoft.hg.internal.ByteArrayChannel;
+import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.repo.HgDataFile;
+import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRepository;
 
 /**
@@ -84,4 +93,55 @@
 		dir_b.content(0, ch = new ByteArrayChannel());
 		assertArrayEquals("a \r\n".getBytes(), ch.toArray());
 	}
+
+	@Test
+	public void testWorkingCopyFileAccess() throws Exception {
+		final File repoDir = TestIncoming.initEmptyTempRepo("testWorkingCopyFileAccess");
+		final Map<String, ?> props = Collections.singletonMap(Internals.CFG_PROPERTY_REVLOG_STREAM_CACHE, false);
+		repo = new HgLookup(new BasicSessionContext(props, null, null)).detect(repoDir);
+		File f1 = new File(repoDir, "file1");
+		final String c1 = "First", c2 = "Second", c3 = "Third";
+		ByteArrayChannel ch;
+		ExecHelper exec = new ExecHelper(new OutputParser.Stub(), repoDir);
+		// commit cset 0
+		write(f1, c1);
+		exec.run("hg", "add");
+		Assert.assertEquals(0, exec.getExitValue());
+		exec.run("hg", "commit", "-m", "c0");
+		Assert.assertEquals(0, exec.getExitValue());
+		// commit cset 1
+		write(f1, c2);
+		exec.run("hg", "commit", "-m", "c1");
+		assertEquals(0, exec.getExitValue());
+		//
+		// modify working copy
+		write(f1, c3);
+		//
+		HgDataFile df = repo.getFileNode(f1.getName());
+		// 1. Shall take content of the file from the dir
+		df.workingCopy(ch = new ByteArrayChannel());
+		assertArrayEquals(c3.getBytes(), ch.toArray());
+		// 2. Shall supply working copy even if no local file is there
+		f1.delete();
+		assertFalse(f1.exists());
+		df = repo.getFileNode(f1.getName());
+		df.workingCopy(ch = new ByteArrayChannel());
+		assertArrayEquals(c2.getBytes(), ch.toArray());
+		//
+		// 3. Shall extract revision of the file that corresponds actual parents (from dirstate) not the TIP as it was  
+		exec.run("hg", "update", "-r", "0");
+		assertEquals(0, exec.getExitValue());
+		f1.delete();
+		assertFalse(f1.exists());
+		// there's no file and workingCopy shall do some extra work to find out actual revision to check out
+		df = repo.getFileNode(f1.getName());
+		df.workingCopy(ch = new ByteArrayChannel());
+		assertArrayEquals(c1.getBytes(), ch.toArray());
+	}
+
+	private static void write(File f, String content) throws IOException {
+		FileWriter fw = new FileWriter(f);
+		fw.write(content);
+		fw.close();
+	}
 }
--- a/test/org/tmatesoft/hg/test/TestIncoming.java	Mon Feb 13 15:11:27 2012 +0100
+++ b/test/org/tmatesoft/hg/test/TestIncoming.java	Wed Feb 15 22:57:56 2012 +0100
@@ -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
@@ -32,6 +32,7 @@
 import org.tmatesoft.hg.core.HgIncomingCommand;
 import org.tmatesoft.hg.core.HgLogCommand;
 import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.internal.BasicSessionContext;
 import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRemoteRepository;
@@ -134,7 +135,7 @@
 
 	static File initEmptyTempRepo(String dirName) throws IOException {
 		File dest = createEmptyDir(dirName);
-		Internals implHelper = new Internals();
+		Internals implHelper = new Internals(new BasicSessionContext(null, null, null));
 		implHelper.setStorageConfig(1, STORE | FNCACHE | DOTENCODE);
 		implHelper.initEmptyRepository(new File(dest, ".hg"));
 		return dest;