diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 5c8935c49080a3bf29cb527de5224919633757e5..ec9b1d7ac361b483194b39c536ca5dd2653a81d7 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -51,6 +51,7 @@
 import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -58,6 +59,7 @@
 
 import junit.framework.TestCase;
 
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileBasedConfig;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
@@ -124,6 +126,7 @@ public void run() {
 		mockSystemReader = new MockSystemReader();
 		mockSystemReader.userGitConfig = new FileBasedConfig(new File(trash,
 				"usergitconfig"));
+		ceilTestDirectories(getCeilings());
 		SystemReader.setInstance(mockSystemReader);
 
 		final long now = mockSystemReader.getCurrentTime();
@@ -142,6 +145,25 @@ public void run() {
 		WindowCache.reconfigure(c);
 	}
 
+
+	protected List<File> getCeilings() {
+		return Collections.singletonList(trash.getParentFile().getAbsoluteFile());
+	}
+
+	private void ceilTestDirectories(List<File> ceilings) {
+		mockSystemReader.setProperty(Constants.GIT_CEILING_DIRECTORIES_KEY, makePath(ceilings));
+	}
+
+	private String makePath(List<?> objects) {
+		final StringBuilder stringBuilder = new StringBuilder();
+		for (Object object : objects) {
+			if (stringBuilder.length() > 0)
+				stringBuilder.append(File.pathSeparatorChar);
+			stringBuilder.append(object.toString());
+		}
+		return stringBuilder.toString();
+	}
+
 	@Override
 	protected void tearDown() throws Exception {
 		RepositoryCache.clear();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
index 18cf8be467295f3b68e4865b206ea0ace7717ef7..b4c5660a6d20b361f793e86bc9a3251a7a7b258b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
@@ -49,15 +49,20 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.jgit.awtui.AwtAuthenticator;
 import org.eclipse.jgit.awtui.AwtSshSessionFactory;
 import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.pgm.opt.CmdLineParser;
 import org.eclipse.jgit.pgm.opt.SubcommandHandler;
 import org.eclipse.jgit.util.CachedAuthenticator;
+import org.eclipse.jgit.util.SystemReader;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.CmdLineException;
 import org.kohsuke.args4j.ExampleMode;
@@ -158,13 +163,50 @@ private void execute(final String[] argv) throws Exception {
 
 		final TextBuiltin cmd = subcommand;
 		if (cmd.requiresRepository()) {
+			if (gitdir == null) {
+				String gitDirEnv = SystemReader.getInstance().getenv(Constants.GIT_DIR_KEY);
+				if (gitDirEnv != null)
+					gitdir = new File(gitDirEnv);
+			}
 			if (gitdir == null)
 				gitdir = findGitDir();
+
+			File gitworktree;
+			String gitWorkTreeEnv = SystemReader.getInstance().getenv(Constants.GIT_WORK_TREE_KEY);
+			if (gitWorkTreeEnv != null)
+				gitworktree = new File(gitWorkTreeEnv);
+			else
+				gitworktree = null;
+
+			File indexfile;
+			String indexFileEnv = SystemReader.getInstance().getenv(Constants.GIT_INDEX_KEY);
+			if (indexFileEnv != null)
+				indexfile = new File(indexFileEnv);
+			else
+				indexfile = null;
+
+			File objectdir;
+			String objectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_OBJECT_DIRECTORY_KEY);
+			if (objectDirEnv != null)
+				objectdir = new File(objectDirEnv);
+			else
+				objectdir = null;
+
+			File[] altobjectdirs;
+			String altObjectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
+			if (altObjectDirEnv != null) {
+				String[] parserdAltObjectDirEnv = altObjectDirEnv.split(File.pathSeparator);
+				altobjectdirs = new File[parserdAltObjectDirEnv.length];
+				for (int i = 0; i < parserdAltObjectDirEnv.length; i++)
+					altobjectdirs[i] = new File(parserdAltObjectDirEnv[i]);
+			} else
+				altobjectdirs = null;
+
 			if (gitdir == null || !gitdir.isDirectory()) {
 				System.err.println("error: can't find git directory");
 				System.exit(1);
 			}
-			cmd.init(new Repository(gitdir), gitdir);
+			cmd.init(new Repository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir);
 		} else {
 			cmd.init(null, gitdir);
 		}
@@ -177,12 +219,21 @@ private void execute(final String[] argv) throws Exception {
 	}
 
 	private static File findGitDir() {
-		File current = new File(".").getAbsoluteFile();
+		Set<String> ceilingDirectories = new HashSet<String>();
+		String ceilingDirectoriesVar = SystemReader.getInstance().getenv(
+				Constants.GIT_CEILING_DIRECTORIES_KEY);
+		if (ceilingDirectoriesVar != null) {
+			ceilingDirectories.addAll(Arrays.asList(ceilingDirectoriesVar
+					.split(File.pathSeparator)));
+		}
+		File current = new File("").getAbsoluteFile();
 		while (current != null) {
 			final File gitDir = new File(current, ".git");
 			if (gitDir.isDirectory())
 				return gitDir;
 			current = current.getParentFile();
+			if (ceilingDirectories.contains(current.getPath()))
+				break;
 		}
 		return null;
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
index d1dfdf4fae63192d08697a657d7117798ba817f5..e29e9e7214b4ed9a3152e6405c230ec39c108901 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
  * and other copyright owners as documented in the project's IP log.
  *
@@ -78,6 +78,161 @@ public void test001_Initalize() {
 		assertEquals(23, HEAD.length());
 	}
 
+	public void test000_openRepoBadArgs() throws IOException {
+		try {
+			new Repository(null, null);
+			fail("Must pass either GIT_DIR or GIT_WORK_TREE");
+		} catch (IllegalArgumentException e) {
+			assertEquals(
+					"Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor",
+					e.getMessage());
+		}
+	}
+
+	/**
+	 * Check the default rules for looking up directories and files within a
+	 * repo when the gitDir is given.
+	 *
+	 * @throws IOException
+	 */
+	public void test000_openrepo_default_gitDirSet() throws IOException {
+		File repo1Parent = new File(trash.getParentFile(), "r1");
+		Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
+		repo1initial.create();
+		repo1initial.close();
+
+		File theDir = new File(repo1Parent, ".git");
+		Repository r = new Repository(theDir, null);
+		assertEqualsPath(theDir, r.getDirectory());
+		assertEqualsPath(repo1Parent, r.getWorkDir());
+		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+		assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+	}
+
+	/**
+	 * Check that we can pass both a git directory and a work tree
+	 * repo when the gitDir is given.
+	 *
+	 * @throws IOException
+	 */
+	public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException {
+		File repo1Parent = new File(trash.getParentFile(), "r1");
+		Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
+		repo1initial.create();
+		repo1initial.close();
+
+		File theDir = new File(repo1Parent, ".git");
+		Repository r = new Repository(theDir, repo1Parent.getParentFile());
+		assertEqualsPath(theDir, r.getDirectory());
+		assertEqualsPath(repo1Parent.getParentFile(), r.getWorkDir());
+		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+		assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+	}
+
+	/**
+	 * Check the default rules for looking up directories and files within a
+	 * repo when the workTree is given.
+	 *
+	 * @throws IOException
+	 */
+	public void test000_openrepo_default_workDirSet() throws IOException {
+		File repo1Parent = new File(trash.getParentFile(), "r1");
+		Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
+		repo1initial.create();
+		repo1initial.close();
+
+		File theDir = new File(repo1Parent, ".git");
+		Repository r = new Repository(null, repo1Parent);
+		assertEqualsPath(theDir, r.getDirectory());
+		assertEqualsPath(repo1Parent, r.getWorkDir());
+		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+		assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+	}
+
+	/**
+	 * Check that worktree config has an effect, given absolute path.
+	 *
+	 * @throws IOException
+	 */
+	public void test000_openrepo_default_absolute_workdirconfig()
+			throws IOException {
+		File repo1Parent = new File(trash.getParentFile(), "r1");
+		File workdir = new File(trash.getParentFile(), "rw");
+		workdir.mkdir();
+		Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
+		repo1initial.create();
+		repo1initial.getConfig().setString("core", null, "worktree",
+				workdir.getAbsolutePath());
+		repo1initial.getConfig().save();
+		repo1initial.close();
+
+		File theDir = new File(repo1Parent, ".git");
+		Repository r = new Repository(theDir, null);
+		assertEqualsPath(theDir, r.getDirectory());
+		assertEqualsPath(workdir, r.getWorkDir());
+		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+		assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+	}
+
+	/**
+	 * Check that worktree config has an effect, given a relative path.
+	 *
+	 * @throws IOException
+	 */
+	public void test000_openrepo_default_relative_workdirconfig()
+			throws IOException {
+		File repo1Parent = new File(trash.getParentFile(), "r1");
+		File workdir = new File(trash.getParentFile(), "rw");
+		workdir.mkdir();
+		Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
+		repo1initial.create();
+		repo1initial.getConfig()
+				.setString("core", null, "worktree", "../../rw");
+		repo1initial.getConfig().save();
+		repo1initial.close();
+
+		File theDir = new File(repo1Parent, ".git");
+		Repository r = new Repository(theDir, null);
+		assertEqualsPath(theDir, r.getDirectory());
+		assertEqualsPath(workdir, r.getWorkDir());
+		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
+		assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
+	}
+
+	/**
+	 * Check that the given index file is honored and the alternate object
+	 * directories too
+	 *
+	 * @throws IOException
+	 */
+	public void test000_openrepo_alternate_index_file_and_objdirs()
+			throws IOException {
+		File repo1Parent = new File(trash.getParentFile(), "r1");
+		File indexFile = new File(trash, "idx");
+		File objDir = new File(trash, "../obj");
+		File[] altObjDirs = new File[] { db.getObjectsDirectory() };
+		Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
+		repo1initial.create();
+		repo1initial.close();
+
+		File theDir = new File(repo1Parent, ".git");
+		Repository r = new Repository(theDir, null, objDir, altObjDirs,
+				indexFile);
+		assertEqualsPath(theDir, r.getDirectory());
+		assertEqualsPath(theDir.getParentFile(), r.getWorkDir());
+		assertEqualsPath(indexFile, r.getIndexFile());
+		assertEqualsPath(objDir, r.getObjectsDirectory());
+		assertNotNull(r.mapCommit("6db9c2ebf75590eef973081736730a9ea169a0c4"));
+		// Must close or the default repo pack files created by this test gets
+		// locked via the alternate object directories on Windows.
+		r.close();
+	}
+
+	protected void assertEqualsPath(File expected, File actual)
+			throws IOException {
+		assertEquals(expected.getCanonicalPath(), actual.getCanonicalPath());
+	}
+
 	public void test002_WriteEmptyTree() throws IOException {
 		// One of our test packs contains the empty tree object. If the pack is
 		// open when we create it we won't write the object file out as a loose
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index c1d78be5afdc97a5f2a3f0a1e60e2d98c939c5c2..05de98c1aa3396a885382b12cda8fe16e8aa59b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -262,6 +262,40 @@ public final class Constants {
 	/** The environment variable that contains the commiter's email */
 	public static final String GIT_COMMITTER_EMAIL_KEY = "GIT_COMMITTER_EMAIL";
 
+	/**
+	 * The environment variable that limits how close to the root of the file
+	 * systems JGit will traverse when looking for a repository root.
+	 */
+	public static final String GIT_CEILING_DIRECTORIES_KEY = "GIT_CEILING_DIRECTORIES";
+
+	/**
+	 * The environment variable that tells us which directory is the ".git"
+	 * directory
+	 */
+	public static final String GIT_DIR_KEY = "GIT_DIR";
+
+	/**
+	 * The environment variable that tells us which directory is the working
+	 * directory.
+	 */
+	public static final String GIT_WORK_TREE_KEY = "GIT_WORK_TREE";
+
+	/**
+	 * The environment variable that tells us which file holds the Git index.
+	 */
+	public static final String GIT_INDEX_KEY = "GIT_INDEX";
+
+	/**
+	 * The environment variable that tells us where objects are stored
+	 */
+	public static final String GIT_OBJECT_DIRECTORY_KEY = "GIT_OBJECT_DIRECTORY";
+
+	/**
+	 * The environment variable that tells us where to look for objects, besides
+	 * the default objects directory.
+	 */
+	public static final String GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY = "GIT_ALTERNATE_OBJECT_DIRECTORIES";
+
 	/** Default value for the user name if no other information is available */
 	public static final String UNKNOWN_USER_DEFAULT = "unknown-user";
 
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 1714d7077b9ea1472c441d86805837bf9b8bd75c..688957b95bdbc5daca9a90454d55849baa51f5eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java
@@ -137,7 +137,7 @@ else if (o1.length > o2.length)
 	 */
 	public GitIndex(Repository db) {
 		this.db = db;
-		this.cacheFile = new File(db.getDirectory(), "index");
+		this.cacheFile = db.getIndexFile();
 	}
 
 	/**
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 297d85f83ea8803e758906bf66e5f96c91523bd7..f94d0d5fb438fe8691f0bfa587efef3eda770024 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java
@@ -84,14 +84,19 @@ public class ObjectDirectory extends ObjectDatabase {
 
 	private final AtomicReference<PackList> packList;
 
+	private final File[] alternateObjectDir;
+
 	/**
 	 * Initialize a reference to an on-disk object directory.
 	 *
 	 * @param dir
 	 *            the location of the <code>objects</code> directory.
+	 * @param alternateObjectDir
+	 *            a list of alternate object directories
 	 */
-	public ObjectDirectory(final File dir) {
+	public ObjectDirectory(final File dir, File[] alternateObjectDir) {
 		objects = dir;
+		this.alternateObjectDir = alternateObjectDir;
 		infoDirectory = new File(objects, "info");
 		packDirectory = new File(objects, "pack");
 		alternatesFile = new File(infoDirectory, "alternates");
@@ -422,15 +427,21 @@ private Set<String> listPackDirectory() {
 
 	@Override
 	protected ObjectDatabase[] loadAlternates() throws IOException {
-		final BufferedReader br = open(alternatesFile);
 		final List<ObjectDatabase> l = new ArrayList<ObjectDatabase>(4);
-		try {
-			String line;
-			while ((line = br.readLine()) != null) {
-				l.add(openAlternate(line));
+		if (alternateObjectDir != null) {
+			for (File d : alternateObjectDir) {
+				l.add(openAlternate(d));
+			}
+		} else {
+			final BufferedReader br = open(alternatesFile);
+			try {
+				String line;
+				while ((line = br.readLine()) != null) {
+					l.add(openAlternate(line));
+				}
+			} finally {
+				br.close();
 			}
-		} finally {
-			br.close();
 		}
 
 		if (l.isEmpty()) {
@@ -447,12 +458,16 @@ private static BufferedReader open(final File f)
 	private ObjectDatabase openAlternate(final String location)
 			throws IOException {
 		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));
 			return new AlternateRepositoryDatabase(db);
 		}
-		return new ObjectDirectory(objdir);
+		return new ObjectDirectory(objdir, null);
 	}
 
 	private static final class PackList {
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 f576b057ca62500ebd46b616c3b6c75e9de38371..a50132bc632f1a19b4001ac100b9726d3bb8a4f0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -109,9 +109,17 @@ public class Repository {
 	private final List<RepositoryListener> listeners = new Vector<RepositoryListener>(); // thread safe
 	static private final List<RepositoryListener> allListeners = new Vector<RepositoryListener>(); // thread safe
 
+	private File workDir;
+
+	private File indexFile;
+
 	/**
 	 * Construct a representation of a Git repository.
 	 *
+	 * The work tree, object directory, alternate object directories and index
+	 * file locations are deduced from the given git directory and the default
+	 * rules.
+	 *
 	 * @param d
 	 *            GIT_DIR (the location of the repository metadata).
 	 * @throws IOException
@@ -119,9 +127,71 @@ public class Repository {
 	 *             accessed.
 	 */
 	public Repository(final File d) throws IOException {
-		gitDir = d.getAbsoluteFile();
-		refs = new RefDatabase(this);
-		objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"));
+		this(d, null, null, null, null); // go figure it out
+	}
+
+	/**
+	 * Construct a representation of a Git repository.
+	 *
+	 * The work tree, object directory, alternate object directories and index
+	 * file locations are deduced from the given git directory and the default
+	 * rules.
+	 *
+	 * @param d
+	 *            GIT_DIR (the location of the repository metadata). May be
+	 *            null work workTree is set
+	 * @param workTree
+	 *            GIT_WORK_TREE (the root of the checkout). May be null for
+	 *            default value.
+	 * @throws IOException
+	 *             the repository appears to already exist but cannot be
+	 *             accessed.
+	 */
+	public Repository(final File d, final File workTree) throws IOException {
+		this(d, workTree, null, null, null); // go figure it out
+	}
+
+	/**
+	 * 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.
+	 * @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) throws IOException {
+
+		if (workTree != null) {
+			workDir = workTree;
+			if (d == null)
+				gitDir = new File(workTree, ".git");
+			else
+				gitDir = d;
+		} else {
+			if (d != null)
+				gitDir = d;
+			else
+				throw new IllegalArgumentException("Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor");
+		}
 
 		final FileBasedConfig userConfig;
 		userConfig = SystemReader.getInstance().openUserConfig();
@@ -136,14 +206,41 @@ public Repository(final File d) throws IOException {
 		}
 		config = new RepositoryConfig(userConfig, FS.resolve(gitDir, "config"));
 
-		if (objectDatabase.exists()) {
-			try {
-				getConfig().load();
-			} catch (ConfigInvalidException e1) {
-				IOException e2 = new IOException("Unknown repository format");
-				e2.initCause(e1);
-				throw e2;
+		try {
+			getConfig().load();
+		} catch (ConfigInvalidException e1) {
+			IOException e2 = new IOException("Unknown repository format");
+			e2.initCause(e1);
+			throw e2;
+		}
+
+		if (workDir == null) {
+			if (d != null) {
+				// Only read core.worktree if GIT_DIR is set explicitly. See
+				// git-config(1).
+				String workTreeConfig = getConfig().getString("core", null, "worktree");
+				if (workTreeConfig != null) {
+					workDir = FS.resolve(d, workTreeConfig);
+				} else {
+					workDir = gitDir.getParentFile();
+				}
 			}
+		}
+
+		refs = new RefDatabase(this);
+		if (objectDir != null)
+			objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""),
+					alternateObjectDir);
+		else
+			objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"),
+					alternateObjectDir);
+
+		if (indexFile != null)
+			this.indexFile = indexFile;
+		else
+			this.indexFile = new File(gitDir, "index");
+
+		if (objectDatabase.exists()) {
 			final String repositoryFormatVersion = getConfig().getString(
 					"core", null, "repositoryFormatVersion");
 			if (!"0".equals(repositoryFormatVersion)) {
@@ -959,6 +1056,13 @@ public GitIndex getIndex() throws IOException {
 		return index;
 	}
 
+	/**
+	 * @return the index file location
+	 */
+	public File getIndexFile() {
+		return indexFile;
+	}
+
 	static byte[] gitInternalSlash(byte[] bytes) {
 		if (File.separatorChar == '/')
 			return bytes;
@@ -1084,7 +1188,17 @@ public static String stripWorkDir(File workDir, File file) {
 	 * @return the workdir file, i.e. where the files are checked out
 	 */
 	public File getWorkDir() {
-		return getDirectory().getParentFile();
+		return workDir;
+	}
+
+	/**
+	 * Override default workdir
+	 *
+	 * @param workTree
+	 *            the work tree directory
+	 */
+	public void setWorkDir(File workTree) {
+		this.workDir = workTree;
 	}
 
 	/**