diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
index 82a0ce84a04faffc6027c90266d3e05fc62cd35b..cc062dbe88e6f1cdaa8e444bc53a9fbcbccca740 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/FileResolver.java
@@ -52,6 +52,7 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.util.FS;
 
 /** Default resolver serving from a single root path in local filesystem. */
 public class FileResolver implements RepositoryResolver {
@@ -82,7 +83,7 @@ public Repository open(final HttpServletRequest req,
 		final Repository db;
 		try {
 			final File gitdir = new File(basePath, repositoryName);
-			db = RepositoryCache.open(FileKey.lenient(gitdir), true);
+			db = RepositoryCache.open(FileKey.lenient(gitdir, FS.DETECTED), true);
 		} catch (IOException e) {
 			throw new RepositoryNotFoundException(repositoryName, e);
 		}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index b4f0b7e90913a6a4ac02f5420cdc2637fb86e2c7..c502fb6344d7d6a47e652eb1c415850b7af9df85 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -53,6 +53,7 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.SystemReader;
 
 public class MockSystemReader extends SystemReader {
@@ -102,7 +103,7 @@ public String getProperty(String key) {
 	}
 
 	@Override
-	public FileBasedConfig openUserConfig() {
+	public FileBasedConfig openUserConfig(FS fs) {
 		return userGitConfig;
 	}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
index 0bc9ee11220857b8add168c49c5cc0f6f534c15d..2043ac20909fb7536fa654f4d35698029aa2cf75 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
@@ -64,6 +64,7 @@
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.util.FS;
 
 /**
  * Custom argument handler {@link AbstractTreeIterator} from string values.
@@ -95,7 +96,7 @@ public int parseArguments(final Parameters params) throws CmdLineException {
 		final String name = params.getParameter(0);
 
 		if (new File(name).isDirectory()) {
-			setter.addValue(new FileTreeIterator(new File(name)));
+			setter.addValue(new FileTreeIterator(new File(name), FS.DETECTED));
 			return 1;
 		}
 
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
index 21dcdffbfd2a2412376af471d9c5def7c2152fb9..dd3b51efc7771c08a7e231896e9bdbbfeb784942 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/lib/T0007_GitIndexTest.java
@@ -337,7 +337,7 @@ public void testCheckout() throws Exception {
 	}
 
 	public void test030_executeBit_coreModeTrue() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Error, Exception {
-		if (!FS.INSTANCE.supportsExecute()) {
+		if (!FS.DETECTED.supportsExecute()) {
 			System.err.println("Test ignored since platform FS does not support the execute permission");
 			return;
 		}
@@ -392,7 +392,7 @@ public void test030_executeBit_coreModeTrue() throws IllegalArgumentException, I
 	}
 
 	public void test031_executeBit_coreModeFalse() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Error, Exception {
-		if (!FS.INSTANCE.supportsExecute()) {
+		if (!FS.DETECTED.supportsExecute()) {
 			System.err.println("Test ignored since platform FS does not support the execute permission");
 			return;
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
index f05889308d7e6d81599fad91c6e10b80289926ef..cab3cba374db527f8064ed175585fb484c2eb188 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
@@ -54,13 +54,13 @@ public void testNonBareFileKey() {
 		File gitdir = db.getDirectory();
 		File parent = gitdir.getParentFile();
 		File other = new File(parent, "notagit");
-		assertEquals(gitdir, FileKey.exact(gitdir).getFile());
-		assertEquals(parent, FileKey.exact(parent).getFile());
-		assertEquals(other, FileKey.exact(other).getFile());
+		assertEquals(gitdir, FileKey.exact(gitdir, db.getFS()).getFile());
+		assertEquals(parent, FileKey.exact(parent, db.getFS()).getFile());
+		assertEquals(other, FileKey.exact(other, db.getFS()).getFile());
 
-		assertEquals(gitdir, FileKey.lenient(gitdir).getFile());
-		assertEquals(gitdir, FileKey.lenient(parent).getFile());
-		assertEquals(other, FileKey.lenient(other).getFile());
+		assertEquals(gitdir, FileKey.lenient(gitdir, db.getFS()).getFile());
+		assertEquals(gitdir, FileKey.lenient(parent, db.getFS()).getFile());
+		assertEquals(other, FileKey.lenient(other, db.getFS()).getFile());
 	}
 
 	public void testBareFileKey() throws IOException {
@@ -71,21 +71,21 @@ public void testBareFileKey() throws IOException {
 		assertTrue(name.endsWith(".git"));
 		name = name.substring(0, name.length() - 4);
 
-		assertEquals(gitdir, FileKey.exact(gitdir).getFile());
+		assertEquals(gitdir, FileKey.exact(gitdir, db.getFS()).getFile());
 
-		assertEquals(gitdir, FileKey.lenient(gitdir).getFile());
-		assertEquals(gitdir, FileKey.lenient(new File(parent, name)).getFile());
+		assertEquals(gitdir, FileKey.lenient(gitdir, db.getFS()).getFile());
+		assertEquals(gitdir, FileKey.lenient(new File(parent, name), db.getFS()).getFile());
 	}
 
 	public void testFileKeyOpenExisting() throws IOException {
 		Repository r;
 
-		r = new FileKey(db.getDirectory()).open(true);
+		r = new FileKey(db.getDirectory(), db.getFS()).open(true);
 		assertNotNull(r);
 		assertEquals(db.getDirectory(), r.getDirectory());
 		r.close();
 
-		r = new FileKey(db.getDirectory()).open(false);
+		r = new FileKey(db.getDirectory(), db.getFS()).open(false);
 		assertNotNull(r);
 		assertEquals(db.getDirectory(), r.getDirectory());
 		r.close();
@@ -99,13 +99,13 @@ public void testFileKeyOpenNew() throws IOException {
 		assertFalse(gitdir.exists());
 
 		try {
-			new FileKey(gitdir).open(true);
+			new FileKey(gitdir, db.getFS()).open(true);
 			fail("incorrectly opened a non existant repository");
 		} catch (RepositoryNotFoundException e) {
 			assertEquals("repository not found: " + gitdir, e.getMessage());
 		}
 
-		final Repository o = new FileKey(gitdir).open(false);
+		final Repository o = new FileKey(gitdir, db.getFS()).open(false);
 		assertNotNull(o);
 		assertEquals(gitdir, o.getDirectory());
 		assertFalse(gitdir.exists());
@@ -114,18 +114,18 @@ public void testFileKeyOpenNew() throws IOException {
 	public void testCacheRegisterOpen() throws Exception {
 		final File dir = db.getDirectory();
 		RepositoryCache.register(db);
-		assertSame(db, RepositoryCache.open(FileKey.exact(dir)));
+		assertSame(db, RepositoryCache.open(FileKey.exact(dir, db.getFS())));
 
 		assertEquals(".git", dir.getName());
 		final File parent = dir.getParentFile();
-		assertSame(db, RepositoryCache.open(FileKey.lenient(parent)));
+		assertSame(db, RepositoryCache.open(FileKey.lenient(parent, db.getFS())));
 	}
 
 	public void testCacheOpen() throws Exception {
-		final FileKey loc = FileKey.exact(db.getDirectory());
+		final FileKey loc = FileKey.exact(db.getDirectory(), db.getFS());
 		final Repository d2 = RepositoryCache.open(loc);
 		assertNotSame(db, d2);
-		assertSame(d2, RepositoryCache.open(FileKey.exact(loc.getFile())));
+		assertSame(d2, RepositoryCache.open(FileKey.exact(loc.getFile(), db.getFS())));
 		d2.close();
 		d2.close();
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java
index 41c4971a04e08181ed9fee6106f7d8cc94a69e1f..6f8076f09204215d3129126b3dc4a86b0eeb7693 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java
@@ -54,6 +54,7 @@
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.SystemReader;
 
 /**
@@ -116,7 +117,7 @@ public void test007_readUserConfig() {
 		final MockSystemReader mockSystemReader = new MockSystemReader();
 		SystemReader.setInstance(mockSystemReader);
 		final String hostname = mockSystemReader.getHostname();
-		final Config userGitConfig = mockSystemReader.openUserConfig();
+		final Config userGitConfig = mockSystemReader.openUserConfig(FS.DETECTED);
 		final Config localConfig = new Config(userGitConfig);
 		mockSystemReader.clearProperties();
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index 081290310a6a8c41ef905a4e6fde8d69db7ed135..eb08e495b968f842405990be9f9d9fd78889c017 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -77,7 +77,7 @@ public void setUp() throws Exception {
 	public void testEmptyIfRootIsFile() throws Exception {
 		final File r = new File(trash, paths[0]);
 		assertTrue(r.isFile());
-		final FileTreeIterator fti = new FileTreeIterator(r);
+		final FileTreeIterator fti = new FileTreeIterator(r, db.getFS());
 		assertTrue(fti.first());
 		assertTrue(fti.eof());
 	}
@@ -85,7 +85,7 @@ public void testEmptyIfRootIsFile() throws Exception {
 	public void testEmptyIfRootDoesNotExist() throws Exception {
 		final File r = new File(trash, "not-existing-file");
 		assertFalse(r.exists());
-		final FileTreeIterator fti = new FileTreeIterator(r);
+		final FileTreeIterator fti = new FileTreeIterator(r, db.getFS());
 		assertTrue(fti.first());
 		assertTrue(fti.eof());
 	}
@@ -96,13 +96,13 @@ public void testEmptyIfRootIsEmpty() throws Exception {
 		r.mkdir();
 		assertTrue(r.isDirectory());
 
-		final FileTreeIterator fti = new FileTreeIterator(r);
+		final FileTreeIterator fti = new FileTreeIterator(r, db.getFS());
 		assertTrue(fti.first());
 		assertTrue(fti.eof());
 	}
 
 	public void testSimpleIterate() throws Exception {
-		final FileTreeIterator top = new FileTreeIterator(trash);
+		final FileTreeIterator top = new FileTreeIterator(trash, db.getFS());
 
 		assertTrue(top.first());
 		assertFalse(top.eof());
@@ -149,7 +149,7 @@ public void testSimpleIterate() throws Exception {
 	}
 
 	public void testComputeFileObjectId() throws Exception {
-		final FileTreeIterator top = new FileTreeIterator(trash);
+		final FileTreeIterator top = new FileTreeIterator(trash, db.getFS());
 
 		final MessageDigest md = Constants.newMessageDigest();
 		md.update(Constants.encodeASCII(Constants.TYPE_BLOB));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
index 0203d5d44a779d46ba9c193726dd5a974d57dcbb..d5cab0e4c6a6ffbb45a14f7c495d04bba84bebe5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
@@ -71,7 +71,6 @@
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.NotSupportedException;
-import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.RawParseUtils;
 
 /**
@@ -328,16 +327,16 @@ private void checkWriteOk() throws IOException {
 		}
 	}
 
-	static boolean File_canExecute( File f){
-		return FS.INSTANCE.canExecute(f);
+	private boolean File_canExecute(File f){
+		return db.getFS().canExecute(f);
 	}
 
-	static boolean File_setExecute(File f, boolean value) {
-		return FS.INSTANCE.setExecute(f, value);
+	private boolean File_setExecute(File f, boolean value) {
+		return db.getFS().setExecute(f, value);
 	}
 
-	static boolean File_hasExecute() {
-		return FS.INSTANCE.supportsExecute();
+	private boolean File_hasExecute() {
+		return db.getFS().supportsExecute();
 	}
 
 	static byte[] makeKey(File wd, File f) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
index 06700ebe1dcf7c4307efc1bd0c93d00e41470601..9a5bcdb68c2181d70825aa15db77df1066eb90d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
@@ -88,6 +88,8 @@ public class ObjectDirectory extends ObjectDatabase {
 
 	private final File[] alternateObjectDir;
 
+	private final FS fs;
+
 	/**
 	 * Initialize a reference to an on-disk object directory.
 	 *
@@ -95,14 +97,18 @@ public class ObjectDirectory extends ObjectDatabase {
 	 *            the location of the <code>objects</code> directory.
 	 * @param alternateObjectDir
 	 *            a list of alternate object directories
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 */
-	public ObjectDirectory(final File dir, File[] alternateObjectDir) {
+	public ObjectDirectory(final File dir, File[] alternateObjectDir, FS fs) {
 		objects = dir;
 		this.alternateObjectDir = alternateObjectDir;
 		infoDirectory = new File(objects, "info");
 		packDirectory = new File(objects, "pack");
 		alternatesFile = new File(infoDirectory, "alternates");
 		packList = new AtomicReference<PackList>(NO_PACKS);
+		this.fs = fs;
 	}
 
 	/**
@@ -485,17 +491,17 @@ private static BufferedReader open(final File f)
 
 	private ObjectDatabase openAlternate(final String location)
 			throws IOException {
-		final File objdir = FS.resolve(objects, location);
+		final File objdir = fs.resolve(objects, location);
 		return openAlternate(objdir);
 	}
 
 	private ObjectDatabase openAlternate(File objdir) throws IOException {
 		final File parent = objdir.getParentFile();
-		if (FileKey.isGitRepository(parent)) {
-			final Repository db = RepositoryCache.open(FileKey.exact(parent));
+		if (FileKey.isGitRepository(parent, fs)) {
+			final Repository db = RepositoryCache.open(FileKey.exact(parent, fs));
 			return new AlternateRepositoryDatabase(db);
 		}
-		return new ObjectDirectory(objdir, null);
+		return new ObjectDirectory(objdir, null, fs);
 	}
 
 	private static final class PackList {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java
index 5ba186955f0b1ecb4f4d8425e09de9e2d92b421b..302b63b48630ee5991994058c55f4ed2361f16ff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java
@@ -151,12 +151,13 @@ public class RefDirectory extends RefDatabase {
 	private final AtomicInteger lastNotifiedModCnt = new AtomicInteger();
 
 	RefDirectory(final Repository db) {
+		final FS fs = db.getFS();
 		parent = db;
 		gitDir = db.getDirectory();
-		refsDir = FS.resolve(gitDir, R_REFS);
-		logsDir = FS.resolve(gitDir, LOGS);
-		logsRefsDir = FS.resolve(gitDir, LOGS + '/' + R_REFS);
-		packedRefsFile = FS.resolve(gitDir, PACKED_REFS);
+		refsDir = fs.resolve(gitDir, R_REFS);
+		logsDir = fs.resolve(gitDir, LOGS);
+		logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS);
+		packedRefsFile = fs.resolve(gitDir, PACKED_REFS);
 
 		looseRefs.set(RefList.<LooseRef> emptyList());
 		packedRefs.set(PackedRefList.NO_PACKED_REFS);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 233cecf310eaa4bdbe5ddebd46ed755b41dd62a8..98362af987bb136231c282a52970b74fa0adb58b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -102,6 +102,8 @@ public class Repository {
 
 	private final File gitDir;
 
+	private final FS fs;
+
 	private final FileBasedConfig userConfig;
 
 	private final RepositoryConfig config;
@@ -185,6 +187,41 @@ public Repository(final File d, final File workTree) throws IOException {
 	 */
 	public Repository(final File d, final File workTree, final File objectDir,
 			final File[] alternateObjectDir, final File indexFile) throws IOException {
+		this(d, workTree, objectDir, alternateObjectDir, indexFile, FS.DETECTED);
+	}
+
+	/**
+	 * Construct a representation of a Git repository using the given parameters
+	 * possibly overriding default conventions.
+	 *
+	 * @param d
+	 *            GIT_DIR (the location of the repository metadata). May be null
+	 *            for default value in which case it depends on GIT_WORK_TREE.
+	 * @param workTree
+	 *            GIT_WORK_TREE (the root of the checkout). May be null for
+	 *            default value if GIT_DIR is
+	 * @param objectDir
+	 *            GIT_OBJECT_DIRECTORY (where objects and are stored). May be
+	 *            null for default value. Relative names ares resolved against
+	 *            GIT_WORK_TREE
+	 * @param alternateObjectDir
+	 *            GIT_ALTERNATE_OBJECT_DIRECTORIES (where more objects are read
+	 *            from). May be null for default value. Relative names ares
+	 *            resolved against GIT_WORK_TREE
+	 * @param indexFile
+	 *            GIT_INDEX_FILE (the location of the index file). May be null
+	 *            for default value. Relative names ares resolved against
+	 *            GIT_WORK_TREE.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
+	 * @throws IOException
+	 *             the repository appears to already exist but cannot be
+	 *             accessed.
+	 */
+	public Repository(final File d, final File workTree, final File objectDir,
+			final File[] alternateObjectDir, final File indexFile,
+			FS fs) throws IOException {
 
 		if (workTree != null) {
 			workDir = workTree;
@@ -199,8 +236,10 @@ public Repository(final File d, final File workTree, final File objectDir,
 				throw new IllegalArgumentException(JGitText.get().eitherGIT_DIRorGIT_WORK_TREEmustBePassed);
 		}
 
-		userConfig = SystemReader.getInstance().openUserConfig();
-		config = new RepositoryConfig(userConfig, FS.resolve(gitDir, "config"));
+		this.fs = fs;
+
+		userConfig = SystemReader.getInstance().openUserConfig(fs);
+		config = new RepositoryConfig(userConfig, fs.resolve(gitDir, "config"));
 
 		loadUserConfig();
 		loadConfig();
@@ -208,7 +247,7 @@ public Repository(final File d, final File workTree, final File objectDir,
 		if (workDir == null) {
 			String workTreeConfig = getConfig().getString("core", null, "worktree");
 			if (workTreeConfig != null) {
-				workDir = FS.resolve(d, workTreeConfig);
+				workDir = fs.resolve(d, workTreeConfig);
 			} else {
 				workDir = gitDir.getParentFile();
 			}
@@ -216,11 +255,11 @@ public Repository(final File d, final File workTree, final File objectDir,
 
 		refs = new RefDirectory(this);
 		if (objectDir != null)
-			objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""),
-					alternateObjectDir);
+			objectDatabase = new ObjectDirectory(fs.resolve(objectDir, ""),
+					alternateObjectDir, fs);
 		else
-			objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"),
-					alternateObjectDir);
+			objectDatabase = new ObjectDirectory(fs.resolve(gitDir, "objects"),
+					alternateObjectDir, fs);
 
 		if (indexFile != null)
 			this.indexFile = indexFile;
@@ -351,6 +390,13 @@ public RepositoryConfig getConfig() {
 		return config;
 	}
 
+	/**
+	 * @return the used file system abstraction
+	 */
+	public FS getFS() {
+		return fs;
+	}
+
 	/**
 	 * Construct a filename where the loose object having a specified SHA-1
 	 * should be stored. If the object is stored in a shared repository the path
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index b086968c6ce2497d0ffb9d1361d68674f8ad1f24..0b0260a4ccea4118833c2a7401fd62333addd5f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -120,7 +120,7 @@ public static Repository open(final Key location, final boolean mustExist)
 	 *            repository to register.
 	 */
 	public static void register(final Repository db) {
-		cache.registerRepository(FileKey.exact(db.getDirectory()), db);
+		cache.registerRepository(FileKey.exact(db.getDirectory(), db.getFS()), db);
 	}
 
 	/**
@@ -133,7 +133,7 @@ public static void register(final Repository db) {
 	 *            repository to unregister.
 	 */
 	public static void close(final Repository db) {
-		cache.unregisterRepository(FileKey.exact(db.getDirectory()));
+		cache.unregisterRepository(FileKey.exact(db.getDirectory(), db.getFS()));
 	}
 
 	/** Unregister all repositories from the cache. */
@@ -248,11 +248,14 @@ public static class FileKey implements Key {
 		 *
 		 * @param directory
 		 *            location where the repository database is.
+		 * @param fs
+		 *            the file system abstraction which will be necessary to
+		 *            perform certain file system operations.
 		 * @return a key for the given directory.
-		 * @see #lenient(File)
+		 * @see #lenient(File, FS)
 		 */
-		public static FileKey exact(final File directory) {
-			return new FileKey(directory);
+		public static FileKey exact(final File directory, FS fs) {
+			return new FileKey(directory, fs);
 		}
 
 		/**
@@ -268,22 +271,30 @@ public static FileKey exact(final File directory) {
 		 *
 		 * @param directory
 		 *            location where the repository database might be.
+		 * @param fs
+		 *            the file system abstraction which will be necessary to
+		 *            perform certain file system operations.
 		 * @return a key for the given directory.
-		 * @see #exact(File)
+		 * @see #exact(File, FS)
 		 */
-		public static FileKey lenient(final File directory) {
-			final File gitdir = resolve(directory);
-			return new FileKey(gitdir != null ? gitdir : directory);
+		public static FileKey lenient(final File directory, FS fs) {
+			final File gitdir = resolve(directory, fs);
+			return new FileKey(gitdir != null ? gitdir : directory, fs);
 		}
 
 		private final File path;
+		private final FS fs;
 
 		/**
 		 * @param directory
 		 *            exact location of the repository.
+		 * @param fs
+		 *            the file system abstraction which will be necessary to
+		 *            perform certain file system operations.
 		 */
-		protected FileKey(final File directory) {
+		protected FileKey(final File directory, FS fs) {
 			path = canonical(directory);
+			this.fs = fs;
 		}
 
 		private static File canonical(final File path) {
@@ -300,7 +311,7 @@ public final File getFile() {
 		}
 
 		public Repository open(final boolean mustExist) throws IOException {
-			if (mustExist && !isGitRepository(path))
+			if (mustExist && !isGitRepository(path, fs))
 				throw new RepositoryNotFoundException(path);
 			return new Repository(path);
 		}
@@ -328,13 +339,16 @@ public String toString() {
 		 *
 		 * @param dir
 		 *            the location of the directory to examine.
+		 * @param fs
+		 *            the file system abstraction which will be necessary to
+		 *            perform certain file system operations.
 		 * @return true if the directory "looks like" a Git repository; false if
 		 *         it doesn't look enough like a Git directory to really be a
 		 *         Git directory.
 		 */
-		public static boolean isGitRepository(final File dir) {
-			return FS.resolve(dir, "objects").exists()
-					&& FS.resolve(dir, "refs").exists()
+		public static boolean isGitRepository(final File dir, FS fs) {
+			return fs.resolve(dir, "objects").exists()
+					&& fs.resolve(dir, "refs").exists()
 					&& isValidHead(new File(dir, Constants.HEAD));
 		}
 
@@ -371,18 +385,21 @@ private static String readFirstLine(final File head) {
 		 *
 		 * @param directory
 		 *            location to guess from. Several permutations are tried.
+		 * @param fs
+		 *            the file system abstraction which will be necessary to
+		 *            perform certain file system operations.
 		 * @return the actual directory location if a better match is found;
 		 *         null if there is no suitable match.
 		 */
-		public static File resolve(final File directory) {
-			if (isGitRepository(directory))
+		public static File resolve(final File directory, FS fs) {
+			if (isGitRepository(directory, fs))
 				return directory;
-			if (isGitRepository(new File(directory, Constants.DOT_GIT)))
+			if (isGitRepository(new File(directory, Constants.DOT_GIT), fs))
 				return new File(directory, Constants.DOT_GIT);
 
 			final String name = directory.getName();
 			final File parent = directory.getParentFile();
-			if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT)))
+			if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT), fs))
 				return new File(parent, name + Constants.DOT_GIT_EXT);
 			return null;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
index 39c7ae8f01e821eef158486434c34428c841fd5b..aa2e2521c2cb152373ee354775ffe1100f78c0ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
@@ -63,6 +63,7 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.util.FS;
 
 /** Basic daemon for the anonymous <code>git://</code> transport protocol. */
 public class Daemon {
@@ -368,7 +369,7 @@ Repository openRepository(String name) {
 		}
 
 		for (final File baseDir : exportBase) {
-			final File gitdir = FileKey.resolve(new File(baseDir, name));
+			final File gitdir = FileKey.resolve(new File(baseDir, name), FS.DETECTED);
 			if (gitdir != null && canExport(gitdir))
 				return openRepository(gitdir);
 		}
@@ -377,7 +378,7 @@ Repository openRepository(String name) {
 
 	private static Repository openRepository(final File gitdir) {
 		try {
-			return RepositoryCache.open(FileKey.exact(gitdir));
+			return RepositoryCache.open(FileKey.exact(gitdir, FS.DETECTED));
 		} catch (IOException err) {
 			// null signals it "wasn't found", which is all that is suitable
 			// for the remote client to know.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
index e7a307f80981e2de46a3b23c8fb16c96b2ad759b..20f3174b2ef6ffeae9129f4e0417334b694f9ba0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
@@ -82,10 +82,13 @@ public class OpenSshConfig {
 	 * requests are cached and are automatically updated if the user modifies
 	 * the configuration file since the last time it was cached.
 	 *
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @return a caching reader of the user's configuration file.
 	 */
-	public static OpenSshConfig get() {
-		File home = FS.userHome();
+	public static OpenSshConfig get(FS fs) {
+		File home = fs.userHome();
 		if (home == null)
 			home = new File(".").getAbsoluteFile();
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
index c30d32d9f928d7c376a5e444c2ae7da69f5f20a6..daa6f4ca2f9fb67442295fd31d027b7129ee7876 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
@@ -83,15 +83,18 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory {
 
 	@Override
 	public synchronized Session getSession(String user, String pass,
-			String host, int port) throws JSchException {
-		final OpenSshConfig.Host hc = getConfig().lookup(host);
+			String host, int port, FS fs) throws JSchException {
+		if (config == null)
+			config = OpenSshConfig.get(fs);
+
+		final OpenSshConfig.Host hc = config.lookup(host);
 		host = hc.getHostName();
 		if (port <= 0)
 			port = hc.getPort();
 		if (user == null)
 			user = hc.getUser();
 
-		final Session session = createSession(hc, user, host, port);
+		final Session session = createSession(hc, user, host, port, fs);
 		if (pass != null)
 			session.setPassword(pass);
 		final String strictHostKeyCheckingPolicy = hc
@@ -117,14 +120,17 @@ public synchronized Session getSession(String user, String pass,
 	 *            server name to connect to.
 	 * @param port
 	 *            port number of the SSH daemon (typically 22).
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @return new session instance, but otherwise unconfigured.
 	 * @throws JSchException
 	 *             the session could not be created.
 	 */
 	protected Session createSession(final OpenSshConfig.Host hc,
-			final String user, final String host, final int port)
+			final String user, final String host, final int port, FS fs)
 			throws JSchException {
-		return getJSch(hc).getSession(user, host, port);
+		return getJSch(hc, fs).getSession(user, host, port);
 	}
 
 	/**
@@ -143,58 +149,54 @@ protected Session createSession(final OpenSshConfig.Host hc,
 	 *
 	 * @param hc
 	 *            host configuration
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @return the JSch instance to use.
 	 * @throws JSchException
 	 *             the user configuration could not be created.
 	 */
-	protected JSch getJSch(final OpenSshConfig.Host hc) throws JSchException {
-		final JSch def = getDefaultJSch();
+	protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
+		if (defaultJSch == null) {
+			defaultJSch = createDefaultJSch(fs);
+			for (Object name : defaultJSch.getIdentityNames()) {
+				byIdentityFile.put((String) name, defaultJSch);
+			}
+		}
+
 		final File identityFile = hc.getIdentityFile();
 		if (identityFile == null) {
-			return def;
+			return defaultJSch;
 		}
 
 		final String identityKey = identityFile.getAbsolutePath();
 		JSch jsch = byIdentityFile.get(identityKey);
 		if (jsch == null) {
 			jsch = new JSch();
-			jsch.setHostKeyRepository(def.getHostKeyRepository());
+			jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
 			jsch.addIdentity(identityKey);
 			byIdentityFile.put(identityKey, jsch);
 		}
 		return jsch;
 	}
 
-	private JSch getDefaultJSch() throws JSchException {
-		if (defaultJSch == null) {
-			defaultJSch = createDefaultJSch();
-			for (Object name : defaultJSch.getIdentityNames()) {
-				byIdentityFile.put((String) name, defaultJSch);
-			}
-		}
-		return defaultJSch;
-	}
-
 	/**
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @return the new default JSch implementation.
 	 * @throws JSchException
 	 *             known host keys cannot be loaded.
 	 */
-	protected JSch createDefaultJSch() throws JSchException {
+	protected JSch createDefaultJSch(FS fs) throws JSchException {
 		final JSch jsch = new JSch();
-		knownHosts(jsch);
-		identities(jsch);
+		knownHosts(jsch, fs);
+		identities(jsch, fs);
 		return jsch;
 	}
 
-	private OpenSshConfig getConfig() {
-		if (config == null)
-			config = OpenSshConfig.get();
-		return config;
-	}
-
-	private static void knownHosts(final JSch sch) throws JSchException {
-		final File home = FS.userHome();
+	private static void knownHosts(final JSch sch, FS fs) throws JSchException {
+		final File home = fs.userHome();
 		if (home == null)
 			return;
 		final File known_hosts = new File(new File(home, ".ssh"), "known_hosts");
@@ -212,8 +214,8 @@ private static void knownHosts(final JSch sch) throws JSchException {
 		}
 	}
 
-	private static void identities(final JSch sch) {
-		final File home = FS.userHome();
+	private static void identities(final JSch sch, FS fs) {
+		final File home = fs.userHome();
 		if (home == null)
 			return;
 		final File sshdir = new File(home, ".ssh");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index 810b04ce40f1ff68f9e9dc39f7c74a27d6deee31..d10010fcf6894c45de1b597b3c1f1f0ee85ce99b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -44,6 +44,8 @@
 
 package org.eclipse.jgit.transport;
 
+import org.eclipse.jgit.util.FS;
+
 import com.jcraft.jsch.JSchException;
 import com.jcraft.jsch.Session;
 
@@ -109,19 +111,22 @@ public static void setInstance(final SshSessionFactory newFactory) {
 	 * @param port
 	 *            port number the server is listening for connections on. May be <=
 	 *            0 to indicate the IANA registered port of 22 should be used.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @return a session that can contact the remote host.
 	 * @throws JSchException
 	 *             the session could not be created.
 	 */
 	public abstract Session getSession(String user, String pass, String host,
-			int port) throws JSchException;
+			int port, FS fs) throws JSchException;
 
 	/**
 	 * Close (or recycle) a session to a host.
 	 *
 	 * @param session
 	 *            a session previously obtained from this factory's
-	 *            {@link #getSession(String,String, String, int)} method.s
+	 *            {@link #getSession(String,String, String, int, FS)} method.s
 	 */
 	public void releaseSession(final Session session) {
 		if (session.isConnected())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
index d25a7b6180261809358d457de29868d3dd117647..f642ac1ea841c96fb45304e2c618f2032584513a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
@@ -128,7 +128,7 @@ protected void initSession() throws TransportException {
 		final String host = uri.getHost();
 		final int port = uri.getPort();
 		try {
-			sock = sch.getSession(user, pass, host, port);
+			sock = sch.getSession(user, pass, host, port, local.getFS());
 			if (!sock.isConnected())
 				sock.connect(tms);
 		} catch (JSchException je) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 2fe3cb93265bf890e862b420d17cbb0dd2f14ce5..e1988a6c85fb056259226d63f933c688a6b32e7e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -66,6 +66,7 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.TransferConfig;
+import org.eclipse.jgit.util.FS;
 
 /**
  * Connects two Git repositories together and copies objects between them.
@@ -323,9 +324,11 @@ private static boolean doesNotExist(final RemoteConfig cfg) {
 	 *
 	 * @param remote
 	 *            location of the remote repository.
+	 * @param fs
+	 *            type of filesystem the local repository is stored on.
 	 * @return true if the protocol is supported.
 	 */
-	public static boolean canHandleProtocol(final URIish remote) {
+	public static boolean canHandleProtocol(final URIish remote, final FS fs) {
 		if (TransportGitSsh.canHandle(remote))
 			return true;
 
@@ -341,10 +344,10 @@ else if (TransportGitAnon.canHandle(remote))
 		else if (TransportAmazonS3.canHandle(remote))
 			return true;
 
-		else if (TransportBundleFile.canHandle(remote))
+		else if (TransportBundleFile.canHandle(remote, fs))
 			return true;
 
-		else if (TransportLocal.canHandle(remote))
+		else if (TransportLocal.canHandle(remote, fs))
 			return true;
 
 		return false;
@@ -378,10 +381,10 @@ else if (TransportGitAnon.canHandle(remote))
 		else if (TransportAmazonS3.canHandle(remote))
 			return new TransportAmazonS3(local, remote);
 
-		else if (TransportBundleFile.canHandle(remote))
+		else if (TransportBundleFile.canHandle(remote, local.getFS()))
 			return new TransportBundleFile(local, remote);
 
-		else if (TransportLocal.canHandle(remote))
+		else if (TransportLocal.canHandle(remote, local.getFS()))
 			return new TransportLocal(local, remote);
 
 		throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, remote));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index bcf6e873fea3dc3cd069bf97d02b48b3410a8812..56a5c9796d7c9c04d8a6c01ff13d347f463edc1d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -69,7 +69,6 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.SymbolicRef;
 import org.eclipse.jgit.lib.Ref.Storage;
-import org.eclipse.jgit.util.FS;
 
 /**
  * Transport over the non-Git aware Amazon S3 protocol.
@@ -130,7 +129,7 @@ static boolean canHandle(final URIish uri) {
 		Properties props = null;
 		File propsFile = new File(local.getDirectory(), uri.getUser());
 		if (!propsFile.isFile())
-			propsFile = new File(FS.userHome(), uri.getUser());
+			propsFile = new File(local.getFS().userHome(), uri.getUser());
 		if (propsFile.isFile()) {
 			try {
 				props = AmazonS3.properties(propsFile);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java
index 0245818fe3da18b6e9e3e58b6eb83b84f51840a3..c47833f21cf08715f3011b81c575e8a1ec79f912 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java
@@ -58,13 +58,13 @@
 import org.eclipse.jgit.util.FS;
 
 class TransportBundleFile extends Transport implements TransportBundle {
-	static boolean canHandle(final URIish uri) {
+	static boolean canHandle(final URIish uri, FS fs) {
 		if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null
 				|| uri.getPass() != null || uri.getPath() == null)
 			return false;
 
 		if ("file".equals(uri.getScheme()) || uri.getScheme() == null) {
-			final File f = FS.resolve(new File("."), uri.getPath());
+			final File f = fs.resolve(new File("."), uri.getPath());
 			return f.isFile() || f.getName().endsWith(".bundle");
 		}
 
@@ -75,7 +75,7 @@ static boolean canHandle(final URIish uri) {
 
 	TransportBundleFile(final Repository local, final URIish uri) {
 		super(local, uri);
-		bundle = FS.resolve(new File("."), uri.getPath()).getAbsoluteFile();
+		bundle = local.getFS().resolve(new File("."), uri.getPath()).getAbsoluteFile();
 	}
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index cf4dbd5392de2903cea391c30cb893607d799d87..08fd8901d8d93d6ddac31b47f705b53cb78edf21 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -91,13 +91,13 @@
 class TransportLocal extends Transport implements PackTransport {
 	private static final String PWD = ".";
 
-	static boolean canHandle(final URIish uri) {
+	static boolean canHandle(final URIish uri, FS fs) {
 		if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null
 				|| uri.getPass() != null || uri.getPath() == null)
 			return false;
 
 		if ("file".equals(uri.getScheme()) || uri.getScheme() == null)
-			return FS.resolve(new File(PWD), uri.getPath()).isDirectory();
+			return fs.resolve(new File(PWD), uri.getPath()).isDirectory();
 		return false;
 	}
 
@@ -106,7 +106,7 @@ static boolean canHandle(final URIish uri) {
 	TransportLocal(final Repository local, final URIish uri) {
 		super(local, uri);
 
-		File d = FS.resolve(new File(PWD), uri.getPath()).getAbsoluteFile();
+		File d = local.getFS().resolve(new File(PWD), uri.getPath()).getAbsoluteFile();
 		if (new File(d, Constants.DOT_GIT).isDirectory())
 			d = new File(d, Constants.DOT_GIT);
 		remoteGitDir = d;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 19db39a8fff1879ba3e14a78ef92be9a6bc1b9cb..8dfab8aa5775be84aaf72087593248355993c0ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -65,6 +65,7 @@
  */
 public class FileTreeIterator extends WorkingTreeIterator {
 	private final File directory;
+	private final FS fs;
 
 	/**
 	 * Create a new iterator to traverse the given directory and its children.
@@ -72,9 +73,13 @@ public class FileTreeIterator extends WorkingTreeIterator {
 	 * @param root
 	 *            the starting directory. This directory should correspond to
 	 *            the root of the repository.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 */
-	public FileTreeIterator(final File root) {
+	public FileTreeIterator(final File root, FS fs) {
 		directory = root;
+		this.fs = fs;
 		init(entries());
 	}
 
@@ -83,20 +88,24 @@ public FileTreeIterator(final File root) {
 	 *
 	 * @param p
 	 *            the parent iterator we were created from.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @param root
 	 *            the subdirectory. This should be a directory contained within
 	 *            the parent directory.
 	 */
-	protected FileTreeIterator(final FileTreeIterator p, final File root) {
+	protected FileTreeIterator(final FileTreeIterator p, final File root, FS fs) {
 		super(p);
 		directory = root;
+		this.fs = fs;
 		init(entries());
 	}
 
 	@Override
 	public AbstractTreeIterator createSubtreeIterator(final Repository repo)
 			throws IncorrectObjectTypeException, IOException {
-		return new FileTreeIterator(this, ((FileEntry) current()).file);
+		return new FileTreeIterator(this, ((FileEntry) current()).file, fs);
 	}
 
 	private Entry[] entries() {
@@ -105,7 +114,7 @@ private Entry[] entries() {
 			return EOF;
 		final Entry[] r = new Entry[all.length];
 		for (int i = 0; i < r.length; i++)
-			r[i] = new FileEntry(all[i]);
+			r[i] = new FileEntry(all[i], fs);
 		return r;
 	}
 
@@ -121,7 +130,7 @@ static public class FileEntry extends Entry {
 
 		private long lastModified;
 
-		FileEntry(final File f) {
+		FileEntry(final File f, FS fs) {
 			file = f;
 
 			if (f.isDirectory()) {
@@ -129,7 +138,7 @@ static public class FileEntry extends Entry {
 					mode = FileMode.GITLINK;
 				else
 					mode = FileMode.TREE;
-			} else if (FS.INSTANCE.canExecute(file))
+			} else if (fs.canExecute(file))
 				mode = FileMode.EXECUTABLE_FILE;
 			else
 				mode = FileMode.REGULAR_FILE;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index c4f4242f90012821b94a43ae69af883548474c4c..b8d433762eb15bbb14701d83b74881ab782f4dfd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -49,19 +49,28 @@
 
 /** Abstraction to support various file system operations not in Java. */
 public abstract class FS {
-	/** The implementation selected for this operating system and JRE. */
-	public static final FS INSTANCE;
+	/** The auto-detected implementation selected for this operating system and JRE. */
+	public static final FS DETECTED;
 
 	static {
 		if (FS_Win32.detect()) {
 			if (FS_Win32_Cygwin.detect())
-				INSTANCE = new FS_Win32_Cygwin();
+				DETECTED = new FS_Win32_Cygwin();
 			else
-				INSTANCE = new FS_Win32();
+				DETECTED = new FS_Win32();
 		} else if (FS_POSIX_Java6.detect())
-			INSTANCE = new FS_POSIX_Java6();
+			DETECTED = new FS_POSIX_Java6();
 		else
-			INSTANCE = new FS_POSIX_Java5();
+			DETECTED = new FS_POSIX_Java5();
+	}
+
+	private final File userHome;
+
+	/**
+	 * Constructs a file system abstraction.
+	 */
+	protected FS() {
+		this.userHome = userHomeImpl();
 	}
 
 	/**
@@ -117,29 +126,7 @@ public abstract class FS {
 	 * @return the translated path. <code>new File(dir,name)</code> if this
 	 *         platform does not require path name translation.
 	 */
-	public static File resolve(final File dir, final String name) {
-		return INSTANCE.resolveImpl(dir, name);
-	}
-
-	/**
-	 * Resolve this file to its actual path name that the JRE can use.
-	 * <p>
-	 * This method can be relatively expensive. Computing a translation may
-	 * require forking an external process per path name translated. Callers
-	 * should try to minimize the number of translations necessary by caching
-	 * the results.
-	 * <p>
-	 * Not all platforms and JREs require path name translation. Currently only
-	 * Cygwin on Win32 require translation for Cygwin based paths.
-	 *
-	 * @param dir
-	 *            directory relative to which the path name is.
-	 * @param name
-	 *            path name to translate.
-	 * @return the translated path. <code>new File(dir,name)</code> if this
-	 *         platform does not require path name translation.
-	 */
-	protected File resolveImpl(final File dir, final String name) {
+	public File resolve(final File dir, final String name) {
 		final File abspn = new File(name);
 		if (abspn.isAbsolute())
 			return abspn;
@@ -157,12 +144,8 @@ protected File resolveImpl(final File dir, final String name) {
 	 *
 	 * @return the user's home directory; null if the user does not have one.
 	 */
-	public static File userHome() {
-		return USER_HOME.home;
-	}
-
-	private static class USER_HOME {
-		static final File home = INSTANCE.userHomeImpl();
+	public File userHome() {
+		return userHome;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index f727084860497b0b3db3c57c01234d68267546be..39f2c03a0670e14cf4e45ed4a3c3a3a7cabf9fe9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -72,7 +72,7 @@ public String run() {
 		return false;
 	}
 
-	protected File resolveImpl(final File dir, final String pn) {
+	public File resolve(final File dir, final String pn) {
 		try {
 			final Process p;
 
@@ -103,7 +103,7 @@ protected File resolveImpl(final File dir, final String pn) {
 			// Fall through and use the default return.
 			//
 		}
-		return super.resolveImpl(dir, pn);
+		return super.resolve(dir, pn);
 	}
 
 	@Override
@@ -116,6 +116,6 @@ public String run() {
 				});
 		if (home == null || home.length() == 0)
 			return super.userHomeImpl();
-		return resolveImpl(new File("."), home);
+		return resolve(new File("."), home);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 771e77058aa10448c451f53ddab44e50486d8516..9d7feb08f2b1eaac544932bb7cd0f9133f66ac36 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -72,8 +72,8 @@ public String getProperty(String key) {
 			return System.getProperty(key);
 		}
 
-		public FileBasedConfig openUserConfig() {
-			final File home = FS.userHome();
+		public FileBasedConfig openUserConfig(FS fs) {
+			final File home = fs.userHome();
 			return new FileBasedConfig(new File(home, ".gitconfig"));
 		}
 
@@ -136,9 +136,12 @@ public static void setInstance(SystemReader newReader) {
 	public abstract String getProperty(String key);
 
 	/**
+	 * @param fs
+	 *            the file system abstraction which will be necessary to
+	 *            perform certain file system operations.
 	 * @return the git configuration found in the user home
 	 */
-	public abstract FileBasedConfig openUserConfig();
+	public abstract FileBasedConfig openUserConfig(FS fs);
 
 	/**
 	 * @return the current system time