[SCM] UNNAMED PROJECT branch, upstream, updated. upstream/3.0.0

Jakub Adam jakub.adam at ktknet.cz
Mon Jun 24 16:32:42 UTC 2013


The following commit has been merged in the upstream branch:
commit 5b30596ed540bca817081f0846a8ce4e15f23d88
Author: Jakub Adam <jakub.adam at ktknet.cz>
Date:   Tue Jun 11 19:22:47 2013 +0200

    Imported Upstream version 3.0.0

diff --git a/README.md b/README.md
index 8d9c137..6e6c0c7 100644
--- a/README.md
+++ b/README.md
@@ -118,8 +118,6 @@ There are some missing features:
 
 - gitattributes support
 
-- Recursive merge strategy
-
 
 Support
 -------
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 392cb5c..54c3eef 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -3,15 +3,14 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Import-Package: org.apache.tools.ant,
- org.apache.tools.ant.types,
- org.eclipse.jgit.ant.tasks;version="2.3.1",
+ org.eclipse.jgit.ant.tasks;version="3.0.0",
+ org.eclipse.jgit.internal.storage.file;version="3.0.0",
  org.eclipse.jgit.junit,
- org.eclipse.jgit.lib;version="2.3.1",
- org.eclipse.jgit.storage.file;version="2.3.1",
- org.eclipse.jgit.util;version="2.3.1",
+ org.eclipse.jgit.lib;version="3.0.0",
+ org.eclipse.jgit.util;version="3.0.0",
  org.hamcrest;version="1.1.0",
  org.junit;version="[4.0.0,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index 9b14073..068776a 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java b/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java
index 760d0dc..7cd5b74 100644
--- a/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java
+++ b/org.eclipse.jgit.ant.test/src/org/eclipse/jgit/ant/tasks/GitCloneTaskTest.java
@@ -51,8 +51,8 @@ import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.DefaultLogger;
 import org.apache.tools.ant.Project;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.util.FS;
 import org.junit.Before;
 import org.junit.Test;
@@ -99,7 +99,7 @@ public class GitCloneTaskTest extends LocalDiskRepositoryTestCase {
 
 	@Test
 	public void shouldCloneAValidGitRepository() throws Exception {
-		FileRepository repo = createBareRepository();
+		Repository repo = createBareRepository();
 		File directory = repo.getDirectory();
 		task.setUri("file://" + directory);
 		task.execute();
@@ -109,7 +109,7 @@ public class GitCloneTaskTest extends LocalDiskRepositoryTestCase {
 
 	@Test
 	public void shouldCreateABareCloneOfAValidGitRepository() throws Exception {
-		FileRepository repo = createBareRepository();
+		Repository repo = createBareRepository();
 		File directory = repo.getDirectory();
 		task.setUri("file://" + directory);
 		task.setBare(true);
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index ccdfbad..439fe93 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -2,9 +2,10 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.apache.tools.ant
+Import-Package: org.apache.tools.ant,
+  org.eclipse.jgit.storage.file;version="[3.0.0,3.1.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="2.3.1"
+Export-Package: org.eclipse.jgit.ant.tasks;version="3.0.0"
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 9be0db8..92a1007 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -48,7 +48,7 @@
 	<parent>
 		<groupId>org.eclipse.jgit</groupId>
 		<artifactId>org.eclipse.jgit-parent</artifactId>
-		<version>2.3.1.201302201838-r</version>
+		<version>3.0.0.201306101825-r</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java
index ebc48ba..c76ae2a 100644
--- a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java
+++ b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java
@@ -53,9 +53,9 @@ import org.apache.tools.ant.types.FileSet;
 import org.apache.tools.ant.types.resources.Union;
 import org.eclipse.jgit.api.AddCommand;
 import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FS;
 
 /**
diff --git a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java
index a40dc2e..14c4bc5 100644
--- a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java
+++ b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java
@@ -54,7 +54,7 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 
 /**
  * Checkout a branch or paths to the working tree.
- * 
+ *
  * @see <a
  *      href="http://www.kernel.org/pub/software/scm/git/docs/git-checkout.html"
  *      >git-checkout(1)</a>
diff --git a/org.eclipse.jgit.console/META-INF/MANIFEST.MF b/org.eclipse.jgit.console/META-INF/MANIFEST.MF
index 0e693d7..3e8c836 100644
--- a/org.eclipse.jgit.console/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.console/META-INF/MANIFEST.MF
@@ -3,12 +3,11 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.console
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.6
-Export-Package: org.eclipse.jgit.console;version="2.3.1"
-Import-Package: org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.nls;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)"
-Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
+Export-Package: org.eclipse.jgit.console;version="3.0.0"
+Import-Package: org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.nls;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)"
diff --git a/org.eclipse.jgit.console/plugin.properties b/org.eclipse.jgit.console/plugin.properties
index 988e716..73a746b 100644
--- a/org.eclipse.jgit.console/plugin.properties
+++ b/org.eclipse.jgit.console/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit Console User Interface
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.console/pom.xml b/org.eclipse.jgit.console/pom.xml
index 8c9708f..32e4d77 100644
--- a/org.eclipse.jgit.console/pom.xml
+++ b/org.eclipse.jgit.console/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.console</artifactId>
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 216a73b..1278d7e 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -2,23 +2,22 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Export-Package: 
- org.eclipse.jgit.http.server;version="2.3.1",
- org.eclipse.jgit.http.server.glue;version="2.3.1",
- org.eclipse.jgit.http.server.resolver;version="2.3.1"
+ org.eclipse.jgit.http.server;version="3.0.0",
+ org.eclipse.jgit.http.server.glue;version="3.0.0",
+ org.eclipse.jgit.http.server.resolver;version="3.0.0"
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
  javax.servlet.http;version="[2.5.0,3.0.0)",
- org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.nls;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport.resolver;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util.io;version="[2.3.1,2.4.0)"
+ org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.nls;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport.resolver;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)"
diff --git a/org.eclipse.jgit.http.server/plugin.properties b/org.eclipse.jgit.http.server/plugin.properties
index 37074ac..d891b50 100644
--- a/org.eclipse.jgit.http.server/plugin.properties
+++ b/org.eclipse.jgit.http.server/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit HTTP Server
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index b4f9b40..a8b5460 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
index d217fe1..bb6efb8 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
@@ -52,9 +52,9 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.file.PackFile;
 import org.eclipse.jgit.lib.ObjectDatabase;
-import org.eclipse.jgit.storage.file.ObjectDirectory;
-import org.eclipse.jgit.storage.file.PackFile;
 
 /** Sends the current list of pack files, sorted most recent first. */
 class InfoPacksServlet extends HttpServlet {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
index 019ec90..511cdf1 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
@@ -56,8 +56,8 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.ObjectDirectory;
 
 /**
  * Requires the target {@link Repository} to be available via local filesystem.
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
index 8486512..babc036 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ObjectFileServlet.java
@@ -60,8 +60,8 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.ObjectDirectory;
 
 /** Sends any object from {@code GIT_DIR/objects/??/0 38}, or any pack file. */
 abstract class ObjectFileServlet extends HttpServlet {
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 46a1efa..8f0a992 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
@@ -22,19 +22,20 @@ Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
  org.eclipse.jetty.util.log;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.util.security;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.util.thread;version="[7.6.0,8.0.0)",
- org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.http.server;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.http.server.glue;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.http.server.resolver;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.internal;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.junit;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.junit.http;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.nls;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport.resolver;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)",
+ org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.http.server;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.http.server.glue;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.http.server.resolver;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.junit;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.junit.http;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.nls;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport.resolver;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)"
diff --git a/org.eclipse.jgit.http.test/plugin.properties b/org.eclipse.jgit.http.test/plugin.properties
index dd33aea..0a620aa 100644
--- a/org.eclipse.jgit.http.test/plugin.properties
+++ b/org.eclipse.jgit.http.test/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit HTTP Tests
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 470d570..7f3eee7 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -51,7 +51,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
index 36c3518..0285bd1 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
@@ -62,10 +62,9 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.ReceivePack;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.Transport;
@@ -77,7 +76,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class AdvertiseErrorTest extends HttpTestCase {
-	private FileRepository remoteRepository;
+	private Repository remoteRepository;
 
 	private URIish remoteURI;
 
@@ -85,7 +84,7 @@ public class AdvertiseErrorTest extends HttpTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final String srcName = src.getRepository().getDirectory().getName();
 
 		ServletContextHandler app = server.addContext("/git");
@@ -121,7 +120,7 @@ public class AdvertiseErrorTest extends HttpTestCase {
 		remoteRepository = src.getRepository();
 		remoteURI = toURIish(app, srcName);
 
-		FileBasedConfig cfg = remoteRepository.getConfig();
+		StoredConfig cfg = remoteRepository.getConfig();
 		cfg.setBoolean("http", null, "receivepack", true);
 		cfg.save();
 	}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
index bb9f470..fbf4d07 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
@@ -70,7 +70,6 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.FetchConnection;
 import org.eclipse.jgit.transport.HttpTransport;
 import org.eclipse.jgit.transport.Transport;
@@ -92,7 +91,7 @@ public class DumbClientDumbServerTest extends HttpTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final File srcGit = src.getRepository().getDirectory();
 		final URI base = srcGit.getParentFile().toURI();
 
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
index 4ddfcc5..354b043 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -74,7 +74,6 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.FetchConnection;
 import org.eclipse.jgit.transport.HttpTransport;
 import org.eclipse.jgit.transport.Transport;
@@ -98,7 +97,7 @@ public class DumbClientSmartServerTest extends HttpTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final String srcName = src.getRepository().getDirectory().getName();
 
 		ServletContextHandler app = server.addContext("/git");
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index 2f56e42..f1056f2 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -47,6 +47,8 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -65,10 +67,9 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.PreReceiveHook;
 import org.eclipse.jgit.transport.PushResult;
 import org.eclipse.jgit.transport.ReceiveCommand;
@@ -83,7 +84,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class HookMessageTest extends HttpTestCase {
-	private FileRepository remoteRepository;
+	private Repository remoteRepository;
 
 	private URIish remoteURI;
 
@@ -91,7 +92,7 @@ public class HookMessageTest extends HttpTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final String srcName = src.getRepository().getDirectory().getName();
 
 		ServletContextHandler app = server.addContext("/git");
@@ -132,7 +133,7 @@ public class HookMessageTest extends HttpTestCase {
 		remoteRepository = src.getRepository();
 		remoteURI = toURIish(app, srcName);
 
-		FileBasedConfig cfg = remoteRepository.getConfig();
+		StoredConfig cfg = remoteRepository.getConfig();
 		cfg.setBoolean("http", null, "receivepack", true);
 		cfg.save();
 	}
@@ -180,4 +181,40 @@ public class HookMessageTest extends HttpTestCase {
 				+ "come back next year!\n", //
 				result.getMessages());
 	}
+
+	@Test
+	public void testPush_HookMessagesToOutputStream() throws Exception {
+		final TestRepository src = createTestRepository();
+		final RevBlob Q_txt = src.blob("new text");
+		final RevCommit Q = src.commit().add("Q", Q_txt).create();
+		final Repository db = src.getRepository();
+		final String dstName = Constants.R_HEADS + "new.branch";
+		Transport t;
+		PushResult result;
+
+		t = Transport.open(db, remoteURI);
+		OutputStream out = new ByteArrayOutputStream();
+		try {
+			final String srcExpr = Q.name();
+			final boolean forceUpdate = false;
+			final String localName = null;
+			final ObjectId oldId = null;
+
+			RemoteRefUpdate update = new RemoteRefUpdate(src.getRepository(),
+					srcExpr, dstName, forceUpdate, localName, oldId);
+			result = t.push(NullProgressMonitor.INSTANCE,
+					Collections.singleton(update), out);
+		} finally {
+			t.close();
+		}
+
+		String expectedMessage = "message line 1\n" //
+				+ "error: no soup for you!\n" //
+				+ "come back next year!\n";
+		assertEquals(expectedMessage, //
+				result.getMessages());
+
+		assertEquals(expectedMessage, out.toString());
+	}
+
 }
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index efad6b9..8bdaf2c 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -72,9 +72,8 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.FetchConnection;
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
@@ -85,7 +84,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class HttpClientTests extends HttpTestCase {
-	private TestRepository<FileRepository> remoteRepository;
+	private TestRepository<Repository> remoteRepository;
 
 	private URIish dumbAuthNoneURI;
 
@@ -134,7 +133,7 @@ public class HttpClientTests extends HttpTestCase {
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
-				final FileRepository db = remoteRepository.getRepository();
+				final Repository db = remoteRepository.getRepository();
 				if (!name.equals(nameOf(db)))
 					throw new RepositoryNotFoundException(name);
 
@@ -148,7 +147,7 @@ public class HttpClientTests extends HttpTestCase {
 		return ctx;
 	}
 
-	private static String nameOf(final FileRepository db) {
+	private static String nameOf(final Repository db) {
 		return db.getDirectory().getName();
 	}
 
@@ -217,7 +216,7 @@ public class HttpClientTests extends HttpTestCase {
 
 	@Test
 	public void testListRemote_Dumb_NoHEAD() throws Exception {
-		FileRepository src = remoteRepository.getRepository();
+		Repository src = remoteRepository.getRepository();
 		File headref = new File(src.getDirectory(), Constants.HEAD);
 		assertTrue("HEAD used to be present", headref.delete());
 		assertFalse("HEAD is gone", headref.exists());
@@ -356,8 +355,8 @@ public class HttpClientTests extends HttpTestCase {
 
 	@Test
 	public void testListRemote_Smart_UploadPackDisabled() throws Exception {
-		FileRepository src = remoteRepository.getRepository();
-		final FileBasedConfig cfg = src.getConfig();
+		Repository src = remoteRepository.getRepository();
+		final StoredConfig cfg = src.getConfig();
 		cfg.setBoolean("http", null, "uploadpack", false);
 		cfg.save();
 
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
index b62ed7f..2823b34 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
@@ -66,9 +66,8 @@ import org.eclipse.jgit.junit.http.HttpTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.PacketLineIn;
 import org.eclipse.jgit.transport.PacketLineOut;
 import org.eclipse.jgit.transport.URIish;
@@ -79,7 +78,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class ProtocolErrorTest extends HttpTestCase {
-	private FileRepository remoteRepository;
+	private Repository remoteRepository;
 
 	private URIish remoteURI;
 
@@ -89,7 +88,7 @@ public class ProtocolErrorTest extends HttpTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final String srcName = src.getRepository().getDirectory().getName();
 
 		ServletContextHandler app = server.addContext("/git");
@@ -113,7 +112,7 @@ public class ProtocolErrorTest extends HttpTestCase {
 		remoteRepository = src.getRepository();
 		remoteURI = toURIish(app, srcName);
 
-		FileBasedConfig cfg = remoteRepository.getConfig();
+		StoredConfig cfg = remoteRepository.getConfig();
 		cfg.setBoolean("http", null, "receivepack", true);
 		cfg.save();
 
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 4f0543a..7b3c717 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -86,13 +86,12 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.eclipse.jgit.transport.FetchConnection;
 import org.eclipse.jgit.transport.HttpTransport;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -107,7 +106,7 @@ import org.junit.Test;
 public class SmartClientSmartServerTest extends HttpTestCase {
 	private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
 
-	private FileRepository remoteRepository;
+	private Repository remoteRepository;
 
 	private URIish remoteURI;
 
@@ -121,7 +120,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final String srcName = src.getRepository().getDirectory().getName();
 
 		ServletContextHandler app = server.addContext("/git");
@@ -601,16 +600,16 @@ public class SmartClientSmartServerTest extends HttpTestCase {
 
 	@Test
 	public void testPush_ChunkedEncoding() throws Exception {
-		final TestRepository<FileRepository> src = createTestRepository();
+		final TestRepository<Repository> src = createTestRepository();
 		final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
 		final RevCommit Q = src.commit().add("Q", Q_bin).create();
-		final FileRepository db = src.getRepository();
+		final Repository db = src.getRepository();
 		final String dstName = Constants.R_HEADS + "new.branch";
 		Transport t;
 
 		enableReceivePack();
 
-		final FileBasedConfig cfg = db.getConfig();
+		final StoredConfig cfg = db.getConfig();
 		cfg.setInt("core", null, "compression", 0);
 		cfg.setInt("http", null, "postbuffer", 8 * 1024);
 		cfg.save();
@@ -660,7 +659,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
 	}
 
 	private void enableReceivePack() throws IOException {
-		final FileBasedConfig cfg = remoteRepository.getConfig();
+		final StoredConfig cfg = remoteRepository.getConfig();
 		cfg.setBoolean("http", null, "receivepack", true);
 		cfg.save();
 	}
diff --git a/org.eclipse.jgit.java7.test/.classpath b/org.eclipse.jgit.java7.test/.classpath
new file mode 100644
index 0000000..131f635
--- /dev/null
+++ b/org.eclipse.jgit.java7.test/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.jgit.java7"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.jgit.test"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.test/.project b/org.eclipse.jgit.java7.test/.project
similarity index 95%
copy from org.eclipse.jgit.test/.project
copy to org.eclipse.jgit.java7.test/.project
index a7b1989..fd4cc60 100644
--- a/org.eclipse.jgit.test/.project
+++ b/org.eclipse.jgit.java7.test/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>org.eclipse.jgit.test</name>
+	<name>org.eclipse.jgit.java7.test</name>
 	<comment></comment>
 	<projects>
 	</projects>
@@ -27,8 +27,8 @@
 		</buildCommand>
 	</buildSpec>
 	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
 	</natures>
 </projectDescription>
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.resources.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.core.resources.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.core.resources.prefs
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.runtime.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.core.runtime.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.core.runtime.prefs
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..e1efe00
--- /dev/null
+++ b/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,393 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.ui.prefs
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.team.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.team.ui.prefs
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.api.tools.prefs
similarity index 100%
copy from org.eclipse.jgit.ant/.settings/org.eclipse.pde.api.tools.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.api.tools.prefs
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.core.prefs
similarity index 100%
copy from org.eclipse.jgit.http.server/.settings/org.eclipse.pde.core.prefs
copy to org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.core.prefs
diff --git a/org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c54de5e
--- /dev/null
+++ b/org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.java7.test
+Bundle-Version: 3.0.0.201306101825-r
+Bundle-Vendor: %provider_name
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.junit;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)"
diff --git a/org.eclipse.jgit.java7.test/build.properties b/org.eclipse.jgit.java7.test/build.properties
new file mode 100644
index 0000000..1415b07
--- /dev/null
+++ b/org.eclipse.jgit.java7.test/build.properties
@@ -0,0 +1,6 @@
+source.. = tst/,\
+	   tst-rsrc/,\
+	   exttst/
+bin.includes = META-INF/,\
+	       .,\
+	       plugin.properties
diff --git a/org.eclipse.jgit.java7.test/plugin.properties b/org.eclipse.jgit.java7.test/plugin.properties
new file mode 100644
index 0000000..5f9d756
--- /dev/null
+++ b/org.eclipse.jgit.java7.test/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit Core Java 7 Tests
+provider_name=Eclipse.org
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.java7.test/pom.xml
similarity index 79%
copy from org.eclipse.jgit.ant.test/pom.xml
copy to org.eclipse.jgit.java7.test/pom.xml
index 9b14073..883a1b8 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.java7.test/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright (C) 2011, Chris Aniszczyk <caniszczyk at gmail.com>
+   Copyright (C) 2012-2013, Robin Rosenberg <robin.rosenberg at dewire.com>
    and other copyright owners as documented in the project's IP log.
 
    This program and the accompanying materials are made available
@@ -50,14 +50,14 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
-  <artifactId>org.eclipse.jgit.ant.test</artifactId>
-  <name>JGit - Ant Tasks Tests</name>
+  <artifactId>org.eclipse.jgit.java7.test</artifactId>
+  <name>JGit - Core Java 7 Tests</name>
 
   <description>
-    JUnit tests for the various ant tasks.
+    JUnit tests for Java7 support in the core library.
   </description>
 
   <dependencies>
@@ -68,12 +68,19 @@
     </dependency>
 
     <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+      <scope>test</scope>
+      <version>[1.1.0,2.0.0)</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
       <version>${project.version}</version>
     </dependency>
 
-   <dependency>
+    <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.junit</artifactId>
       <version>${project.version}</version>
@@ -81,7 +88,7 @@
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit.ant</artifactId>
+      <artifactId>org.eclipse.jgit.java7</artifactId>
       <version>${project.version}</version>
     </dependency>
 
@@ -90,12 +97,18 @@
   <build>
     <testSourceDirectory>src/</testSourceDirectory>
 
+    <testResources>
+      <testResource>
+	<directory>tst-rsrc/</directory>
+      </testResource>
+    </testResources>
+
     <plugins>
       <plugin>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <argLine>-Xmx256m -Dfile.encoding=UTF-8</argLine>
-        </configuration>
+	<artifactId>maven-surefire-plugin</artifactId>
+	<configuration>
+	  <argLine>-Xmx256m -Dfile.encoding=UTF-8</argLine>
+	</configuration>
       </plugin>
     </plugins>
   </build>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java b/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FSJava7Test.java
similarity index 50%
copy from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
copy to org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FSJava7Test.java
index 530636f..d735bd3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
+++ b/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FSJava7Test.java
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2008, Imran M Yousuf <imyousuf at smartitengineering.com>
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski at gmail.com>
+ * Copyright (C) 2012-2013, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -42,93 +41,75 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.util;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.junit.JGitTestUtil;
+import java.io.File;
+import java.io.IOException;
+
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-public class PackReverseIndexTest extends RepositoryTestCase {
-
-	private PackIndex idx;
+public class FSJava7Test {
 
-	private PackReverseIndex reverseIdx;
+	private final File trash = new File(new File("target"), "trash");
 
-	/**
-	 * Set up tested class instance, test constructor by the way.
-	 */
 	@Before
 	public void setUp() throws Exception {
-		super.setUp();
-		// index with both small (< 2^31) and big offsets
-		idx = PackIndex.open(JGitTestUtil.getTestResourceFile(
-				"pack-huge.idx"));
-		reverseIdx = new PackReverseIndex(idx);
+		FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY | FileUtils.SKIP_MISSING);
+		assertTrue(trash.mkdirs());
 	}
 
-	/**
-	 * Test findObject() for all index entries.
-	 */
-	@Test
-	public void testFindObject() {
-		for (MutableEntry me : idx)
-			assertEquals(me.toObjectId(), reverseIdx.findObject(me.getOffset()));
+	@After
+	public void tearDown() throws Exception {
+		FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY);
 	}
 
 	/**
-	 * Test findObject() with illegal argument.
-	 */
-	@Test
-	public void testFindObjectWrongOffset() {
-		assertNull(reverseIdx.findObject(0));
-	}
-
-	/**
-	 * Test findNextOffset() for all index entries.
+	 * The old File methods traverse symbolic links and look at the targets.
+	 * With symbolic links we usually want to modify/look at the link. For some
+	 * reason the executable attribute seems to always look at the target, but
+	 * for the other attributes like lastModified, hidden and exists we must
+	 * differ between the link and the target.
 	 *
-	 * @throws CorruptObjectException
+	 * @throws IOException
+	 * @throws InterruptedException
 	 */
 	@Test
-	public void testFindNextOffset() throws CorruptObjectException {
-		long offset = findFirstOffset();
-		assertTrue(offset > 0);
-		for (int i = 0; i < idx.getObjectCount(); i++) {
-			long newOffset = reverseIdx.findNextOffset(offset, Long.MAX_VALUE);
-			assertTrue(newOffset > offset);
-			if (i == idx.getObjectCount() - 1)
-				assertEquals(newOffset, Long.MAX_VALUE);
-			else
-				assertEquals(newOffset, idx.findOffset(reverseIdx
-						.findObject(newOffset)));
-			offset = newOffset;
-		}
-	}
+	public void testSymlinkAttributes() throws IOException, InterruptedException {
+		FS fs = FS.DETECTED;
+		File link = new File(trash, "x");
+		File target = new File(trash, "y");
+		fs.createSymLink(link, "y");
+		assertTrue(fs.exists(link));
+		String targetName = fs.readSymLink(link);
+		assertEquals("y", targetName);
+		assertTrue(fs.lastModified(link) > 0);
+		assertTrue(fs.exists(link));
+		assertFalse(fs.canExecute(link));
+		assertEquals(1, fs.length(link));
+		assertFalse(fs.exists(target));
+		assertFalse(fs.isFile(target));
+		assertFalse(fs.isDirectory(target));
+		assertFalse(fs.canExecute(target));
 
-	/**
-	 * Test findNextOffset() with wrong illegal argument as offset.
-	 */
-	@Test
-	public void testFindNextOffsetWrongOffset() {
-		try {
-			reverseIdx.findNextOffset(0, Long.MAX_VALUE);
-			fail("findNextOffset() should throw exception");
-		} catch (CorruptObjectException x) {
-			// expected
-		}
+		RepositoryTestCase.fsTick(link);
+		// Now create the link target
+		FileUtils.createNewFile(target);
+		assertTrue(fs.exists(link));
+		assertTrue(fs.lastModified(link) > 0);
+		assertTrue(fs.lastModified(target) > fs.lastModified(link));
+		assertFalse(fs.canExecute(link));
+		fs.setExecute(target, true);
+		assertFalse(fs.canExecute(link));
+		assertTrue(fs.canExecute(target));
 	}
 
-	private long findFirstOffset() {
-		long min = Long.MAX_VALUE;
-		for (MutableEntry me : idx)
-			min = Math.min(min, me.getOffset());
-		return min;
-	}
 }
diff --git a/org.eclipse.jgit.java7/.classpath b/org.eclipse.jgit.java7/.classpath
new file mode 100644
index 0000000..098194c
--- /dev/null
+++ b/org.eclipse.jgit.java7/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.http.server/.fbprefs b/org.eclipse.jgit.java7/.fbprefs
similarity index 100%
copy from org.eclipse.jgit.http.server/.fbprefs
copy to org.eclipse.jgit.java7/.fbprefs
diff --git a/org.eclipse.jgit.test/.project b/org.eclipse.jgit.java7/.project
similarity index 95%
copy from org.eclipse.jgit.test/.project
copy to org.eclipse.jgit.java7/.project
index a7b1989..291c8ff 100644
--- a/org.eclipse.jgit.test/.project
+++ b/org.eclipse.jgit.java7/.project
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>org.eclipse.jgit.test</name>
+	<name>org.eclipse.jgit.java7</name>
 	<comment></comment>
 	<projects>
 	</projects>
@@ -27,8 +27,8 @@
 		</buildCommand>
 	</buildSpec>
 	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
 		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
 	</natures>
 </projectDescription>
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.core.resources.prefs
similarity index 100%
copy from org.eclipse.jgit.ant/.settings/org.eclipse.core.resources.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.core.resources.prefs
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.core.runtime.prefs
similarity index 100%
copy from org.eclipse.jgit.ant/.settings/org.eclipse.core.runtime.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.core.runtime.prefs
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c479807
--- /dev/null
+++ b/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,393 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.jdt.ui.prefs
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.tasks.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.tasks.ui.prefs
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.team.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.team.ui.prefs
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.pde.api.tools.prefs
similarity index 100%
copy from org.eclipse.jgit.ant/.settings/org.eclipse.pde.api.tools.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.pde.api.tools.prefs
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.pde.core.prefs
similarity index 100%
copy from org.eclipse.jgit.http.server/.settings/org.eclipse.pde.core.prefs
copy to org.eclipse.jgit.java7/.settings/org.eclipse.pde.core.prefs
diff --git a/org.eclipse.jgit.java7/META-INF/MANIFEST.MF b/org.eclipse.jgit.java7/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..3a789a7
--- /dev/null
+++ b/org.eclipse.jgit.java7/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Fragment-Host: org.eclipse.jgit;bundle-version="3.0.0"
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.java7
+Bundle-Version: 3.0.0.201306101825-r
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Export-Package: org.eclipse.jgit.util;version="3.0.0"
diff --git a/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 0000000..6d1ece1
--- /dev/null
+++ b/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.java7 - Sources
+Bundle-SymbolicName: org.eclipse.jgit.java7.source;singleton:=true
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 3.0.0.201306101825-r
+Eclipse-SourceBundle: org.eclipse.jgit.java7;version="3.0.0";roots="."
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html b/org.eclipse.jgit.java7/about.html
similarity index 88%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
copy to org.eclipse.jgit.java7/about.html
index 01a2671..2cb2663 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
+++ b/org.eclipse.jgit.java7/about.html
@@ -11,13 +11,13 @@
     margin: 0.25in 0.5in 0.25in 0.5in;
     tab-interval: 0.5in;
     }
-  p {  	
+  p {
     margin-left: auto;
     margin-top:  0.5em;
     margin-bottom: 0.5em;
     }
   p.list {
-  	margin-left: 0.5in;
+	margin-left: 0.5in;
     margin-top:  0.05em;
     margin-bottom: 0.05em;
     }
@@ -32,26 +32,26 @@
 <p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
 
 <p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification, 
+<p>Redistribution and use in source and binary forms, with or without modification,
 	are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice, 
+<ul><li>Redistributions of source code must retain the above copyright notice,
 	this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice, 
-	this list of conditions and the following disclaimer in the documentation 
+<li>Redistributions in binary form must reproduce the above copyright notice,
+	this list of conditions and the following disclaimer in the documentation
 	and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its 
-	contributors may be used to endorse or promote products derived from 
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+	contributors may be used to endorse or promote products derived from
 	this software without specific prior written permission. </li></ul>
 </p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.</p>
 
 </body>
diff --git a/org.eclipse.jgit.junit.http/build.properties b/org.eclipse.jgit.java7/build.properties
similarity index 79%
copy from org.eclipse.jgit.junit.http/build.properties
copy to org.eclipse.jgit.java7/build.properties
index f4ae970..185d310 100644
--- a/org.eclipse.jgit.junit.http/build.properties
+++ b/org.eclipse.jgit.java7/build.properties
@@ -1,5 +1,7 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               plugin.properties
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               about.html,\
+               plugin.properties
+
diff --git a/org.eclipse.jgit.java7/plugin.properties b/org.eclipse.jgit.java7/plugin.properties
new file mode 100644
index 0000000..fc2c16a
--- /dev/null
+++ b/org.eclipse.jgit.java7/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit Java 7 Support
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit.java7/pom.xml
similarity index 81%
copy from org.eclipse.jgit/pom.xml
copy to org.eclipse.jgit.java7/pom.xml
index ecc09be..58e1760 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit.java7/pom.xml
@@ -1,9 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright (C) 2009, Google Inc.
-   Copyright (C) 2009, Igor Fedorenko <igor at ifedorenko.com>
-   Copyright (C) 2008, Imran M Yousuf <imyousuf at smartitengineering.com>
-   Copyright (C) 2010, Matthias Sohn <matthias.sohn at sap.com>
+   Copyright (C) 2012-2013, Robin Rosenberg
    and other copyright owners as documented in the project's IP log.
 
    This program and the accompanying materials are made available
@@ -53,28 +50,29 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
-  <artifactId>org.eclipse.jgit</artifactId>
-  <name>JGit - Core</name>
+  <artifactId>org.eclipse.jgit.java7</artifactId>
+  <name>JGit - Core Java7 Support</name>
 
   <description>
-    Repository access and algorithms
+    Java7 support for symbolic links etc
   </description>
 
-  <properties>
-    <translate-qualifier/>
-    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
-  </properties>
-
   <dependencies>
     <dependency>
-      <groupId>com.jcraft</groupId>
-      <artifactId>jsch</artifactId>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
     </dependency>
   </dependencies>
 
+  <properties>
+    <translate-qualifier/>
+    <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
+  </properties>
+
   <build>
     <sourceDirectory>src/</sourceDirectory>
 
@@ -84,16 +82,23 @@
         <includes>
           <include>plugin.properties</include>
           <include>about.html</include>
-		  <include>META-INF/eclipse.inf</include>
         </includes>
       </resource>
       <resource>
-        <directory>resources/</directory>
+	    <directory>resources/</directory>
       </resource>
     </resources>
 
     <plugins>
       <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>7</source>
+          <target>7</target>
+          <encoding>UTF-8</encoding>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-antrun-plugin</artifactId>
         <executions>
@@ -153,6 +158,26 @@
     <pluginManagement>
       <plugins>
         <plugin>
+	      <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-pmd-plugin</artifactId>
+          <version>2.7.1</version>
+          <configuration>
+            <sourceEncoding>utf-8</sourceEncoding>
+            <minimumTokens>100</minimumTokens>
+            <targetJdk>1.5</targetJdk>
+            <format>xml</format>
+            <failOnViolation>false</failOnViolation>
+          </configuration>
+          <executions>
+            <execution>
+              <goals>
+                <goal>cpd-check</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
+
+        <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>findbugs-maven-plugin</artifactId>
           <configuration>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java
similarity index 55%
copy from org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
copy to org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java
index 0502547..b19a432 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
+++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java
@@ -1,7 +1,5 @@
 /*
- * Copyright (C) 2007, Robin Rosenberg <me at lathund.dewire.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -46,80 +44,105 @@
 package org.eclipse.jgit.util;
 
 import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.io.IOException;
 
-class FS_POSIX_Java6 extends FS_POSIX {
-	private static final Method canExecute;
+import org.eclipse.jgit.util.FS;
 
-	private static final Method setExecute;
+/**
+ * FS implementation for Java7 on unix like systems
+ */
+public class FS_POSIX_Java7 extends FS_POSIX {
 
-	static {
-		canExecute = needMethod(File.class, "canExecute"); //$NON-NLS-1$
-		setExecute = needMethod(File.class, "setExecutable", Boolean.TYPE); //$NON-NLS-1$
+	FS_POSIX_Java7(FS_POSIX_Java7 src) {
+		super(src);
 	}
 
-	static boolean hasExecute() {
-		return canExecute != null && setExecute != null;
+	FS_POSIX_Java7() {
+		// empty
 	}
 
-	private static Method needMethod(final Class<?> on, final String name,
-			final Class<?>... args) {
-		try {
-			return on.getMethod(name, args);
-		} catch (SecurityException e) {
-			return null;
-		} catch (NoSuchMethodException e) {
-			return null;
-		}
+	@Override
+	public FS newInstance() {
+		return new FS_POSIX_Java7(this);
 	}
 
-	FS_POSIX_Java6() {
-		super();
+	@Override
+	public boolean supportsExecute() {
+		return true;
 	}
 
-	FS_POSIX_Java6(FS src) {
-		super(src);
+	@Override
+	public boolean canExecute(File f) {
+		return FileUtil.canExecute(f);
 	}
 
 	@Override
-	public FS newInstance() {
-		return new FS_POSIX_Java6(this);
+	public boolean setExecute(File f, boolean canExecute) {
+		return FileUtil.setExecute(f, canExecute);
 	}
 
-	public boolean supportsExecute() {
+	@Override
+	public boolean retryFailedLockFileCommit() {
+		return false;
+	}
+
+	@Override
+	public boolean supportsSymlinks() {
 		return true;
 	}
 
-	public boolean canExecute(final File f) {
-		try {
-			final Object r = canExecute.invoke(f, (Object[]) null);
-			return ((Boolean) r).booleanValue();
-		} catch (IllegalArgumentException e) {
-			throw new Error(e);
-		} catch (IllegalAccessException e) {
-			throw new Error(e);
-		} catch (InvocationTargetException e) {
-			throw new Error(e);
-		}
+	@Override
+	public boolean isSymLink(File path) throws IOException {
+		return FileUtil.isSymlink(path);
 	}
 
-	public boolean setExecute(final File f, final boolean canExec) {
-		try {
-			final Object r;
-			r = setExecute.invoke(f, new Object[] { Boolean.valueOf(canExec) });
-			return ((Boolean) r).booleanValue();
-		} catch (IllegalArgumentException e) {
-			throw new Error(e);
-		} catch (IllegalAccessException e) {
-			throw new Error(e);
-		} catch (InvocationTargetException e) {
-			throw new Error(e);
-		}
+	@Override
+	public long lastModified(File path) throws IOException {
+		return FileUtil.lastModified(path);
 	}
 
 	@Override
-	public boolean retryFailedLockFileCommit() {
-		return false;
+	public void setLastModified(File path, long time) throws IOException {
+		FileUtil.setLastModified(path, time);
+	}
+
+	@Override
+	public long length(File f) throws IOException {
+		return FileUtil.getLength(f);
+	}
+
+	@Override
+	public boolean exists(File path) {
+		return FileUtil.exists(path);
+	}
+
+	@Override
+	public boolean isDirectory(File path) {
+		return FileUtil.isDirectory(path);
+	}
+
+	@Override
+	public boolean isFile(File path) {
+		return FileUtil.isFile(path);
+	}
+
+	@Override
+	public boolean isHidden(File path) throws IOException {
+		return FileUtil.isHidden(path);
+	}
+
+	@Override
+	public void setHidden(File path, boolean hidden) throws IOException {
+		// no action on POSIX
+	}
+
+	@Override
+	public String readSymLink(File path) throws IOException {
+		return FileUtil.readSymlink(path);
+	}
+
+	@Override
+	public void createSymLink(File path, String target) throws IOException {
+		FileUtil.createSymLink(path, target);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java
similarity index 58%
copy from org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
copy to org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java
index c79bbe6..b015362 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -44,35 +44,82 @@
 package org.eclipse.jgit.util;
 
 import java.io.File;
+import java.io.IOException;
 
-class FS_POSIX_Java5 extends FS_POSIX {
-	FS_POSIX_Java5() {
-		super();
-	}
+/**
+ * FS for Java7 on Windows
+ */
+public class FS_Win32_Java7 extends FS_Win32 {
 
-	FS_POSIX_Java5(FS src) {
+	FS_Win32_Java7(FS src) {
 		super(src);
 	}
 
+	FS_Win32_Java7() {
+	}
+
 	@Override
 	public FS newInstance() {
-		return new FS_POSIX_Java5(this);
+		return new FS_Win32_Java7(this);
+	}
+
+	@Override
+	public boolean supportsSymlinks() {
+		return true;
+	}
+
+	@Override
+	public boolean isSymLink(File path) throws IOException {
+		return FileUtil.isSymlink(path);
+	}
+
+	@Override
+	public long lastModified(File path) throws IOException {
+		return FileUtil.lastModified(path);
 	}
 
-	public boolean supportsExecute() {
-		return false;
+	@Override
+	public void setLastModified(File path, long time) throws IOException {
+		FileUtil.setLastModified(path, time);
 	}
 
-	public boolean canExecute(final File f) {
-		return false;
+	@Override
+	public long length(File f) throws IOException {
+		return FileUtil.getLength(f);
 	}
 
-	public boolean setExecute(final File f, final boolean canExec) {
-		return false;
+	@Override
+	public boolean exists(File path) {
+		return FileUtil.exists(path);
+	}
+
+	@Override
+	public boolean isDirectory(File path) {
+		return FileUtil.isDirectory(path);
+	}
+
+	@Override
+	public boolean isFile(File path) {
+		return FileUtil.isFile(path);
+	}
+
+	@Override
+	public boolean isHidden(File path) throws IOException {
+		return FileUtil.isHidden(path);
+	}
+
+	@Override
+	public void setHidden(File path, boolean hidden) throws IOException {
+		FileUtil.setHidden(path, hidden);
+	}
+
+	@Override
+	public String readSymLink(File path) throws IOException {
+		return FileUtil.readSymlink(path);
 	}
 
 	@Override
-	public boolean retryFailedLockFileCommit() {
-		return false;
+	public void createSymLink(File path, String target) throws IOException {
+		FileUtil.createSymLink(path, target);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java
similarity index 57%
copy from org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
copy to org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java
index c79bbe6..1130668 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -44,35 +44,82 @@
 package org.eclipse.jgit.util;
 
 import java.io.File;
+import java.io.IOException;
 
-class FS_POSIX_Java5 extends FS_POSIX {
-	FS_POSIX_Java5() {
-		super();
-	}
+/**
+ * FS for Java7 on Windows with Cygwin
+ */
+public class FS_Win32_Java7Cygwin extends FS_Win32_Cygwin {
 
-	FS_POSIX_Java5(FS src) {
+	FS_Win32_Java7Cygwin(FS src) {
 		super(src);
 	}
 
+	FS_Win32_Java7Cygwin() {
+	}
+
 	@Override
 	public FS newInstance() {
-		return new FS_POSIX_Java5(this);
+		return new FS_Win32_Java7Cygwin(this);
+	}
+
+	@Override
+	public boolean supportsSymlinks() {
+		return true;
+	}
+
+	@Override
+	public boolean isSymLink(File path) throws IOException {
+		return FileUtil.isSymlink(path);
+	}
+
+	@Override
+	public long lastModified(File path) throws IOException {
+		return FileUtil.lastModified(path);
 	}
 
-	public boolean supportsExecute() {
-		return false;
+	@Override
+	public void setLastModified(File path, long time) throws IOException {
+		FileUtil.setLastModified(path, time);
 	}
 
-	public boolean canExecute(final File f) {
-		return false;
+	@Override
+	public long length(File f) throws IOException {
+		return FileUtil.getLength(f);
 	}
 
-	public boolean setExecute(final File f, final boolean canExec) {
-		return false;
+	@Override
+	public boolean exists(File path) {
+		return FileUtil.exists(path);
+	}
+
+	@Override
+	public boolean isDirectory(File path) {
+		return FileUtil.isDirectory(path);
+	}
+
+	@Override
+	public boolean isFile(File path) {
+		return FileUtil.isFile(path);
+	}
+
+	@Override
+	public boolean isHidden(File path) throws IOException {
+		return FileUtil.isHidden(path);
+	}
+
+	@Override
+	public void setHidden(File path, boolean hidden) throws IOException {
+		FileUtil.setHidden(path, hidden);
+	}
+
+	@Override
+	public String readSymLink(File path) throws IOException {
+		return FileUtil.readSymlink(path);
 	}
 
 	@Override
-	public boolean retryFailedLockFileCommit() {
-		return false;
+	public void createSymLink(File path, String target) throws IOException {
+		FileUtil.createSymLink(path, target);
 	}
 }
diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java
new file mode 100644
index 0000000..3cf1c12
--- /dev/null
+++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.text.Normalizer;
+import java.text.Normalizer.Form;
+
+import org.eclipse.jgit.util.SystemReader;
+
+class FileUtil {
+
+	static String readSymlink(File path) throws IOException {
+		Path nioPath = path.toPath();
+		Path target = Files.readSymbolicLink(nioPath);
+		String targetString = target.toString();
+		if (SystemReader.getInstance().isWindows())
+			targetString = targetString.replace('\\', '/');
+		else if (SystemReader.getInstance().isMacOS())
+			targetString = Normalizer.normalize(targetString, Form.NFC);
+		return targetString;
+	}
+
+	public static void createSymLink(File path, String target)
+			throws IOException {
+		Path nioPath = path.toPath();
+		if (Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS))
+			Files.delete(nioPath);
+		if (SystemReader.getInstance().isWindows())
+			target = target.replace('/', '\\');
+		Path nioTarget = new File(target).toPath();
+		Files.createSymbolicLink(nioPath, nioTarget);
+	}
+
+	public static boolean isSymlink(File path) {
+		Path nioPath = path.toPath();
+		return Files.isSymbolicLink(nioPath);
+	}
+
+	public static long lastModified(File path) throws IOException {
+		Path nioPath = path.toPath();
+		return Files.getLastModifiedTime(nioPath, LinkOption.NOFOLLOW_LINKS)
+				.toMillis();
+	}
+
+	public static void setLastModified(File path, long time) throws IOException {
+		Path nioPath = path.toPath();
+		Files.setLastModifiedTime(nioPath, FileTime.fromMillis(time));
+	}
+
+	public static boolean exists(File path) {
+		Path nioPath = path.toPath();
+		return Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS);
+	}
+
+	public static boolean isHidden(File path) throws IOException {
+		Path nioPath = path.toPath();
+		return Files.isHidden(nioPath);
+	}
+
+	public static void setHidden(File path, boolean hidden) throws IOException {
+		Path nioPath = path.toPath();
+		Files.setAttribute(nioPath, "dos:hidden", Boolean.valueOf(hidden), //$NON-NLS-1$
+				LinkOption.NOFOLLOW_LINKS);
+	}
+
+	public static long getLength(File path) throws IOException {
+		Path nioPath = path.toPath();
+		if (Files.isSymbolicLink(nioPath))
+			return Files.readSymbolicLink(nioPath).toString().length();
+		return Files.size(nioPath);
+	}
+
+	public static boolean isDirectory(File path) {
+		Path nioPath = path.toPath();
+		return Files.isDirectory(nioPath, LinkOption.NOFOLLOW_LINKS);
+	}
+
+	public static boolean isFile(File path) {
+		Path nioPath = path.toPath();
+		return Files.isRegularFile(nioPath, LinkOption.NOFOLLOW_LINKS);
+	}
+
+	public static boolean canExecute(File path) {
+		if (!isFile(path))
+			return false;
+		return path.canExecute();
+	}
+
+	public static boolean setExecute(File path, boolean executable) {
+		if (!isFile(path))
+			return false;
+		return path.setExecutable(executable);
+	}
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/Java7FSFactory.java
similarity index 75%
copy from org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
copy to org.eclipse.jgit.java7/src/org/eclipse/jgit/util/Java7FSFactory.java
index c79bbe6..2e8c0ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/Java7FSFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,36 +43,24 @@
 
 package org.eclipse.jgit.util;
 
-import java.io.File;
-
-class FS_POSIX_Java5 extends FS_POSIX {
-	FS_POSIX_Java5() {
-		super();
-	}
-
-	FS_POSIX_Java5(FS src) {
-		super(src);
-	}
-
-	@Override
-	public FS newInstance() {
-		return new FS_POSIX_Java5(this);
-	}
-
-	public boolean supportsExecute() {
-		return false;
-	}
-
-	public boolean canExecute(final File f) {
-		return false;
-	}
-
-	public boolean setExecute(final File f, final boolean canExec) {
-		return false;
-	}
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.FSFactory;
+import org.eclipse.jgit.util.SystemReader;
 
+/**
+ * A factory for creating FS instances on Java7
+ */
+public class Java7FSFactory extends FSFactory {
 	@Override
-	public boolean retryFailedLockFileCommit() {
-		return false;
+	public FS detect(Boolean cygwinUsed) {
+		if (SystemReader.getInstance().isWindows()) {
+			if (cygwinUsed == null)
+				cygwinUsed = Boolean.valueOf(FS_Win32_Cygwin.isCygwin());
+			if (cygwinUsed.booleanValue())
+				return new FS_Win32_Java7Cygwin();
+			else
+				return new FS_Win32_Java7();
+		} else
+			return new FS_POSIX_Java7();
 	}
 }
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index e749dea..674d066 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -2,16 +2,14 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
  javax.servlet.http;version="[2.5.0,3.0.0)",
- org.eclipse.jetty.continuation;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.http;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.io;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.security;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.security.authentication;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.server;version="[7.6.0,8.0.0)",
@@ -21,15 +19,13 @@ Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
  org.eclipse.jetty.util.component;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.util.log;version="[7.6.0,8.0.0)",
  org.eclipse.jetty.util.security;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.thread;version="[7.6.0,8.0.0)",
- org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.http.server;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.http.server.resolver;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.junit;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport.resolver;version="[2.3.1,2.4.0)",
+ org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.http.server;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.junit;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport.resolver;version="[3.0.0,3.1.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="2.3.1"
+Export-Package: org.eclipse.jgit.junit.http;version="3.0.0"
diff --git a/org.eclipse.jgit.junit.http/build.properties b/org.eclipse.jgit.junit.http/build.properties
index f4ae970..aa1a008 100644
--- a/org.eclipse.jgit.junit.http/build.properties
+++ b/org.eclipse.jgit.junit.http/build.properties
@@ -1,5 +1,5 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               plugin.properties
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties
diff --git a/org.eclipse.jgit.junit.http/plugin.properties b/org.eclipse.jgit.junit.http/plugin.properties
index cb837d8..7a96b09 100644
--- a/org.eclipse.jgit.junit.http/plugin.properties
+++ b/org.eclipse.jgit.junit.http/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit JUnit Http Utility Classes
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 145d8f8..16803da 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -1,121 +1,121 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   Copyright (C) 2010, Jens Baumgart <jens.baumgart at sap.com>
-   and other copyright owners as documented in the project's IP log.
-
-   This program and the accompanying materials are made available
-   under the terms of the Eclipse Distribution License v1.0 which
-   accompanies this distribution, is reproduced below, and is
-   available at http://www.eclipse.org/org/documents/edl-v10.php
-
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or
-   without modification, are permitted provided that the following
-   conditions are met:
-
-   - Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-   - Redistributions in binary form must reproduce the above
-     copyright notice, this list of conditions and the following
-     disclaimer in the documentation and/or other materials provided
-     with the distribution.
-
-   - Neither the name of the Eclipse Foundation, Inc. nor the
-     names of its contributors may be used to endorse or promote
-     products derived from this software without specific prior
-     written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.eclipse.jgit</groupId>
-    <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
-  </parent>
-
-  <artifactId>org.eclipse.jgit.junit.http</artifactId>
-  <name>JGit - JUnit Http Utility Classes</name>
-
-  <description>
-    Utility classes to support Http based JUnit testing of JGit applications.
-  </description>
-
-  <properties>
-    <translate-qualifier/>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>provided</scope>
-    </dependency>
-
-    <dependency>
-      <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit.http.server</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.eclipse.jgit</groupId>
-      <artifactId>org.eclipse.jgit.junit</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-servlet</artifactId>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <sourceDirectory>src/</sourceDirectory>
-
-    <resources>
-      <resource>
-        <directory>.</directory>
-        <includes>
-          <include>plugin.properties</include>
-        </includes>
-      </resource>
-    </resources>
-
-    <plugins>
-      <plugin>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${bundle-manifest}</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2010, Jens Baumgart <jens.baumgart at sap.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>3.0.0.201306101825-r</version>
+  </parent>
+
+  <artifactId>org.eclipse.jgit.junit.http</artifactId>
+  <name>JGit - JUnit Http Utility Classes</name>
+
+  <description>
+    Utility classes to support Http based JUnit testing of JGit applications.
+  </description>
+
+  <properties>
+    <translate-qualifier/>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.http.server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <sourceDirectory>src/</sourceDirectory>
+
+    <resources>
+      <resource>
+        <directory>.</directory>
+        <includes>
+          <include>plugin.properties</include>
+        </includes>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${bundle-manifest}</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java
index 56d05c6..84bb888 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java
@@ -63,7 +63,6 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.URIish;
@@ -85,9 +84,9 @@ public abstract class HttpTestCase extends LocalDiskRepositoryTestCase {
 		super.tearDown();
 	}
 
-	protected TestRepository<FileRepository> createTestRepository()
+	protected TestRepository<Repository> createTestRepository()
 			throws IOException {
-		return new TestRepository<FileRepository>(createBareRepository());
+		return new TestRepository<Repository>(createBareRepository());
 	}
 
 	protected URIish toURIish(String path) throws URISyntaxException {
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
index 1221e1c..e550e6c 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
@@ -53,7 +53,6 @@ import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.http.server.GitServlet;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.URIish;
 import org.eclipse.jgit.transport.resolver.RepositoryResolver;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
@@ -66,11 +65,11 @@ public class SimpleHttpServer {
 
 	AppServer server;
 
-	private final FileRepository db;
+	private final Repository db;
 
 	private URIish uri;
 
-	public SimpleHttpServer(FileRepository repository) {
+	public SimpleHttpServer(Repository repository) {
 		this.db = repository;
 		server = new AppServer();
 	}
@@ -109,7 +108,7 @@ public class SimpleHttpServer {
 		return ctx;
 	}
 
-	private static String nameOf(final FileRepository db) {
+	private static String nameOf(final Repository db) {
 		return db.getDirectory().getName();
 	}
 
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 3912ebd..91402c5 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -2,30 +2,23 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.eclipse.jgit.api;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.api.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.diff;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.dircache;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.fnmatch;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.merge;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.patch;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revplot;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk.filter;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.pack;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.treewalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util.io;version="[2.3.1,2.4.0)",
+Import-Package: org.eclipse.jgit.api;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.api.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.dircache;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.treewalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.treewalk.filter;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util.io;version="[3.0.0,3.1.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="2.3.1"
-Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
+Export-Package: org.eclipse.jgit.junit;version="3.0.0"
diff --git a/org.eclipse.jgit.junit/plugin.properties b/org.eclipse.jgit.junit/plugin.properties
index 836a27f..9842967 100644
--- a/org.eclipse.jgit.junit/plugin.properties
+++ b/org.eclipse.jgit.junit/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit JUnit Utility Classes
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index f27bb88..04aa7d8 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
index 4bf597a..c6fe4e4 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
@@ -54,7 +54,7 @@ import java.lang.reflect.Method;
 import java.net.URISyntaxException;
 import java.net.URL;
 
-import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.RawParseUtils;
@@ -135,14 +135,14 @@ public abstract class JGitTestUtil {
 		return JGitTestUtil.class.getClassLoader();
 	}
 
-	public static File writeTrashFile(final FileRepository db,
+	public static File writeTrashFile(final Repository db,
 			final String name, final String data) throws IOException {
 		File path = new File(db.getWorkTree(), name);
 		write(path, data);
 		return path;
 	}
 
-	public static File writeTrashFile(final FileRepository db,
+	public static File writeTrashFile(final Repository db,
 			final String subdir,
 			final String name, final String data) throws IOException {
 		File path = new File(db.getWorkTree() + "/" + subdir, name);
@@ -190,13 +190,13 @@ public abstract class JGitTestUtil {
 		return new String(body, 0, body.length, "UTF-8");
 	}
 
-	public static String read(final FileRepository db, final String name)
+	public static String read(final Repository db, final String name)
 			throws IOException {
 		File file = new File(db.getWorkTree(), name);
 		return read(file);
 	}
 
-	public static void deleteTrashFile(final FileRepository db,
+	public static void deleteTrashFile(final Repository db,
 			final String name) throws IOException {
 		File path = new File(db.getWorkTree(), name);
 		FileUtils.delete(path);
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 1614c37..565f934 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
@@ -58,13 +58,12 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
-import org.eclipse.jgit.storage.file.WindowCache;
 import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
@@ -150,7 +149,7 @@ public abstract class LocalDiskRepositoryTestCase {
 		c.setPackedGitWindowSize(8 * WindowCacheConfig.KB);
 		c.setPackedGitMMAP(useMMAP);
 		c.setDeltaBaseCacheLimit(8 * WindowCacheConfig.KB);
-		WindowCache.reconfigure(c);
+		c.install();
 	}
 
 
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index 067f128..397fb61 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -64,6 +64,7 @@ import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
 import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
@@ -72,8 +73,8 @@ import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Before;
 
@@ -375,18 +376,19 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
 	public static long fsTick(File lastFile) throws InterruptedException,
 			IOException {
 		long sleepTime = 1;
-		if (lastFile != null && !lastFile.exists())
+		FS fs = FS.DETECTED;
+		if (lastFile != null && !fs.exists(lastFile))
 			throw new FileNotFoundException(lastFile.getPath());
 		File tmp = File.createTempFile("FileTreeIteratorWithTimeControl", null);
 		try {
-			long startTime = (lastFile == null) ? tmp.lastModified() : lastFile
-					.lastModified();
-			long actTime = tmp.lastModified();
+			long startTime = (lastFile == null) ? fs.lastModified(tmp) : fs
+					.lastModified(lastFile);
+			long actTime = fs.lastModified(tmp);
 			while (actTime <= startTime) {
 				Thread.sleep(sleepTime);
 				sleepTime *= 5;
-				tmp.setLastModified(System.currentTimeMillis());
-				actTime = tmp.lastModified();
+				fs.setLastModified(tmp, System.currentTimeMillis());
+				actTime = fs.lastModified(tmp);
 			}
 			return actTime;
 		} finally {
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index 2f5bcda..b30fa59 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -68,6 +68,12 @@ import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
@@ -88,12 +94,6 @@ import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepository;
-import org.eclipse.jgit.storage.file.LockFile;
-import org.eclipse.jgit.storage.file.ObjectDirectory;
-import org.eclipse.jgit.storage.file.PackFile;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.eclipse.jgit.util.FileUtils;
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs
index 6f96ce8..14bdc2c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs
@@ -1,3 +1,3 @@
-#Fri Jun 18 23:33:45 CEST 2010
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs
index 1a32dba..898252b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs
@@ -1,3 +1,3 @@
-#Fri Jun 18 23:33:45 CEST 2010
-eclipse.preferences.version=1
-line.separator=\n
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
index 01a2671..1826b47 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
@@ -11,13 +11,13 @@
     margin: 0.25in 0.5in 0.25in 0.5in;
     tab-interval: 0.5in;
     }
-  p {  	
+  p {
     margin-left: auto;
     margin-top:  0.5em;
     margin-bottom: 0.5em;
     }
   p.list {
-  	margin-left: 0.5in;
+    margin-left: 0.5in;
     margin-top:  0.05em;
     margin-bottom: 0.05em;
     }
@@ -32,26 +32,26 @@
 <p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
 
 <p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification, 
-	are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice, 
-	this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice, 
-	this list of conditions and the following disclaimer in the documentation 
-	and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its 
-	contributors may be used to endorse or promote products derived from 
-	this software without specific prior written permission. </li></ul>
+<p>Redistribution and use in source and binary forms, with or without modification,
+    are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.</li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.</li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.</li></ul>
 </p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.</p>
 
 </body>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
index 442a76e..44112e8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
@@ -8,7 +8,7 @@
 #
 ###############################################################################
 
-featureName=Eclipse JGit
+featureName=Java implementation of Git
 providerName=Eclipse JGit
 
 updateSiteName=Eclipse JGit Update Site
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index f7702c2..021e25d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit"
       label="%featureName"
-      version="2.3.1.201302201838-r"
+      version="3.0.0.201306101825-r"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -35,4 +35,12 @@
          install-size="0"
          version="0.0.0"
          unpack="false"/>
+
+   <plugin
+         id="javaewah"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index 53bf31b..33e295f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
@@ -63,7 +63,12 @@
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>2.3.1.201302201838-r</version>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.java7</artifactId>
+      <version>${project.version}</version>
     </dependency>
   </dependencies>
 
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.project
new file mode 100644
index 0000000..318860e
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.java7.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.resources.prefs
similarity index 96%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.resources.prefs
index 6f96ce8..14bdc2c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.resources.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.resources.prefs
@@ -1,3 +1,3 @@
-#Fri Jun 18 23:33:45 CEST 2010
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.runtime.prefs
similarity index 96%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.runtime.prefs
index 1a32dba..898252b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.core.runtime.prefs
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.runtime.prefs
@@ -1,3 +1,3 @@
-#Fri Jun 18 23:33:45 CEST 2010
-eclipse.preferences.version=1
-line.separator=\n
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.team.ui.prefs
similarity index 100%
copy from org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.team.ui.prefs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/build.properties
similarity index 100%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/build.properties
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/build.properties
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/edl-v10.html
similarity index 72%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/edl-v10.html
index 01a2671..1826b47 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/edl-v10.html
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/edl-v10.html
@@ -11,13 +11,13 @@
     margin: 0.25in 0.5in 0.25in 0.5in;
     tab-interval: 0.5in;
     }
-  p {  	
+  p {
     margin-left: auto;
     margin-top:  0.5em;
     margin-bottom: 0.5em;
     }
   p.list {
-  	margin-left: 0.5in;
+    margin-left: 0.5in;
     margin-top:  0.05em;
     margin-bottom: 0.05em;
     }
@@ -32,26 +32,26 @@
 <p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
 
 <p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification, 
-	are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice, 
-	this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice, 
-	this list of conditions and the following disclaimer in the documentation 
-	and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its 
-	contributors may be used to endorse or promote products derived from 
-	this software without specific prior written permission. </li></ul>
+<p>Redistribution and use in source and binary forms, with or without modification,
+    are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.</li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.</li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.</li></ul>
 </p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.</p>
 
 </body>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties
similarity index 98%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties
index 442a76e..5d5c785 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties
@@ -8,19 +8,19 @@
 #
 ###############################################################################
 
-featureName=Eclipse JGit
+featureName=Java implementation of Git - optional Java 7 libraries
 providerName=Eclipse JGit
 
 updateSiteName=Eclipse JGit Update Site
 
 # description property - text of the "Feature Description"
 description=\
-A pure Java implementation of the Git version control system.\n
+Optional Java 7 libraries for JGit.\n
 ################ end of description property ##################################
 
 # "copyright" property - text of the "Feature Update Copyright"
 copyright=\
-Copyright (c) 2005, 2010 Shawn Pearce, Robin Rosenberg, et.al.\n\
+Copyright (c) 2005, 2013 Shawn Pearce, Robin Rosenberg, et.al.\n\
 All rights reserved. This program and the accompanying materials\n\
 are made available under the terms of the Eclipse Distribution License v1.0\n\
 which accompanies this distribution, and is available at\n\
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.xml
similarity index 75%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.xml
index a92b36c..74cff0f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.xml
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <feature
-      id="org.eclipse.jgit.pgm.source"
+      id="org.eclipse.jgit.java7"
       label="%featureName"
-      version="2.3.1.201302201838-r"
+      version="3.0.0.201306101825-r"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
-     %description
+      %description
    </description>
 
    <copyright>
-     %copyright
+      %copyright
    </copyright>
 
    <license url="%licenseURL">
-     %license
+      %license
    </license>
 
    <url>
@@ -23,9 +23,11 @@
    </url>
 
    <plugin
-         id="org.eclipse.jgit.pgm.source"
+         id="org.eclipse.jgit.java7"
          download-size="0"
          install-size="0"
          version="0.0.0"
+         fragment="true"
          unpack="false"/>
+
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/license.html
similarity index 100%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/license.html
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/pom.xml
similarity index 87%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/pom.xml
index 53bf31b..861d52a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/pom.xml
@@ -50,21 +50,28 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
-  <artifactId>org.eclipse.jgit</artifactId>
+  <artifactId>org.eclipse.jgit.java7</artifactId>
   <packaging>eclipse-feature</packaging>
 
-  <name>JGit Feature</name>
-
+  <name>JGit Optional Java 7 Feature</name>
   <dependencies>
+
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit</artifactId>
-      <version>2.3.1.201302201838-r</version>
+      <version>${project.version}</version>
     </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.java7</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
   </dependencies>
 
 </project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties
index 324c653..7853e55 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties
@@ -9,7 +9,7 @@
 #
 ###############################################################################
 
-featureName=Eclipse JGit JUnit Test Support
+featureName=JUnit test support for Java implementation of Git
 providerName=Eclipse JGit
 
 updateSiteName=Eclipse JGit Update Site
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 72c10e8..9fdc7b8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -1,48 +1,50 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<feature
-      id="org.eclipse.jgit.junit"
-      label="%featureName"
-      version="2.3.1.201302201838-r"
-      provider-name="%providerName">
-
-   <description url="http://www.eclipse.org/jgit/">
-      %description
-   </description>
-
-   <copyright>
-      %copyright
-   </copyright>
-
-   <license url="%licenseURL">
-      %license
-   </license>
-
-   <url>
-      <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
-      <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
-   </url>
-
-   <requires>
-      <import plugin="com.jcraft.jsch"/>
-   </requires>
-
-   <plugin
-         id="org.eclipse.jgit.junit"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
-   <plugin
-         id="org.eclipse.jgit.junit.http"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"/>
-
-   <plugin
-         id="org.eclipse.jgit.http.server"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"/>
-
-</feature>
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eclipse.jgit.junit"
+      label="%featureName"
+      version="3.0.0.201306101825-r"
+      provider-name="%providerName">
+
+   <description url="http://www.eclipse.org/jgit/">
+      %description
+   </description>
+
+   <copyright>
+      %copyright
+   </copyright>
+
+   <license url="%licenseURL">
+      %license
+   </license>
+
+   <url>
+      <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+      <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+   </url>
+
+   <requires>
+      <import plugin="com.jcraft.jsch"/>
+   </requires>
+
+   <plugin
+         id="org.eclipse.jgit.junit"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.junit.http"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.http.server"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index b992ae2..e90b580 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties
index 6921234..7b479f3 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties
@@ -8,7 +8,7 @@
 #
 ###############################################################################
 
-featureName=Eclipse JGit Command Line Interface
+featureName=Command Line Interface for Java implementation of Git
 providerName=Eclipse JGit
 
 updateSiteName=Eclipse JGit Update Site
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index cf6d6c2..5596bd8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.pgm"
       label="%featureName"
-      version="2.3.1.201302201838-r"
+      version="3.0.0.201306101825-r"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
    </url>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="2.3.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="3.0.0" match="equivalent"/>
    </requires>
 
    <plugin
@@ -53,4 +53,12 @@
          install-size="0"
          version="0.0.0"
          unpack="false"/>
+
+   <plugin
+         id="javaewah"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index e48a9d1..65f4385 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
@@ -69,6 +69,12 @@
 
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.java7</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties
index 51c5f32..8d4df46 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties
@@ -8,7 +8,7 @@
 #
 ###############################################################################
 
-featureName=Eclipse JGit Command Line Interface - Source
+featureName=Command Line Interface for Java implementation of Git - Source Code
 providerName=Eclipse JGit
 
 updateSiteName=Eclipse JGit Update Site
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
index a92b36c..1c6e043 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.pgm.source"
       label="%featureName"
-      version="2.3.1.201302201838-r"
+      version="3.0.0.201306101825-r"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
index 671573b..0b67586 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index af9ce1a..546793e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -18,11 +18,12 @@
    <feature url="features/org.eclipse.jgit.junit_0.0.0.qualifier.jar" id="org.eclipse.jgit.junit" version="0.0.0" patch="true">
       <category name="JGit"/>
    </feature>
+   <feature url="features/org.eclipse.jgit.java7_0.0.0.qualifier.jar" id="org.eclipse.jgit.java7" version="0.0.0" patch="true">
+      <category name="JGit"/>
+   </feature>
    <category-def name="JGit" label="JGit">
       <description>
          JGit
       </description>
    </category-def>
 </site>
-
-
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 4873ca9..e731fca 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
@@ -64,6 +64,11 @@
       <artifactId>org.eclipse.jgit</artifactId>
       <version>${project.version}</version>
     </dependency>
+	<dependency>
+	  <groupId>org.eclipse.jgit</groupId>
+	  <artifactId>org.eclipse.jgit.java7</artifactId>
+	  <version>${project.version}</version>
+	</dependency>
     <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties
index 8617d40..dd12bc4 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties
@@ -8,7 +8,7 @@
 #
 ###############################################################################
 
-featureName=Eclipse JGit - Source
+featureName=Java implementation of Git - Source Code
 providerName=Eclipse JGit
 
 updateSiteName=Eclipse JGit Update Site
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index 270b2d7..311aeb7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.source"
       label="%featureName"
-      version="2.3.1.201302201838-r"
+      version="3.0.0.201306101825-r"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index e519c8c..47710c6 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 3317f09..78159e0 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,22 +53,23 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>2.3.1.201302201838-r</version>
+  <version>3.0.0.201306101825-r</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
 
   <properties>
-    <tycho-version>0.16.0</tycho-version>
+    <tycho-version>0.17.0</tycho-version>
     <jetty-version>7.6.0.v20120127</jetty-version>
     <args4j-version>2.0.21.v201301150030</args4j-version>
     <jsch-version>0.1.46.v201205102330</jsch-version>
     <eclipse-site>http://download.eclipse.org/releases/juno</eclipse-site>
-    <orbit-site>http://download.eclipse.org/tools/orbit/downloads/drops/R20130118183705/repository/</orbit-site>
+    <orbit-site>http://download.eclipse.org/tools/orbit/downloads/drops/R20130517111416/repository/</orbit-site>
   </properties>
 
   <modules>
     <module>org.eclipse.jgit.feature</module>
+    <module>org.eclipse.jgit.java7.feature</module>
     <module>org.eclipse.jgit.pgm.feature</module>
     <module>org.eclipse.jgit.source.feature</module>
     <module>org.eclipse.jgit.pgm.source.feature</module>
@@ -94,6 +95,20 @@
     </repository>
   </repositories>
 
+  <distributionManagement>
+    <repository>
+      <id>repo.eclipse.org</id>
+      <name>JGit Maven Repository - Releases</name>
+      <url>https://repo.eclipse.org/content/repositories/jgit-releases/</url>
+    </repository>
+    <snapshotRepository>
+      <id>repo.eclipse.org</id>
+      <name>JGit Maven Repository - Snapshots</name>
+      <url>https://repo.eclipse.org/content/repositories/jgit-snapshots/</url>
+      <uniqueVersion>true</uniqueVersion>
+    </snapshotRepository>
+  </distributionManagement>
+
   <dependencies>
     <!-- sources artifacts so that we can place them in the features -->
     <dependency>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index e370641..48e302a 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -2,23 +2,24 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.eclipse.jgit.api;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.dircache;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.junit;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.merge;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.pgm;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.pgm.opt;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util.io;version="[2.3.1,2.4.0)",
+Import-Package: org.eclipse.jgit.api;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.dircache;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.java7;version="[3.0.0,3.1.0)";resolution:=optional,
+ org.eclipse.jgit.junit;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.merge;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.pgm;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.pgm.internal;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.pgm.opt;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util.io;version="[3.0.0,3.1.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.4.0,5.0.0)",
- org.kohsuke.args4j;version="[2.0.12,2.1.0)",
- org.kohsuke.args4j.spi;version="[2.0.12,2.1.0)"
+ org.kohsuke.args4j;version="[2.0.12,2.1.0)"
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
new file mode 100644
index 0000000..7dac988
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch	
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.jgit.pgm.test/tst"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="2"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.jgit.pgm.test/tst"/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7" path="1" type="4"/>
"/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.pgm.test"/>
</runtimeClasspathEntry>
"/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.pgm.test"/>
+</launchConfiguration>
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests.launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests.launch
index 3e7c17f..46829ca 100644
--- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests.launch
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests.launch
@@ -1,15 +1,16 @@
-<?xml version="1.0" encoding="UTF-8"?><launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/org.eclipse.jgit.pgm.test/tst"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="2"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.jgit.pgm.test/tst"/>
-<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
-<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.pgm.test"/>
-</launchConfiguration>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.jgit.pgm.test/tst"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="2"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.jgit.pgm.test/tst"/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.pgm.test"/>
+</launchConfiguration>
diff --git a/org.eclipse.jgit.pgm.test/plugin.properties b/org.eclipse.jgit.pgm.test/plugin.properties
index df18ade..e131c69 100644
--- a/org.eclipse.jgit.pgm.test/plugin.properties
+++ b/org.eclipse.jgit.pgm.test/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit Command Line Interface Tests
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 8bde689..0d61757 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
@@ -60,6 +60,23 @@
     Tests for command line client tools built on top of JGit.
   </description>
 
+  <profiles>
+    <profile>
+      <id>jgit.java7</id>
+      <activation>
+	<jdk>1.7</jdk>
+      </activation>
+      <dependencies>
+	<dependency>
+	  <groupId>org.eclipse.jgit</groupId>
+	  <artifactId>org.eclipse.jgit.java7</artifactId>
+	  <version>${project.version}</version>
+	  <optional>true</optional>
+	</dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
index 24c4029..a328bae 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
@@ -42,6 +42,8 @@
  */
 package org.eclipse.jgit.lib;
 
+import static org.junit.Assert.assertEquals;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -50,12 +52,11 @@ import java.util.List;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.pgm.CLIGitCommand;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.junit.Before;
 
 public class CLIRepositoryTestCase extends LocalDiskRepositoryTestCase {
 	/** Test repository, initialized for this test case. */
-	protected FileRepository db;
+	protected Repository db;
 
 	/** Working directory of {@link #db}. */
 	protected File trash;
@@ -83,4 +84,92 @@ public class CLIRepositoryTestCase extends LocalDiskRepositoryTestCase {
 	protected void deleteTrashFile(final String name) throws IOException {
 		JGitTestUtil.deleteTrashFile(db, name);
 	}
+
+	/**
+	 * Execute the given commands and print the output to stdout. Use this
+	 * function instead of the normal {@link #execute(String...)} when preparing
+	 * a test case: the command is executed and then its output is printed on
+	 * stdout, thus making it easier to prepare the correct command and expected
+	 * output for the test case.
+	 *
+	 * @param cmds
+	 *            The commands to execute
+	 * @return the result of the command, see {@link #execute(String...)}
+	 * @throws Exception
+	 */
+	protected String[] executeAndPrint(String... cmds) throws Exception {
+		String[] lines = execute(cmds);
+		for (String line : lines) {
+			System.out.println(line);
+		}
+		return lines;
+	}
+
+	/**
+	 * Execute the given commands and print test code comparing expected and
+	 * actual output. Use this function instead of the normal
+	 * {@link #execute(String...)} when preparing a test case: the command is
+	 * executed and test code is generated using the command output as a
+	 * template of what is expected. The code generated is printed on stdout and
+	 * can be pasted in the test case function.
+	 *
+	 * @param cmds
+	 *            The commands to execute
+	 * @return the result of the command, see {@link #execute(String...)}
+	 * @throws Exception
+	 */
+	protected String[] executeAndPrintTestCode(String... cmds) throws Exception {
+		String[] lines = execute(cmds);
+		String cmdString = cmdString(cmds);
+		if (lines.length == 0)
+			System.out.println("\t\tassertTrue(execute(" + cmdString
+					+ ").length == 0);");
+		else {
+			System.out
+					.println("\t\tassertArrayOfLinesEquals(new String[] { //");
+			System.out.print("\t\t\t\t\t\t\"" + escapeJava(lines[0]));
+			for (int i=1; i<lines.length; i++) {
+				System.out.println("\", //");
+				System.out.print("\t\t\t\t\t\t\"" + escapeJava(lines[i]));
+			}
+			System.out.println("\" //");
+			System.out.println("\t\t\t\t}, execute(" + cmdString + ")); //");
+		}
+		return lines;
+	}
+
+	protected String cmdString(String... cmds) {
+		if (cmds.length == 0)
+			return "";
+		else if (cmds.length == 1)
+			return "\"" + escapeJava(cmds[0]) + "\"";
+		else {
+			StringBuilder sb = new StringBuilder(cmdString(cmds[0]));
+			for (int i=1; i<cmds.length; i++) {
+				sb.append(", ");
+				sb.append(cmdString(cmds[i]));
+			}
+			return sb.toString();
+		}
+	}
+
+	protected String escapeJava(String line) {
+		// very crude implementation but ok for generating test code
+		return line.replaceAll("\"", "\\\\\"") //
+				.replaceAll("\\\\", "\\\\\\")
+				.replaceAll("\t", "\\\\t");
+	}
+
+	protected void assertArrayOfLinesEquals(String[] expected, String[] actual) {
+		assertEquals(toText(expected), toText(actual));
+	}
+
+	private static String toText(String[] lines) {
+		StringBuilder b = new StringBuilder();
+		for (String s : lines) {
+			b.append(s);
+			b.append('\n');
+		}
+		return b.toString();
+	}
 }
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
index 78706da..d77b150 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
@@ -48,6 +48,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.CmdLineParser;
 import org.eclipse.jgit.pgm.opt.SubcommandHandler;
 import org.eclipse.jgit.util.IO;
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
index 28a107b..8c5066a 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
@@ -97,11 +97,45 @@ public class MergeTest extends CLIRepositoryTestCase {
 		git.add().addFilepattern("side").call();
 		git.commit().setMessage("side commit").call();
 
-		assertEquals("Merge made by the '" + MergeStrategy.RESOLVE.getName()
+		assertEquals("Merge made by the '" + MergeStrategy.RECURSIVE.getName()
 				+ "' strategy.", execute("git merge master")[0]);
 	}
 
 	@Test
+	public void testMergeNoCommit() throws Exception {
+		git.branchCreate().setName("side").call();
+		writeTrashFile("master", "content");
+		git.add().addFilepattern("master").call();
+		git.commit().setMessage("master commit").call();
+		git.checkout().setName("side").call();
+		writeTrashFile("side", "content");
+		git.add().addFilepattern("side").call();
+		git.commit().setMessage("side commit").call();
+
+		assertEquals(
+				"Automatic merge went well; stopped before committing as requested",
+				execute("git merge --no-commit master")[0]);
+	}
+
+	@Test
+	public void testMergeNoCommitSquash() throws Exception {
+		git.branchCreate().setName("side").call();
+		writeTrashFile("master", "content");
+		git.add().addFilepattern("master").call();
+		git.commit().setMessage("master commit").call();
+		git.checkout().setName("side").call();
+		writeTrashFile("side", "content");
+		git.add().addFilepattern("side").call();
+		git.commit().setMessage("side commit").call();
+
+		assertArrayEquals(
+				new String[] {
+						"Squash commit -- not updating HEAD",
+						"Automatic merge went well; stopped before committing as requested",
+						"" }, execute("git merge --no-commit --squash master"));
+	}
+
+	@Test
 	public void testSquash() throws Exception {
 		git.branchCreate().setName("side").call();
 		writeTrashFile("file1", "content1");
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ReflogTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ReflogTest.java
index 68346ec..ce80832 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ReflogTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ReflogTest.java
@@ -1,80 +1,80 @@
-/*
- * Copyright (C) 2012, Tomasz Zarna <tomasz.zarna at tasktop.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.pgm;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.junit.Test;
-
-public class ReflogTest extends CLIRepositoryTestCase {
-	@Test
-	public void testClean() throws Exception {
-		assertArrayEquals(new String[] { "" }, execute("git reflog"));
-	}
-
-	@Test
-	public void testSingleCommit() throws Exception {
-		new Git(db).commit().setMessage("initial commit").call();
-
-		assertEquals("6fd41be HEAD@{0}: commit (initial): initial commit",
-				execute("git reflog")[0]);
-	}
-
-	@Test
-	public void testBranch() throws Exception {
-		Git git = new Git(db);
-		git.commit().setMessage("first commit").call();
-		git.checkout().setCreateBranch(true).setName("side").call();
-		writeTrashFile("file", "side content");
-		git.add().addFilepattern("file").call();
-		git.commit().setMessage("side commit").call();
-
-		assertArrayEquals(new String[] {
-				"38890c7 side@{0}: commit: side commit",
-				"d216986 side@{1}: branch: Created from commit first commit",
-				"" }, execute("git reflog refs/heads/side"));
-	}
+/*
+ * Copyright (C) 2012, Tomasz Zarna <tomasz.zarna at tasktop.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.junit.Test;
+
+public class ReflogTest extends CLIRepositoryTestCase {
+	@Test
+	public void testClean() throws Exception {
+		assertArrayEquals(new String[] { "" }, execute("git reflog"));
+	}
+
+	@Test
+	public void testSingleCommit() throws Exception {
+		new Git(db).commit().setMessage("initial commit").call();
+
+		assertEquals("6fd41be HEAD@{0}: commit (initial): initial commit",
+				execute("git reflog")[0]);
+	}
+
+	@Test
+	public void testBranch() throws Exception {
+		Git git = new Git(db);
+		git.commit().setMessage("first commit").call();
+		git.checkout().setCreateBranch(true).setName("side").call();
+		writeTrashFile("file", "side content");
+		git.add().addFilepattern("file").call();
+		git.commit().setMessage("side commit").call();
+
+		assertArrayEquals(new String[] {
+				"38890c7 side@{0}: commit: side commit",
+				"d216986 side@{1}: branch: Created from commit first commit",
+				"" }, execute("git reflog refs/heads/side"));
+	}
 }
\ No newline at end of file
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
index ed2c5d0..73ae598 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, François Rey <eclipse.org_ at _francois_._rey_._name>
+ * Copyright (C) 2012, 2013 François Rey <eclipse.org_ at _francois_._rey_._name>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -42,7 +42,6 @@
  */
 package org.eclipse.jgit.pgm;
 
-import static org.junit.Assert.assertEquals;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.lib.CLIRepositoryTestCase;
@@ -192,7 +191,7 @@ public class StatusTest extends CLIRepositoryTestCase {
 						"# On branch master", //
 						"# Unmerged paths:", //
 						"# ", //
-						"# \tunmerged", //
+						"# \tboth modified:      unmerged", //
 						"# ", //
 						"# Untracked files:", //
 						"# ", //
@@ -206,7 +205,7 @@ public class StatusTest extends CLIRepositoryTestCase {
 						"# Not currently on any branch.", //
 						"# Unmerged paths:", //
 						"# ", //
-						"# \tunmerged", //
+						"# \tboth modified:      unmerged", //
 						"# ", //
 						"# Untracked files:", //
 						"# ", //
@@ -214,17 +213,4 @@ public class StatusTest extends CLIRepositoryTestCase {
 						"" //
 				}, execute("git status")); //
 	}
-
-	private void assertArrayOfLinesEquals(String[] expected, String[] actual) {
-		assertEquals(toText(expected), toText(actual));
-	}
-
-	private String toText(String[] lines) {
-		StringBuilder b = new StringBuilder();
-		for (String s : lines) {
-			b.append(s);
-			b.append('\n');
-		}
-		return b.toString();
-	}
 }
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index ae90d08..6de85fd 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -2,39 +2,41 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Import-Package: org.apache.commons.compress.archivers;version="[1.3,2.0)",
  org.apache.commons.compress.archivers.tar;version="[1.3,2.0)",
  org.apache.commons.compress.archivers.zip;version="[1.3,2.0)",
- org.eclipse.jgit.api;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.api.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.awtui;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.blame;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.diff;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.dircache;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.merge;version="2.3.1",
- org.eclipse.jgit.nls;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.notes;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revplot;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk.filter;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.pack;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport.resolver;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.treewalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util.io;version="[2.3.1,2.4.0)",
+ org.eclipse.jgit.api;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.api.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.awtui;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.blame;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.diff;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.dircache;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.merge;version="3.0.0",
+ org.eclipse.jgit.nls;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.notes;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revplot;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk.filter;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.pack;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport.resolver;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.treewalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.treewalk.filter;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util.io;version="[3.0.0,3.1.0)",
  org.kohsuke.args4j;version="[2.0.12,2.1.0)",
  org.kohsuke.args4j.spi;version="[2.0.12,2.1.0)"
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.pgm;version="2.3.1";
+Export-Package: org.eclipse.jgit.pgm;version="3.0.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.nls,
    org.eclipse.jgit.treewalk,
@@ -43,7 +45,8 @@ Export-Package: org.eclipse.jgit.pgm;version="2.3.1";
    org.eclipse.jgit.pgm.opt,
    org.eclipse.jgit.awtui,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="2.3.1",
- org.eclipse.jgit.pgm.opt;version="2.3.1"
+ org.eclipse.jgit.pgm.debug;version="3.0.0",
+ org.eclipse.jgit.pgm.internal;version="3.0.0";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="3.0.0"
 Main-Class: org.eclipse.jgit.pgm.Main
 Implementation-Title: JGit Command Line Interface
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 6a00b0f..1cd5a2b 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@ Bundle-ManifestVersion: 2
 Bundle-Name: org.eclipse.jgit.pgm - Sources
 Bundle-SymbolicName: org.eclipse.jgit.pgm.source;singleton:=true
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 2.3.1.201302201838-r
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="2.3.1";roots="."
+Bundle-Version: 3.0.0.201306101825-r
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="3.0.0";roots="."
 
diff --git a/org.eclipse.jgit.pgm/plugin.properties b/org.eclipse.jgit.pgm/plugin.properties
index d71fee0..118fe47 100644
--- a/org.eclipse.jgit.pgm/plugin.properties
+++ b/org.eclipse.jgit.pgm/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit Command Line Interface
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 5041272..0d85101 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
@@ -93,11 +93,30 @@
     <profile>
       <id>java6</id>
       <activation>
-        <activeByDefault>true</activeByDefault>
+        <jdk>1.6</jdk>
+	  </activation>
+      <dependencies>
+        <dependency>
+          <groupId>org.eclipse.jgit</groupId>
+          <artifactId>org.eclipse.jgit.console</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+      </dependencies>
+    </profile>
+    <profile>
+      <id>java7</id>
+      <activation>
+       <jdk>1.7</jdk>
       </activation>
       <dependencies>
         <dependency>
           <groupId>org.eclipse.jgit</groupId>
+          <artifactId>org.eclipse.jgit.java7</artifactId>
+          <version>${project.version}</version>
+          <optional>true</optional>
+        </dependency>
+        <dependency>
+          <groupId>org.eclipse.jgit</groupId>
           <artifactId>org.eclipse.jgit.console</artifactId>
           <version>${project.version}</version>
         </dependency>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
similarity index 91%
rename from org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
rename to org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 0a4d90b..cdf7123 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -7,14 +7,13 @@ N=N
 
 alreadyOnBranch=Already on ''{0}''
 alreadyUpToDate=Already up-to-date.
-archiveEntryModeIgnored=warning: mode of {0} ignored
 authorInfo=Author: {0} <{1}>
 averageMSPerRead=average {0} ms/read
 branchAlreadyExists=A branch named ''{0}'' already exists.
 branchCreatedFrom=branch: Created from {0}
 branchDetachedHEAD=detached HEAD
-branchIsNotAnAncestorOfYourCurrentHEAD=The branch '{0}' is not an ancestor of your current HEAD.\nIf you are sure you want to delete it, run 'jgit branch -D {0}'.
-branchNotFound=branch '{0}' not found.
+branchIsNotAnAncestorOfYourCurrentHEAD=The branch ''{0}'' is not an ancestor of your current HEAD.\nIf you are sure you want to delete it, run ''jgit branch -D {0}''.
+branchNotFound=branch ''{0}'' not found.
 cacheTreePathInfo="{0}": {1} entries, {2} children
 cannotBeRenamed={0} cannot be renamed
 cannotChekoutNoHeadsAdvertisedByRemote=cannot checkout; no HEAD advertised by remote
@@ -23,7 +22,7 @@ cannotCreateCommand=Cannot create command {0}
 cannotCreateOutputStream=cannot create output stream
 cannotDeatchHEAD=Cannot detach HEAD
 cannotDeleteFile=error: The following file could not be deleted:
-cannotDeleteTheBranchWhichYouAreCurrentlyOn=Cannot delete the branch '{0}' which you are currently on.
+cannotDeleteTheBranchWhichYouAreCurrentlyOn=Cannot delete the branch ''{0}'' which you are currently on.
 cannotGuessLocalNameFrom=cannot guess local name from {0}
 cannotLock=Cannot lock {0}
 cannotMergeDetachedHead=Cannot merge into detached HEAD
@@ -33,7 +32,6 @@ cannotRenameDetachedHEAD=Cannot rename detached HEAD
 cannotResolve=Cannot resolve {0}
 cannotSetupConsole=Cannot setup console
 cannotUseObjectsWithGlog=Cannot use --objects with glog
-cannotWrite=Cannot write {0}
 cantFindGitDirectory=error: can't find git directory
 cantWrite=Can't write {0}
 changesNotStagedForCommit=Changes not staged for commit:
@@ -48,6 +46,7 @@ deletedRemoteBranch=Deleted remote branch {0}
 doesNotExist={0} does not exist
 dontOverwriteLocalChanges=error: Your local changes to the following file would be overwritten by merge:
 everythingUpToDate=Everything up-to-date
+exceptionCaughtDuringExecutionOfArchiveCommand=Exception caught during execution of archive command
 expectedNumberOfbytes=Expected {0} bytes.
 exporting=Exporting {0}
 failedToCommitIndex=failed to commit index
@@ -65,9 +64,12 @@ jgitVersion=jgit version {0}
 lineFormat=# {0}
 listeningOn=Listening on {0}
 mergeConflict=CONFLICT(content): Merge conflict in {0}
+mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge:
 mergeFailed=Automatic merge failed; fix conflicts and then commit the result
+mergeCheckoutFailed=Please, commit your changes or stash them before you can merge.
 mergeMadeBy=Merge made by the ''{0}'' strategy.
-mergedSquashed=Squash commit -- not updating HEAD\nAutomatic merge went well; stopped before committing as requested
+mergedSquashed=Squash commit -- not updating HEAD
+mergeWentWellStoppedBeforeCommitting=Automatic merge went well; stopped before committing as requested
 metaVar_DAG=DAG
 metaVar_KEY=KEY
 metaVar_archiveFormat=format
@@ -76,6 +78,7 @@ metaVar_author=AUTHOR
 metaVar_base=base
 metaVar_blameL=START,END
 metaVar_blameReverse=START..END
+metaVar_branchName=branch
 metaVar_bucket=BUCKET
 metaVar_command=command
 metaVar_commandDetail=DETAIL
@@ -116,14 +119,14 @@ mostCommonlyUsedCommandsAre=The most commonly used commands are:
 needApprovalToDestroyCurrentRepository=Need approval to destroy current repository
 noGitRepositoryConfigured=No Git repository configured.
 noSuchFile=no such file: {0}
+noSuchRemoteRef=no such remote ref: ''{0}''
 noTREESectionInIndex=no 'TREE' section in index
 nonFastForward=non-fast forward
 notABranch={0} is not a branch
 notACommit={0} is not a commit
-notAGitRepository='{0}' not a git repository
+notAGitRepository=''{0}'' not a git repository
 notAJgitCommand={0} is not a jgit command
 notARevision=Not a revision: {0}
-notATagVersionIsRequired={0} is not a tag, --version is required
 notATree={0} is not a tree
 notAValidRefName={0} is not a valid ref name
 notAnIndexFile={0} is not an index file
@@ -144,13 +147,21 @@ remoteMessage=remote: {0}
 remoteRefObjectChangedIsNotExpectedOne=remote ref object changed - is not expected one {0}
 remoteSideDoesNotSupportDeletingRefs=remote side does not support deleting refs
 repaint=Repaint
-serviceNotSupported=Service '{0}' not supported
+serviceNotSupported=Service ''{0}'' not supported
 skippingObject=skipping {0} {1}
 statusFileListFormat=\t%1$s
 statusFileListFormatWithPrefix=\t%1$-11s %2$s
+statusFileListFormatUnmerged=\t%1$-20s%2$s
 statusModified=modified:
 statusNewFile=new file:
 statusRemoved=deleted:
+statusBothDeleted=both deleted:
+statusAddedByUs=added by us:
+statusDeletedByThem=deleted by them:
+statusAddedByThem=added by them:
+statusDeletedByUs=deleted by us:
+statusBothAdded=both added:
+statusBothModified=both modified:
 switchedToNewBranch=Switched to a new branch ''{0}''
 switchedToBranch=Switched to branch ''{0}''
 tagAlreadyExists=tag ''{0}'' already exists
@@ -246,6 +257,7 @@ usage_manageReflogInformation=Manage reflog information
 usage_mergeStrategy=Use the given merge strategy. Can be supplied more than once to specify them in the order they should be tried. If there is no -s option, the resolve strategy is used. Currently the following strategies are supported: ours, theirs, simple-two-way-in-core, resolve
 usage_moveRenameABranch=move/rename a branch
 usage_nameStatus=show only name and status of files
+usage_noCommit=Don't commit after a successful merge
 usage_noPrefix=do not show any source or destination prefix
 usage_noRenames=disable rename detection
 usage_noShowStandardNotes=Disable showing notes from the standard /refs/notes/commits branch
@@ -274,5 +286,5 @@ usage_symbolicVersionForTheProject=Symbolic version for the project
 usage_tagMessage=tag message
 usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
 usage_useNameInsteadOfOriginToTrackUpstream=use <name> instead of 'origin' to track upstream
+usage_checkoutBranchAfterClone=checkout named branch instead of remotes's HEAD
 usage_viewCommitHistory=View commit history
-warningNoCommitGivenOnCommandLine=warning: No commit given on command line, assuming {0}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java
index ad9a36d..1fe39da 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java
@@ -57,6 +57,7 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.transport.FetchResult;
 import org.eclipse.jgit.transport.TrackingRefUpdate;
 import org.kohsuke.args4j.Option;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java
index 365d968..5e66c36 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java
@@ -57,6 +57,7 @@ import java.net.URLConnection;
 import java.text.MessageFormat;
 import java.util.Properties;
 
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.transport.AmazonS3;
 import org.kohsuke.args4j.Argument;
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
index 963528d..815c96b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
@@ -43,144 +43,33 @@
 
 package org.eclipse.jgit.pgm;
 
-import java.lang.String;
-import java.lang.System;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.EnumMap;
-import java.util.Map;
-import java.text.MessageFormat;
-
-import org.apache.commons.compress.archivers.ArchiveOutputStream;
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
-import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
-import org.apache.commons.compress.archivers.tar.TarConstants;
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
-import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.MutableObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.pgm.TextBuiltin;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.pgm.archive.ArchiveCommand;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
 @Command(common = true, usage = "usage_archive")
 class Archive extends TextBuiltin {
 	@Argument(index = 0, metaVar = "metaVar_treeish")
-	private AbstractTreeIterator tree;
+	private ObjectId tree;
 
 	@Option(name = "--format", metaVar = "metaVar_archiveFormat", usage = "usage_archiveFormat")
-	private Format format = Format.ZIP;
+	private ArchiveCommand.Format format = ArchiveCommand.Format.ZIP;
 
 	@Override
 	protected void run() throws Exception {
-		final TreeWalk walk = new TreeWalk(db);
-		final ObjectReader reader = walk.getObjectReader();
-		final MutableObjectId idBuf = new MutableObjectId();
-		final Archiver fmt = formats.get(format);
-		final ArchiveOutputStream outa = fmt.createArchiveOutputStream(outs);
-
 		if (tree == null)
 			throw die(CLIText.get().treeIsRequired);
 
-		walk.reset();
-		walk.addTree(tree);
-		walk.setRecursive(true);
-		while (walk.next()) {
-			final String name = walk.getPathString();
-			final FileMode mode = walk.getFileMode(0);
-
-			if (mode == FileMode.TREE)
-				// ZIP entries for directories are optional.
-				// Leave them out, mimicking "git archive".
-				continue;
-
-			walk.getObjectId(idBuf, 0);
-			fmt.putEntry(name, mode, reader.open(idBuf), outa);
+		final ArchiveCommand cmd = new ArchiveCommand(db);
+		try {
+			cmd.setTree(tree)
+					.setFormat(format)
+					.setOutputStream(outs).call();
+		} finally {
+			cmd.release();
 		}
-
-		outa.close();
-	}
-
-	static private void warnArchiveEntryModeIgnored(String name) {
-		System.err.println(MessageFormat.format( //
-				CLIText.get().archiveEntryModeIgnored, //
-				name));
-	}
-
-	public enum Format {
-		ZIP,
-		TAR
-	}
-
-	private static interface Archiver {
-		ArchiveOutputStream createArchiveOutputStream(OutputStream s);
-		void putEntry(String path, FileMode mode, //
-				ObjectLoader loader, ArchiveOutputStream out) //
-				throws IOException;
-	}
-
-	private static final Map<Format, Archiver> formats;
-	static {
-		Map<Format, Archiver> fmts = new EnumMap<Format, Archiver>(Format.class);
-		fmts.put(Format.ZIP, new Archiver() {
-			public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
-				return new ZipArchiveOutputStream(s);
-			}
-
-			public void putEntry(String path, FileMode mode, //
-					ObjectLoader loader, ArchiveOutputStream out) //
-					throws IOException {
-				final ZipArchiveEntry entry = new ZipArchiveEntry(path);
-
-				if (mode == FileMode.REGULAR_FILE) {
-					// ok
-				} else if (mode == FileMode.EXECUTABLE_FILE
-						|| mode == FileMode.SYMLINK) {
-					entry.setUnixMode(mode.getBits());
-				} else {
-					warnArchiveEntryModeIgnored(path);
-				}
-				entry.setSize(loader.getSize());
-				out.putArchiveEntry(entry);
-				loader.copyTo(out);
-				out.closeArchiveEntry();
-			}
-		});
-		fmts.put(Format.TAR, new Archiver() {
-			public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
-				return new TarArchiveOutputStream(s);
-			}
-
-			public void putEntry(String path, FileMode mode, //
-					ObjectLoader loader, ArchiveOutputStream out) //
-					throws IOException {
-				if (mode == FileMode.SYMLINK) {
-					final TarArchiveEntry entry = new TarArchiveEntry( //
-							path, TarConstants.LF_SYMLINK);
-					entry.setLinkName(new String( //
-							loader.getCachedBytes(100), "UTF-8")); //$NON-NLS-1$
-					out.putArchiveEntry(entry);
-					out.closeArchiveEntry();
-					return;
-				}
-
-				final TarArchiveEntry entry = new TarArchiveEntry(path);
-				if (mode == FileMode.REGULAR_FILE ||
-				    mode == FileMode.EXECUTABLE_FILE)
-					entry.setMode(mode.getBits());
-				else
-					warnArchiveEntryModeIgnored(path);
-				entry.setSize(loader.getSize());
-				out.putArchiveEntry(entry);
-				loader.copyTo(out);
-				out.closeArchiveEntry();
-			}
-		});
-		formats = fmts;
 	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
index 341a25b..286710e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -68,6 +68,7 @@ import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.kohsuke.args4j.Argument;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
index 28e30bc..969a8e0 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.lib.RefRename;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.CmdLineParser;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.kohsuke.args4j.Argument;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 953a5e0..8115039 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -53,6 +53,7 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index 5406527..888f54d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -47,9 +47,6 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
@@ -57,13 +54,14 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefComparator;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.transport.FetchResult;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
@@ -78,13 +76,16 @@ class Clone extends AbstractFetchCommand {
 	@Option(name = "--origin", aliases = { "-o" }, metaVar = "metaVar_remoteName", usage = "usage_useNameInsteadOfOriginToTrackUpstream")
 	private String remoteName = Constants.DEFAULT_REMOTE_NAME;
 
+	@Option(name = "--branch", aliases = { "-b" }, metaVar = "metaVar_branchName", usage = "usage_checkoutBranchAfterClone")
+	private String branch;
+
 	@Argument(index = 0, required = true, metaVar = "metaVar_uriish")
 	private String sourceUri;
 
 	@Argument(index = 1, metaVar = "metaVar_directory")
 	private String localName;
 
-	private FileRepository dst;
+	private Repository dst;
 
 	@Override
 	protected final boolean requiresRepository() {
@@ -107,9 +108,9 @@ class Clone extends AbstractFetchCommand {
 		if (gitdir == null)
 			gitdir = new File(localName, Constants.DOT_GIT).getAbsolutePath();
 
-		dst = new FileRepository(gitdir);
+		dst = new FileRepositoryBuilder().setGitDir(new File(gitdir)).build();
 		dst.create();
-		final FileBasedConfig dstcfg = dst.getConfig();
+		final StoredConfig dstcfg = dst.getConfig();
 		dstcfg.setBoolean("core", null, "bare", false); //$NON-NLS-1$ //$NON-NLS-2$
 		dstcfg.save();
 		db = dst;
@@ -121,13 +122,21 @@ class Clone extends AbstractFetchCommand {
 
 		saveRemote(uri);
 		final FetchResult r = runFetch();
-		final Ref branch = guessHEAD(r);
-		doCheckout(branch);
+		final Ref checkoutRef;
+		if (branch == null)
+			checkoutRef = guessHEAD(r);
+		else {
+			checkoutRef = r.getAdvertisedRef(Constants.R_HEADS + branch);
+			if (checkoutRef == null)
+				throw die(MessageFormat.format(CLIText.get().noSuchRemoteRef,
+						branch));
+		}
+		doCheckout(checkoutRef);
 	}
 
 	private void saveRemote(final URIish uri) throws URISyntaxException,
 			IOException {
-		final FileBasedConfig dstcfg = dst.getConfig();
+		final StoredConfig dstcfg = dst.getConfig();
 		final RemoteConfig rc = new RemoteConfig(dstcfg, remoteName);
 		rc.addURI(uri);
 		rc.addFetchRefSpec(new RefSpec().setForceUpdate(true)
@@ -152,19 +161,16 @@ class Clone extends AbstractFetchCommand {
 
 	private static Ref guessHEAD(final FetchResult result) {
 		final Ref idHEAD = result.getAdvertisedRef(Constants.HEAD);
-		final List<Ref> availableRefs = new ArrayList<Ref>();
 		Ref head = null;
 		for (final Ref r : result.getAdvertisedRefs()) {
 			final String n = r.getName();
 			if (!n.startsWith(Constants.R_HEADS))
 				continue;
-			availableRefs.add(r);
 			if (idHEAD == null || head != null)
 				continue;
 			if (r.getObjectId().equals(idHEAD.getObjectId()))
 				head = r;
 		}
-		Collections.sort(availableRefs, RefComparator.INSTANCE);
 		if (idHEAD != null && head == null)
 			head = idHEAD;
 		return head;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java
index f637aba..5222515 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandRef.java
@@ -47,6 +47,8 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.text.MessageFormat;
 
+import org.eclipse.jgit.pgm.internal.CLIText;
+
 /**
  * Description of a command (a {@link TextBuiltin} subclass.
  * <p>
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
index a50e744..14c449a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
@@ -48,6 +48,7 @@ import org.eclipse.jgit.api.errors.NoHeadException;
 import org.eclipse.jgit.api.errors.NoMessageException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.util.RawParseUtils;
 import org.kohsuke.args4j.Argument;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
index 8087efe..04182d6 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
@@ -50,8 +50,8 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executors;
 
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.WindowCache;
 import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.storage.pack.PackConfig;
 import org.eclipse.jgit.transport.DaemonClient;
@@ -111,11 +111,7 @@ class Daemon extends TextBuiltin {
 
 			FileBasedConfig cfg = new FileBasedConfig(configFile, FS.DETECTED);
 			cfg.load();
-
-			WindowCacheConfig wcc = new WindowCacheConfig();
-			wcc.fromConfig(cfg);
-			WindowCache.reconfigure(wcc);
-
+			new WindowCacheConfig().fromConfig(cfg).install();
 			packConfig.fromConfig(cfg);
 		}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
index d38c53a..8f05168 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
@@ -66,6 +66,7 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
index faaf85f..8b35fd1 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
@@ -59,6 +59,7 @@ import javax.swing.JScrollPane;
 
 import org.eclipse.jgit.awtui.CommitGraphPane;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revplot.PlotWalk;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevSort;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
index 4585650..731ef01 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
@@ -46,9 +46,9 @@ package org.eclipse.jgit.pgm;
 
 import java.io.BufferedInputStream;
 
+import org.eclipse.jgit.internal.storage.file.ObjectDirectoryPackParser;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.storage.file.ObjectDirectoryPackParser;
 import org.eclipse.jgit.transport.PackParser;
 import org.kohsuke.args4j.Option;
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
index ab49887..b3e73b5 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Init.java
@@ -53,6 +53,7 @@ import java.text.MessageFormat;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.InitCommand;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.kohsuke.args4j.Option;
 
 @Command(common = true, usage = "usage_CreateAnEmptyGitRepository")
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index eaba6e5..ae0c954 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -68,6 +68,7 @@ import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.util.GitDateFormatter;
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 69d7d35..b948bc2 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
@@ -59,6 +59,7 @@ import org.eclipse.jgit.awtui.AwtCredentialsProvider;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryBuilder;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.CmdLineParser;
 import org.eclipse.jgit.pgm.opt.SubcommandHandler;
 import org.eclipse.jgit.util.CachedAuthenticator;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
index 7eaa5fa..67b7cc8 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
@@ -51,12 +51,14 @@ import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.MergeCommand;
 import org.eclipse.jgit.api.MergeResult;
 import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
+import org.eclipse.jgit.api.errors.CheckoutConflictException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.merge.MergeStrategy;
 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.kohsuke.args4j.Argument;
@@ -71,7 +73,10 @@ class Merge extends TextBuiltin {
 	@Option(name = "--squash", usage = "usage_squash")
 	private boolean squash;
 
-	private MergeStrategy mergeStrategy = MergeStrategy.RESOLVE;
+	@Option(name = "--no-commit", usage = "usage_noCommit")
+	private boolean noCommit = false;
+
+	private MergeStrategy mergeStrategy = MergeStrategy.RECURSIVE;
 
 	@Argument(required = true)
 	private String ref;
@@ -111,12 +116,17 @@ class Merge extends TextBuiltin {
 		Ref oldHead = db.getRef(Constants.HEAD);
 		Git git = new Git(db);
 		MergeCommand mergeCmd = git.merge().setStrategy(mergeStrategy)
-				.setSquash(squash).setFastForward(ff);
+				.setSquash(squash).setFastForward(ff).setCommit(!noCommit);
 		if (srcRef != null)
 			mergeCmd.include(srcRef);
 		else
 			mergeCmd.include(src);
-		MergeResult result = mergeCmd.call();
+		MergeResult result;
+		try {
+			result = mergeCmd.call();
+		} catch (CheckoutConflictException e) {
+			result = new MergeResult(e.getConflictingPaths()); // CHECKOUT_CONFLICT
+		}
 
 		switch (result.getMergeStatus()) {
 		case ALREADY_UP_TO_DATE:
@@ -131,6 +141,12 @@ class Merge extends TextBuiltin {
 					.name()));
 			outw.println(result.getMergeStatus().toString());
 			break;
+		case CHECKOUT_CONFLICT:
+			outw.println(CLIText.get().mergeCheckoutConflict);
+			for (String collidingPath : result.getCheckoutConflicts())
+				outw.println("\t" + collidingPath); //$NON-NLS-1$
+			outw.println(CLIText.get().mergeCheckoutFailed);
+			break;
 		case CONFLICTING:
 			for (String collidingPath : result.getConflicts().keySet())
 				outw.println(MessageFormat.format(CLIText.get().mergeConflict,
@@ -160,9 +176,14 @@ class Merge extends TextBuiltin {
 				name = "recursive"; //$NON-NLS-1$
 			outw.println(MessageFormat.format(CLIText.get().mergeMadeBy, name));
 			break;
+		case MERGED_NOT_COMMITTED:
+			outw.println(CLIText.get().mergeWentWellStoppedBeforeCommitting);
+			break;
 		case MERGED_SQUASHED:
 		case FAST_FORWARD_SQUASHED:
+		case MERGED_SQUASHED_NOT_COMMITTED:
 			outw.println(CLIText.get().mergedSquashed);
+			outw.println(CLIText.get().mergeWentWellStoppedBeforeCommitting);
 			break;
 		case ABORTED:
 			throw die(CLIText.get().ffNotPossibleAborting);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
index ea3cac7..b252de8 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
@@ -58,6 +58,7 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.transport.PushResult;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
index dd6de1d..bc8f497 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/ReceivePack.java
@@ -49,6 +49,7 @@ import java.text.MessageFormat;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.util.FS;
 import org.kohsuke.args4j.Argument;
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java
index 4ac7ade..aa90f8d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java
@@ -1,88 +1,82 @@
-/*
- * Copyright (C) 2012, Tomasz Zarna <tomasz.zarna at tasktop.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.pgm;
-
-import java.util.Collection;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.ReflogCommand;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.kohsuke.args4j.Argument;
-
- at Command(common = true, usage = "usage_manageReflogInformation")
-class Reflog extends TextBuiltin {
-
-	@Argument(metaVar = "metaVar_ref")
-	private String ref;
-
-	@Override
-	protected void run() throws Exception {
-		ReflogCommand cmd = new Git(db).reflog();
-		if (ref != null)
-			cmd.setRef(ref);
-		Collection<ReflogEntry> entries = cmd.call();
-		int i = 0;
-		for (ReflogEntry entry : entries) {
-			outw.println(toString(entry, i++));
-		}
-	}
-
-	private String toString(ReflogEntry entry, int i) {
-		final StringBuilder s = new StringBuilder();
-		s.append(entry.getNewId().abbreviate(7).name());
-		s.append(" "); //$NON-NLS-1$
-		s.append(ref == null ? Constants.HEAD : Repository.shortenRefName(ref));
-		s.append("@{" + i + "}:"); //$NON-NLS-1$ //$NON-NLS-2$
-		s.append(" "); //$NON-NLS-1$
-		// temporary workaround for bug 393463
-		if (entry.getOldId().equals(ObjectId.zeroId()))
-			s.append(entry.getComment().replaceFirst("^commit:", //$NON-NLS-1$
-					"commit (initial):")); //$NON-NLS-1$
-		else
-			s.append(entry.getComment());
-		return s.toString();
-	}
-}
+/*
+ * Copyright (C) 2012, Tomasz Zarna <tomasz.zarna at tasktop.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+import java.util.Collection;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.ReflogCommand;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.Repository;
+import org.kohsuke.args4j.Argument;
+
+ at Command(common = true, usage = "usage_manageReflogInformation")
+class Reflog extends TextBuiltin {
+
+	@Argument(metaVar = "metaVar_ref")
+	private String ref;
+
+	@Override
+	protected void run() throws Exception {
+		ReflogCommand cmd = new Git(db).reflog();
+		if (ref != null)
+			cmd.setRef(ref);
+		Collection<ReflogEntry> entries = cmd.call();
+		int i = 0;
+		for (ReflogEntry entry : entries) {
+			outw.println(toString(entry, i++));
+		}
+	}
+
+	private String toString(ReflogEntry entry, int i) {
+		final StringBuilder s = new StringBuilder();
+		s.append(entry.getNewId().abbreviate(7).name());
+		s.append(" "); //$NON-NLS-1$
+		s.append(ref == null ? Constants.HEAD : Repository.shortenRefName(ref));
+		s.append("@{" + i + "}:"); //$NON-NLS-1$ //$NON-NLS-2$
+		s.append(" "); //$NON-NLS-1$
+		s.append(entry.getComment());
+		return s.toString();
+	}
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
index 8543dcb..4327403 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
@@ -50,10 +50,12 @@ import java.util.List;
 
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
+import org.eclipse.jgit.diff.DiffConfig;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
 import org.eclipse.jgit.revwalk.FollowFilter;
 import org.eclipse.jgit.revwalk.ObjectWalk;
@@ -117,9 +119,7 @@ abstract class RevWalkTextBuiltin extends TextBuiltin {
 	}
 
 	@Option(name = "--follow", metaVar = "metaVar_path")
-	void follow(final String path) {
-		pathFilter = FollowFilter.create(path);
-	}
+	private String followPath;
 
 	@Argument(index = 0, metaVar = "metaVar_commitish")
 	private final List<RevCommit> commits = new ArrayList<RevCommit>();
@@ -150,11 +150,14 @@ abstract class RevWalkTextBuiltin extends TextBuiltin {
 		for (final RevSort s : sorting)
 			walk.sort(s, true);
 
-		if (pathFilter instanceof FollowFilter)
-			walk.setTreeFilter(pathFilter);
-		else if (pathFilter != TreeFilter.ALL)
+		if (pathFilter == TreeFilter.ALL) {
+			if (followPath != null)
+				walk.setTreeFilter(FollowFilter.create(followPath,
+						db.getConfig().get(DiffConfig.KEY)));
+		} else if (pathFilter != TreeFilter.ALL) {
 			walk.setTreeFilter(AndTreeFilter.create(pathFilter,
 					TreeFilter.ANY_DIFF));
+		}
 
 		if (revLimiter.size() == 1)
 			walk.setRevFilter(revLimiter.get(0));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
index 7d6a171..a33a2d4 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
@@ -63,6 +63,7 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
index 4d2308e..45cfd92 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, François Rey <eclipse.org_ at _francois_._rey_._name>
+ * Copyright (C) 2011, 2013 François Rey <eclipse.org_ at _francois_._rey_._name>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -47,12 +47,16 @@ import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.IndexDiff.StageState;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 @Command(usage = "usage_Status", common = true)
 class Status extends TextBuiltin {
@@ -63,6 +67,8 @@ class Status extends TextBuiltin {
 
 	protected final String statusFileListFormatWithPrefix = CLIText.get().statusFileListFormatWithPrefix;
 
+	protected final String statusFileListFormatUnmerged = CLIText.get().statusFileListFormatUnmerged;
+
 	@Override
 	protected void run() throws Exception {
 		// Print current branch name
@@ -82,7 +88,8 @@ class Status extends TextBuiltin {
 		Collection<String> modified = status.getModified();
 		Collection<String> missing = status.getMissing();
 		Collection<String> untracked = status.getUntracked();
-		Collection<String> unmerged = status.getConflicting();
+		Map<String, StageState> unmergedStates = status
+				.getConflictingStageState();
 		Collection<String> toBeCommitted = new ArrayList<String>(added);
 		toBeCommitted.addAll(changed);
 		toBeCommitted.addAll(removed);
@@ -106,12 +113,12 @@ class Status extends TextBuiltin {
 					modified, missing, null);
 			firstHeader = false;
 		}
-		int nbUnmerged = unmerged.size();
+		int nbUnmerged = unmergedStates.size();
 		if (nbUnmerged > 0) {
 			if (!firstHeader)
 				printSectionHeader(""); //$NON-NLS-1$
 			printSectionHeader(CLIText.get().unmergedPaths);
-			printList(unmerged);
+			printUnmerged(unmergedStates);
 			firstHeader = false;
 		}
 		int nbUntracked = untracked.size();
@@ -168,4 +175,40 @@ class Status extends TextBuiltin {
 		}
 		return list.size();
 	}
+
+	private void printUnmerged(Map<String, StageState> unmergedStates)
+			throws IOException {
+		List<String> paths = new ArrayList<String>(unmergedStates.keySet());
+		Collections.sort(paths);
+		for (String path : paths) {
+			StageState state = unmergedStates.get(path);
+			String stateDescription = getStageStateDescription(state);
+			outw.println(CLIText.formatLine(String.format(
+					statusFileListFormatUnmerged, stateDescription, path)));
+			outw.flush();
+		}
+	}
+
+	private static String getStageStateDescription(StageState stageState) {
+		CLIText text = CLIText.get();
+		switch (stageState) {
+		case BOTH_DELETED:
+			return text.statusBothDeleted;
+		case ADDED_BY_US:
+			return text.statusAddedByUs;
+		case DELETED_BY_THEM:
+			return text.statusDeletedByThem;
+		case ADDED_BY_THEM:
+			return text.statusAddedByThem;
+		case DELETED_BY_US:
+			return text.statusDeletedByUs;
+		case BOTH_ADDED:
+			return text.statusBothAdded;
+		case BOTH_MODIFIED:
+			return text.statusBothModified;
+		default:
+			throw new IllegalArgumentException("Unknown StageState: " //$NON-NLS-1$
+					+ stageState);
+		}
+	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index 23a16ab..a90d4c4 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -58,6 +58,7 @@ import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
index c4e89c3..dc08143 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
@@ -60,6 +60,7 @@ import java.util.ResourceBundle;
 
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.pgm.opt.CmdLineParser;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.util.io.ThrowingPrintWriter;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
index df7f1c3..4ace0aa 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/UploadPack.java
@@ -49,6 +49,7 @@ import java.text.MessageFormat;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.util.FS;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java
index cd7f511..2dce535 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Version.java
@@ -45,6 +45,8 @@ package org.eclipse.jgit.pgm;
 
 import java.text.MessageFormat;
 
+import org.eclipse.jgit.pgm.internal.CLIText;
+
 @Command(common = true, usage = "usage_DisplayTheVersionOfJgit")
 class Version extends TextBuiltin {
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java
similarity index 58%
copy from org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
copy to org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java
index 963528d..1235d0a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java
@@ -40,16 +40,12 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+package org.eclipse.jgit.pgm.archive;
 
-package org.eclipse.jgit.pgm;
-
-import java.lang.String;
-import java.lang.System;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.EnumMap;
 import java.util.Map;
-import java.text.MessageFormat;
 
 import org.apache.commons.compress.archivers.ArchiveOutputStream;
 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@@ -57,63 +53,64 @@ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
 import org.apache.commons.compress.archivers.tar.TarConstants;
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.GitCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.pgm.CLIText;
-import org.eclipse.jgit.pgm.TextBuiltin;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.TreeWalk;
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.Option;
-
- at Command(common = true, usage = "usage_archive")
-class Archive extends TextBuiltin {
-	@Argument(index = 0, metaVar = "metaVar_treeish")
-	private AbstractTreeIterator tree;
-
-	@Option(name = "--format", metaVar = "metaVar_archiveFormat", usage = "usage_archiveFormat")
-	private Format format = Format.ZIP;
-
-	@Override
-	protected void run() throws Exception {
-		final TreeWalk walk = new TreeWalk(db);
-		final ObjectReader reader = walk.getObjectReader();
-		final MutableObjectId idBuf = new MutableObjectId();
-		final Archiver fmt = formats.get(format);
-		final ArchiveOutputStream outa = fmt.createArchiveOutputStream(outs);
-
-		if (tree == null)
-			throw die(CLIText.get().treeIsRequired);
-
-		walk.reset();
-		walk.addTree(tree);
-		walk.setRecursive(true);
-		while (walk.next()) {
-			final String name = walk.getPathString();
-			final FileMode mode = walk.getFileMode(0);
-
-			if (mode == FileMode.TREE)
-				// ZIP entries for directories are optional.
-				// Leave them out, mimicking "git archive".
-				continue;
-
-			walk.getObjectId(idBuf, 0);
-			fmt.putEntry(name, mode, reader.open(idBuf), outa);
-		}
 
-		outa.close();
-	}
-
-	static private void warnArchiveEntryModeIgnored(String name) {
-		System.err.println(MessageFormat.format( //
-				CLIText.get().archiveEntryModeIgnored, //
-				name));
-	}
-
-	public enum Format {
+/**
+ * Create an archive of files from a named tree.
+ * <p>
+ * Examples (<code>git</code> is a {@link Git} instance):
+ * <p>
+ * Create a tarball from HEAD:
+ *
+ * <pre>
+ * cmd = new ArchiveCommand(git.getRepository());
+ * try {
+ *	cmd.setTree(db.resolve("HEAD"))
+ *		.setOutputStream(out).call();
+ * } finally {
+ *	cmd.release();
+ * }
+ * </pre>
+ * <p>
+ * Create a ZIP file from master:
+ *
+ * <pre>
+ * try {
+ *	cmd.setTree(db.resolve("master"))
+ *		.setFormat(ArchiveCommand.Format.ZIP)
+ *		.setOutputStream(out).call();
+ * } finally {
+ *	cmd.release();
+ * }
+ * </pre>
+ *
+ * @see <a href="http://git-htmldocs.googlecode.com/git/git-archive.html"
+ *      >Git documentation about archive</a>
+ *
+ * @since 3.0
+ */
+public class ArchiveCommand extends GitCommand<OutputStream> {
+	/**
+	 * Available archival formats (corresponding to values for
+	 * the --format= option)
+	 */
+	public static enum Format {
+		/** Zip format */
 		ZIP,
+
+		/** Posix TAR-format */
 		TAR
 	}
 
@@ -125,6 +122,7 @@ class Archive extends TextBuiltin {
 	}
 
 	private static final Map<Format, Archiver> formats;
+
 	static {
 		Map<Format, Archiver> fmts = new EnumMap<Format, Archiver>(Format.class);
 		fmts.put(Format.ZIP, new Archiver() {
@@ -143,7 +141,8 @@ class Archive extends TextBuiltin {
 						|| mode == FileMode.SYMLINK) {
 					entry.setUnixMode(mode.getBits());
 				} else {
-					warnArchiveEntryModeIgnored(path);
+					// TODO(jrn): Let the caller know the tree contained
+					// an entry with unsupported mode (e.g., a submodule).
 				}
 				entry.setSize(loader.getSize());
 				out.putArchiveEntry(entry);
@@ -171,10 +170,12 @@ class Archive extends TextBuiltin {
 
 				final TarArchiveEntry entry = new TarArchiveEntry(path);
 				if (mode == FileMode.REGULAR_FILE ||
-				    mode == FileMode.EXECUTABLE_FILE)
+				    mode == FileMode.EXECUTABLE_FILE) {
 					entry.setMode(mode.getBits());
-				else
-					warnArchiveEntryModeIgnored(path);
+				} else {
+					// TODO(jrn): Let the caller know the tree contained
+					// an entry with unsupported mode (e.g., a submodule).
+				}
 				entry.setSize(loader.getSize());
 				out.putArchiveEntry(entry);
 				loader.copyTo(out);
@@ -183,4 +184,95 @@ class Archive extends TextBuiltin {
 		});
 		formats = fmts;
 	}
+
+	private OutputStream out;
+	private TreeWalk walk;
+	private Format format = Format.TAR;
+
+	/**
+	 * @param repo
+	 */
+	public ArchiveCommand(Repository repo) {
+		super(repo);
+		walk = new TreeWalk(repo);
+	}
+
+	/**
+	 * Release any resources used by the internal ObjectReader.
+	 * <p>
+	 * This does not close the output stream set with setOutputStream, which
+	 * belongs to the caller.
+	 */
+	public void release() {
+		walk.release();
+	}
+
+	/**
+	 * @return the stream to which the archive has been written
+	 */
+	@Override
+	public OutputStream call() throws GitAPIException {
+		final MutableObjectId idBuf = new MutableObjectId();
+		final Archiver fmt = formats.get(format);
+		final ArchiveOutputStream outa = fmt.createArchiveOutputStream(out);
+		final ObjectReader reader = walk.getObjectReader();
+
+		try {
+			try {
+				walk.setRecursive(true);
+				while (walk.next()) {
+					final String name = walk.getPathString();
+					final FileMode mode = walk.getFileMode(0);
+
+					if (mode == FileMode.TREE)
+						// ZIP entries for directories are optional.
+						// Leave them out, mimicking "git archive".
+						continue;
+
+					walk.getObjectId(idBuf, 0);
+					fmt.putEntry(name, mode, reader.open(idBuf), outa);
+				}
+			} finally {
+				outa.close();
+			}
+		} catch (IOException e) {
+			// TODO(jrn): Throw finer-grained errors.
+			throw new JGitInternalException(
+					CLIText.get().exceptionCaughtDuringExecutionOfArchiveCommand, e);
+		}
+
+		return out;
+	}
+
+	/**
+	 * @param tree
+	 *            the tag, commit, or tree object to produce an archive for
+	 * @return this
+	 * @throws IOException
+	 */
+	public ArchiveCommand setTree(ObjectId tree) throws IOException {
+		final RevWalk rw = new RevWalk(walk.getObjectReader());
+		walk.reset(rw.parseTree(tree));
+		return this;
+	}
+
+	/**
+	 * @param out
+	 *	      the stream to which to write the archive
+	 * @return this
+	 */
+	public ArchiveCommand setOutputStream(OutputStream out) {
+		this.out = out;
+		return this;
+	}
+
+	/**
+	 * @param fmt
+	 *	      archive format (e.g., Format.TAR)
+	 * @return this
+	 */
+	public ArchiveCommand setFormat(Format fmt) {
+		this.format = fmt;
+		return this;
+	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
index 663f16c..3efe884 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
@@ -70,8 +70,8 @@ import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryBuilder;
 import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.TreeWalk;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/Gc.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/Gc.java
index 656a97c..615d50f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/Gc.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/Gc.java
@@ -43,10 +43,10 @@
 
 package org.eclipse.jgit.pgm.debug;
 
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.GC;
 import org.eclipse.jgit.lib.TextProgressMonitor;
 import org.eclipse.jgit.pgm.TextBuiltin;
-import org.eclipse.jgit.storage.file.FileRepository;
-import org.eclipse.jgit.storage.file.GC;
 
 class Gc extends TextBuiltin {
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java
index 4769f11..5e6195e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/MakeCacheTree.java
@@ -51,8 +51,8 @@ import java.text.MessageFormat;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheTree;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 class MakeCacheTree extends TextBuiltin {
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java
index ab4ec2f..7cef64b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ReadDirCache.java
@@ -48,8 +48,8 @@ import static java.lang.Long.valueOf;
 
 import java.text.MessageFormat;
 
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 class ReadDirCache extends TextBuiltin {
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 7f1e332..f25956f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -58,6 +58,7 @@ import java.util.Map;
 
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.internal.storage.file.LockFile;
 import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -69,10 +70,9 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefWriter;
 import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.LockFile;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
index 9fa8c69..7b0c287 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCacheTree.java
@@ -51,8 +51,8 @@ import java.text.MessageFormat;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheTree;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 class ShowCacheTree extends TextBuiltin {
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
index a0b461d..4f6d150 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
@@ -46,11 +46,11 @@ package org.eclipse.jgit.pgm.debug;
 import java.net.URL;
 
 import org.kohsuke.args4j.Option;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.Command;
 import org.eclipse.jgit.pgm.CommandCatalog;
 import org.eclipse.jgit.pgm.CommandRef;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 @Command(usage = "usage_displayAListOfAllRegisteredJgitCommands")
 class ShowCommands extends TextBuiltin {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
index 338929a..26ad931 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
@@ -51,18 +51,18 @@ import java.util.zip.InflaterInputStream;
 
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
+import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.pgm.TextBuiltin;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.pack.BinaryDelta;
-import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
-import org.eclipse.jgit.storage.pack.PackWriter;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.util.TemporaryBuffer;
 import org.kohsuke.args4j.Argument;
 
@@ -94,7 +94,7 @@ class ShowPackDelta extends TextBuiltin {
 			throws IOException, MissingObjectException,
 			StoredObjectRepresentationNotAvailableException {
 		ObjectReuseAsIs asis = (ObjectReuseAsIs) reader;
-		ObjectToPack target = asis.newObjectToPack(obj);
+		ObjectToPack target = asis.newObjectToPack(obj, obj.getType());
 
 		PackWriter pw = new PackWriter(reader) {
 			@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
index aa46ea1..e43c9a6 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
@@ -64,8 +64,8 @@ import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryBuilder;
 import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.util.FS;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
index 142dbee..4adb276 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteDirCache.java
@@ -45,8 +45,8 @@
 package org.eclipse.jgit.pgm.debug;
 
 import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 class WriteDirCache extends TextBuiltin {
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
similarity index 92%
rename from org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
rename to org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 552629e..62865d5 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, 2012 Sasa Zivkov <sasa.zivkov at sap.com>
+ * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.pgm;
+package org.eclipse.jgit.pgm.internal;
 
 import java.text.MessageFormat;
 
@@ -76,7 +76,6 @@ public class CLIText extends TranslationBundle {
 	// @formatter:off
 	/***/ public String alreadyOnBranch;
 	/***/ public String alreadyUpToDate;
-	/***/ public String archiveEntryModeIgnored;
 	/***/ public String authorInfo;
 	/***/ public String averageMSPerRead;
 	/***/ public String branchAlreadyExists;
@@ -103,7 +102,6 @@ public class CLIText extends TranslationBundle {
 	/***/ public String cannotResolve;
 	/***/ public String cannotSetupConsole;
 	/***/ public String cannotUseObjectsWithGlog;
-	/***/ public String cannotWrite;
 	/***/ public String cantFindGitDirectory;
 	/***/ public String cantWrite;
 	/***/ public String changesNotStagedForCommit;
@@ -117,6 +115,7 @@ public class CLIText extends TranslationBundle {
 	/***/ public String doesNotExist;
 	/***/ public String dontOverwriteLocalChanges;
 	/***/ public String everythingUpToDate;
+	/***/ public String exceptionCaughtDuringExecutionOfArchiveCommand;
 	/***/ public String expectedNumberOfbytes;
 	/***/ public String exporting;
 	/***/ public String failedToCommitIndex;
@@ -133,10 +132,13 @@ public class CLIText extends TranslationBundle {
 	/***/ public String jgitVersion;
 	/***/ public String lineFormat;
 	/***/ public String listeningOn;
+	/***/ public String mergeCheckoutConflict;
 	/***/ public String mergeConflict;
 	/***/ public String mergeFailed;
+	/***/ public String mergeCheckoutFailed;
 	/***/ public String mergeMadeBy;
 	/***/ public String mergedSquashed;
+	/***/ public String mergeWentWellStoppedBeforeCommitting;
 	/***/ public String metaVar_KEY;
 	/***/ public String metaVar_archiveFormat;
 	/***/ public String metaVar_arg;
@@ -180,6 +182,7 @@ public class CLIText extends TranslationBundle {
 	/***/ public String needApprovalToDestroyCurrentRepository;
 	/***/ public String noGitRepositoryConfigured;
 	/***/ public String noSuchFile;
+	/***/ public String noSuchRemoteRef;
 	/***/ public String noTREESectionInIndex;
 	/***/ public String nonFastForward;
 	/***/ public String notABranch;
@@ -187,7 +190,6 @@ public class CLIText extends TranslationBundle {
 	/***/ public String notAGitRepository;
 	/***/ public String notAJgitCommand;
 	/***/ public String notARevision;
-	/***/ public String notATagVersionIsRequired;
 	/***/ public String notATree;
 	/***/ public String notAValidRefName;
 	/***/ public String notAnIndexFile;
@@ -212,9 +214,17 @@ public class CLIText extends TranslationBundle {
 	/***/ public String skippingObject;
 	/***/ public String statusFileListFormat;
 	/***/ public String statusFileListFormatWithPrefix;
+	/***/ public String statusFileListFormatUnmerged;
 	/***/ public String statusModified;
 	/***/ public String statusNewFile;
 	/***/ public String statusRemoved;
+	/***/ public String statusBothDeleted;
+	/***/ public String statusAddedByUs;
+	/***/ public String statusDeletedByThem;
+	/***/ public String statusAddedByThem;
+	/***/ public String statusDeletedByUs;
+	/***/ public String statusBothAdded;
+	/***/ public String statusBothModified;
 	/***/ public String switchedToNewBranch;
 	/***/ public String switchedToBranch;
 	/***/ public String tagAlreadyExists;
@@ -229,5 +239,4 @@ public class CLIText extends TranslationBundle {
 	/***/ public String unsupportedOperation;
 	/***/ public String untrackedFiles;
 	/***/ public String updating;
-	/***/ public String warningNoCommitGivenOnCommandLine;
 }
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 ca97104..087dbb0 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
@@ -60,7 +60,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
index cea3209..c36cc2c 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
@@ -56,8 +56,8 @@ import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Setter;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
index f6550a5..fa24d4b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
@@ -54,7 +54,7 @@ import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Parameters;
 import org.kohsuke.args4j.spi.Setter;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 /**
  * Custom argument handler {@link ObjectId} from string values.
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
index 50ff02a..122cce7 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
@@ -53,7 +53,7 @@ import org.kohsuke.args4j.OptionDef;
 import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Parameters;
 import org.kohsuke.args4j.spi.Setter;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RefSpecHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RefSpecHandler.java
index 43b727a..dae0c47 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RefSpecHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RefSpecHandler.java
@@ -49,7 +49,7 @@ import org.kohsuke.args4j.OptionDef;
 import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Parameters;
 import org.kohsuke.args4j.spi.Setter;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.transport.RefSpec;
 
 /**
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
index 4d917dc..b1be128 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
@@ -56,7 +56,7 @@ import org.kohsuke.args4j.spi.Setter;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
index 0b607ee..eb155af 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
@@ -56,7 +56,7 @@ import org.kohsuke.args4j.spi.Setter;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.revwalk.RevTree;
 
 /**
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java
index 35ed22b..c62ef0d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java
@@ -51,10 +51,10 @@ import org.kohsuke.args4j.OptionDef;
 import org.kohsuke.args4j.spi.OptionHandler;
 import org.kohsuke.args4j.spi.Parameters;
 import org.kohsuke.args4j.spi.Setter;
-import org.eclipse.jgit.pgm.CLIText;
 import org.eclipse.jgit.pgm.CommandCatalog;
 import org.eclipse.jgit.pgm.CommandRef;
 import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
 
 /**
  * Custom Argument handler for jgit command selection.
diff --git a/org.eclipse.jgit.test/.project b/org.eclipse.jgit.test/.project
index a7b1989..a7ac51e 100644
--- a/org.eclipse.jgit.test/.project
+++ b/org.eclipse.jgit.test/.project
@@ -3,6 +3,7 @@
 	<name>org.eclipse.jgit.test</name>
 	<comment></comment>
 	<projects>
+		<project>org.eclipse.jgit.java7</project>
 	</projects>
 	<buildSpec>
 		<buildCommand>
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 792ab3e..4b3aa66 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -2,46 +2,48 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.eclipse.jgit.api;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.api.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.awtui;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.blame;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.console;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.diff;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.dircache;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.events;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.fnmatch;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.ignore;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.internal;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.junit;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.merge;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.nls;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.notes;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.patch;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.pgm;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revplot;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk.filter;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.file;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.storage.pack;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.submodule;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.treewalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.treewalk.filter;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util.io;version="[2.3.1,2.4.0)",
+Import-Package: javaewah;version="0.5.6",
+ org.eclipse.jgit.api;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.api.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.awtui;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.blame;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.console;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.diff;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.dircache;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.events;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.fnmatch;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.ignore;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.java7;version="[3.0.0,3.1.0)";resolution:=optional,
+ org.eclipse.jgit.junit;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.merge;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.nls;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.notes;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.patch;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.pgm;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.pgm.internal;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revplot;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk.filter;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.file;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.storage.pack;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.submodule;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.treewalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.treewalk.filter;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util.io;version="[3.0.0,3.1.0)",
  org.hamcrest;version="[1.1.0,2.0.0)",
- org.hamcrest.text.pattern;version="[1.1.0,2.0.0)",
  org.junit;version="[4.4.0,5.0.0)",
  org.junit.experimental.theories;version="[4.4.0,5.0.0)",
  org.junit.runner;version="[4.4.0,5.0.0)"
-Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)",
- org.hamcrest.core;bundle-version="[1.1.0,2.0.0)"
-Export-Package: org.eclipse.jgit.lib;version=2.3.1
+Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
new file mode 100644
index 0000000..af009c0
--- /dev/null
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch	
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.jgit.test/tst"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="2"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.jgit.test/tst"/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/>
"/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.test"/>
</runtimeClasspathEntry>
"/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.test"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256m"/>
+</launchConfiguration>
diff --git a/org.eclipse.jgit.test/plugin.properties b/org.eclipse.jgit.test/plugin.properties
index ea60411..1d32644 100644
--- a/org.eclipse.jgit.test/plugin.properties
+++ b/org.eclipse.jgit.test/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit Core Tests
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 67c250e..f3f5547 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
@@ -62,6 +62,22 @@
     JUnit tests for the core library.
   </description>
 
+  <profiles>
+    <profile>
+      <id>jgit.java7</id>
+      <activation>
+	<jdk>1.7</jdk>
+      </activation>
+      <dependencies>
+	<dependency>
+	  <groupId>org.eclipse.jgit</groupId>
+	  <artifactId>org.eclipse.jgit.java7</artifactId>
+	  <version>${project.version}</version>
+	</dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
index fb98c2f..4087fb0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
@@ -44,7 +44,7 @@
 package org.eclipse.jgit.api;
 
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
index 0130bdf..f66661a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
@@ -60,10 +60,10 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.junit.Test;
 
 /**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 99e05d4..31f909a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2011, 2013 Chris Aniszczyk <caniszczyk at gmail.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -50,6 +50,7 @@ import static org.junit.Assert.fail;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -63,7 +64,6 @@ import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -71,6 +71,8 @@ import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.submodule.SubmoduleStatus;
 import org.eclipse.jgit.submodule.SubmoduleStatusType;
 import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.util.SystemReader;
 import org.junit.Test;
 
@@ -89,11 +91,10 @@ public class CloneCommandTest extends RepositoryTestCase {
 		writeTrashFile("Test.txt", "Hello world");
 		git.add().addFilepattern("Test.txt").call();
 		git.commit().setMessage("Initial commit").call();
+		git.tag().setName("tag-initial").setMessage("Tag initial").call();
 
-		// create a master branch and switch to it
-		git.branchCreate().setName("test").call();
-		RefUpdate rup = db.updateRef(Constants.HEAD);
-		rup.link("refs/heads/test");
+		// create a test branch and switch to it
+		git.checkout().setCreateBranch(true).setName("test").call();
 
 		// commit something on the test branch
 		writeTrashFile("Test.txt", "Some change");
@@ -105,7 +106,7 @@ public class CloneCommandTest extends RepositoryTestCase {
 
 	@Test
 	public void testCloneRepository() throws IOException,
-			JGitInternalException, GitAPIException {
+			JGitInternalException, GitAPIException, URISyntaxException {
 		File directory = createTempDirectory("testCloneRepository");
 		CloneCommand command = Git.cloneRepository();
 		command.setDirectory(directory);
@@ -130,6 +131,28 @@ public class CloneCommandTest extends RepositoryTestCase {
 								"test", ConfigConstants.CONFIG_KEY_MERGE));
 		assertEquals(2, git2.branchList().setListMode(ListMode.REMOTE).call()
 				.size());
+		assertEquals(new RefSpec("+refs/heads/*:refs/remotes/origin/*"),
+				fetchRefSpec(git2.getRepository()));
+	}
+
+	@Test
+	public void testBareCloneRepository() throws IOException,
+			JGitInternalException, GitAPIException, URISyntaxException {
+		File directory = createTempDirectory("testCloneRepository_bare");
+		CloneCommand command = Git.cloneRepository();
+		command.setBare(true);
+		command.setDirectory(directory);
+		command.setURI("file://" + git.getRepository().getWorkTree().getPath());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+		assertEquals(new RefSpec("+refs/heads/*:refs/heads/*"),
+				fetchRefSpec(git2.getRepository()));
+	}
+
+	public static RefSpec fetchRefSpec(Repository r) throws URISyntaxException {
+		RemoteConfig remoteConfig =
+				new RemoteConfig(r.getConfig(), Constants.DEFAULT_REMOTE_NAME);
+		return remoteConfig.getFetchRefSpecs().get(0);
 	}
 
 	@Test
@@ -181,6 +204,36 @@ public class CloneCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testCloneRepositoryWithBranchShortName() throws Exception {
+		File directory = createTempDirectory("testCloneRepositoryWithBranch");
+		CloneCommand command = Git.cloneRepository();
+		command.setBranch("test");
+		command.setDirectory(directory);
+		command.setURI("file://" + git.getRepository().getWorkTree().getPath());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		assertNotNull(git2);
+		assertEquals("refs/heads/test", git2.getRepository().getFullBranch());
+	}
+
+	@Test
+	public void testCloneRepositoryWithTagName() throws Exception {
+		File directory = createTempDirectory("testCloneRepositoryWithBranch");
+		CloneCommand command = Git.cloneRepository();
+		command.setBranch("tag-initial");
+		command.setDirectory(directory);
+		command.setURI("file://" + git.getRepository().getWorkTree().getPath());
+		Git git2 = command.call();
+		addRepoToClose(git2.getRepository());
+
+		assertNotNull(git2);
+		ObjectId taggedCommit = db.resolve("tag-initial^{commit}");
+		assertEquals(taggedCommit.name(), git2
+				.getRepository().getFullBranch());
+	}
+
+	@Test
 	public void testCloneRepositoryOnlyOneBranch() throws IOException,
 			JGitInternalException, GitAPIException {
 		File directory = createTempDirectory("testCloneRepositoryWithBranch");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
index 07e3c48..79d8f60 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
@@ -61,8 +61,8 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
index abfbd15..56a1f38 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk at gmail.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,55 +43,154 @@
 package org.eclipse.jgit.api;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.util.Collection;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchResult;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.TagOpt;
+import org.eclipse.jgit.transport.TrackingRefUpdate;
 import org.eclipse.jgit.transport.URIish;
+import org.junit.Before;
 import org.junit.Test;
 
 public class FetchCommandTest extends RepositoryTestCase {
 
-	@Test
-	public void testFetch() throws JGitInternalException, IOException,
-			GitAPIException, URISyntaxException {
+	private Git git;
+	private Git remoteGit;
+
+	@Before
+	public void setupRemoteRepository() throws IOException, URISyntaxException {
+		git = new Git(db);
 
 		// create other repository
-		Repository db2 = createWorkRepository();
-		Git git2 = new Git(db2);
+		Repository remoteRepository = createWorkRepository();
+		remoteGit = new Git(remoteRepository);
 
 		// setup the first repository to fetch from the second repository
 		final StoredConfig config = db.getConfig();
 		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
-		URIish uri = new URIish(db2.getDirectory().toURI().toURL());
+		URIish uri = new URIish(remoteRepository.getDirectory().toURI().toURL());
 		remoteConfig.addURI(uri);
 		remoteConfig.update(config);
 		config.save();
+	}
 
-		// create some refs via commits and tag
-		RevCommit commit = git2.commit().setMessage("initial commit").call();
-		Ref tagRef = git2.tag().setName("tag").call();
+	@Test
+	public void testFetch() throws JGitInternalException, IOException,
+			GitAPIException {
 
-		Git git1 = new Git(db);
+		// create some refs via commits and tag
+		RevCommit commit = remoteGit.commit().setMessage("initial commit").call();
+		Ref tagRef = remoteGit.tag().setName("tag").call();
 
 		RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
-		git1.fetch().setRemote("test").setRefSpecs(spec)
+		git.fetch().setRemote("test").setRefSpecs(spec)
 				.call();
 
 		assertEquals(commit.getId(),
 				db.resolve(commit.getId().getName() + "^{commit}"));
 		assertEquals(tagRef.getObjectId(),
 				db.resolve(tagRef.getObjectId().getName()));
+	}
+
+	@Test
+	public void fetchShouldAutoFollowTag() throws Exception {
+		remoteGit.commit().setMessage("commit").call();
+		Ref tagRef = remoteGit.tag().setName("foo").call();
+
+		RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
+		git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
+
+		assertEquals(tagRef.getObjectId(), db.resolve("foo"));
+	}
+
+	@Test
+	public void fetchShouldAutoFollowTagForFetchedObjects() throws Exception {
+		remoteGit.commit().setMessage("commit").call();
+		Ref tagRef = remoteGit.tag().setName("foo").call();
+		remoteGit.commit().setMessage("commit2").call();
+		RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
+		git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
+		assertEquals(tagRef.getObjectId(), db.resolve("foo"));
+	}
+
+	@Test
+	public void fetchShouldNotFetchTagsFromOtherBranches() throws Exception {
+		remoteGit.commit().setMessage("commit").call();
+		remoteGit.checkout().setName("other").setCreateBranch(true).call();
+		remoteGit.commit().setMessage("commit2").call();
+		remoteGit.tag().setName("foo").call();
+		RefSpec spec = new RefSpec(
+				"refs/heads/master:refs/remotes/origin/master");
+		git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
+		assertNull(db.resolve("foo"));
+	}
+
+	@Test
+	public void fetchWithUpdatedTagShouldNotTryToUpdateLocal() throws Exception {
+		final String tagName = "foo";
+		remoteGit.commit().setMessage("commit").call();
+		Ref tagRef = remoteGit.tag().setName(tagName).call();
+		ObjectId originalId = tagRef.getObjectId();
+
+		RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
+		git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
+		assertEquals(originalId, db.resolve(tagName));
+
+		remoteGit.commit().setMessage("commit 2").call();
+		remoteGit.tag().setName(tagName).setForceUpdate(true).call();
 
+		FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
+
+		Collection<TrackingRefUpdate> refUpdates = result
+				.getTrackingRefUpdates();
+		assertEquals(1, refUpdates.size());
+		TrackingRefUpdate update = refUpdates.iterator().next();
+		assertEquals("refs/heads/master", update.getRemoteName());
+
+		assertEquals(originalId, db.resolve(tagName));
 	}
 
+	@Test
+	public void fetchWithExplicitTagsShouldUpdateLocal() throws Exception {
+		final String tagName = "foo";
+		remoteGit.commit().setMessage("commit").call();
+		Ref tagRef1 = remoteGit.tag().setName(tagName).call();
+
+		RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
+		git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.AUTO_FOLLOW).call();
+		assertEquals(tagRef1.getObjectId(), db.resolve(tagName));
+
+		remoteGit.commit().setMessage("commit 2").call();
+		Ref tagRef2 = remoteGit.tag().setName(tagName).setForceUpdate(true)
+				.call();
+
+		FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
+				.setTagOpt(TagOpt.FETCH_TAGS).call();
+		TrackingRefUpdate update = result.getTrackingRefUpdate(Constants.R_TAGS
+				+ tagName);
+		assertEquals(RefUpdate.Result.FORCED, update.getResult());
+		assertEquals(tagRef2.getObjectId(), db.resolve(tagName));
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
index 1f53645..34432c5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
@@ -51,6 +51,7 @@ import java.util.Iterator;
 import java.util.List;
 
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Test;
@@ -89,6 +90,36 @@ public class LogCommandTest extends RepositoryTestCase {
 		assertFalse(log.hasNext());
 	}
 
+    @Test
+    public void logAllCommitsWithTag() throws Exception {
+		List<RevCommit> commits = new ArrayList<RevCommit>();
+		Git git = Git.wrap(db);
+
+		writeTrashFile("Test.txt", "Hello world");
+		git.add().addFilepattern("Test.txt").call();
+		commits.add(git.commit().setMessage("initial commit").call());
+
+		TagCommand tagCmd = git.tag();
+		tagCmd.setName("tagcommit");
+		tagCmd.setObjectId(commits.get(0));
+		tagCmd.setTagger(new PersonIdent(db));
+		Ref tag = tagCmd.call();
+
+		tagCmd = git.tag();
+		tagCmd.setName("tagtree");
+		tagCmd.setObjectId(commits.get(0).getTree());
+		tagCmd.setTagger(new PersonIdent(db));
+		tagCmd.call();
+
+		Iterator<RevCommit> log = git.log().all().call().iterator();
+		assertTrue(log.hasNext());
+		RevCommit commit = log.next();
+		tag = db.peel(tag);
+
+		assertEquals(commit.getName(), tag.getPeeledObjectId().getName());
+		assertTrue(commits.contains(commit));
+	}
+
 	private List<RevCommit> createCommits(Git git) throws Exception {
 		List<RevCommit> commits = new ArrayList<RevCommit>();
 		writeTrashFile("Test.txt", "Hello world");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index dfd5c54..67e1879 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -96,10 +96,10 @@ public class MergeCommandTest extends RepositoryTestCase {
 		MergeResult result = git.merge().include(db.getRef(Constants.HEAD)).call();
 		assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
 		// no reflog entry written by merge
-		assertEquals("commit: initial commit",
+		assertEquals("commit (initial): initial commit",
 				db
 				.getReflogReader(Constants.HEAD).getLastEntry().getComment());
-		assertEquals("commit: initial commit",
+		assertEquals("commit (initial): initial commit",
 				db
 				.getReflogReader(db.getBranch()).getLastEntry().getComment());
 	}
@@ -142,6 +142,28 @@ public class MergeCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testFastForwardNoCommit() throws Exception {
+		Git git = new Git(db);
+		RevCommit first = git.commit().setMessage("initial commit").call();
+		createBranch(first, "refs/heads/branch1");
+
+		RevCommit second = git.commit().setMessage("second commit").call();
+
+		checkoutBranch("refs/heads/branch1");
+
+		MergeResult result = git.merge().include(db.getRef(Constants.MASTER))
+				.setCommit(false).call();
+
+		assertEquals(MergeResult.MergeStatus.FAST_FORWARD,
+				result.getMergeStatus());
+		assertEquals(second, result.getNewHead());
+		assertEquals("merge refs/heads/master: Fast-forward", db
+				.getReflogReader(Constants.HEAD).getLastEntry().getComment());
+		assertEquals("merge refs/heads/master: Fast-forward", db
+				.getReflogReader(db.getBranch()).getLastEntry().getComment());
+	}
+
+	@Test
 	public void testFastForwardWithFiles() throws Exception {
 		Git git = new Git(db);
 
@@ -234,6 +256,31 @@ public class MergeCommandTest extends RepositoryTestCase {
 				db.getReflogReader(db.getBranch()).getLastEntry().getComment());
 	}
 
+	@Theory
+	public void testMergeSuccessAllStrategiesNoCommit(
+			MergeStrategy mergeStrategy) throws Exception {
+		Git git = new Git(db);
+
+		RevCommit first = git.commit().setMessage("first").call();
+		createBranch(first, "refs/heads/side");
+
+		writeTrashFile("a", "a");
+		git.add().addFilepattern("a").call();
+		git.commit().setMessage("second").call();
+
+		checkoutBranch("refs/heads/side");
+		writeTrashFile("b", "b");
+		git.add().addFilepattern("b").call();
+		RevCommit thirdCommit = git.commit().setMessage("third").call();
+
+		MergeResult result = git.merge().setStrategy(mergeStrategy)
+				.setCommit(false)
+				.include(db.getRef(Constants.MASTER)).call();
+		assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
+		assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(),
+				thirdCommit.getId());
+	}
+
 	@Test
 	public void testContentMerge() throws Exception {
 		Git git = new Git(db);
@@ -280,6 +327,33 @@ public class MergeCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testMergeTag() throws Exception {
+		Git git = new Git(db);
+
+		writeTrashFile("a", "a");
+		git.add().addFilepattern("a").call();
+		RevCommit initialCommit = git.commit().setMessage("initial").call();
+
+		createBranch(initialCommit, "refs/heads/side");
+		checkoutBranch("refs/heads/side");
+
+		writeTrashFile("b", "b");
+		git.add().addFilepattern("b").call();
+		RevCommit secondCommit = git.commit().setMessage("side").call();
+		Ref tag = git.tag().setAnnotated(true).setMessage("my tag 01")
+				.setName("tag01").setObjectId(secondCommit).call();
+
+		checkoutBranch("refs/heads/master");
+
+		writeTrashFile("a", "a2");
+		git.add().addFilepattern("a").call();
+		git.commit().setMessage("main").call();
+
+		MergeResult result = git.merge().include(tag).setStrategy(MergeStrategy.RESOLVE).call();
+		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+	}
+
+	@Test
 	public void testMergeMessage() throws Exception {
 		Git git = new Git(db);
 
@@ -481,6 +555,56 @@ public class MergeCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testSuccessfulContentMergeNoCommit() throws Exception {
+		Git git = new Git(db);
+
+		writeTrashFile("a", "1\na\n3\n");
+		writeTrashFile("b", "1\nb\n3\n");
+		writeTrashFile("c/c/c", "1\nc\n3\n");
+		git.add().addFilepattern("a").addFilepattern("b")
+				.addFilepattern("c/c/c").call();
+		RevCommit initialCommit = git.commit().setMessage("initial").call();
+
+		createBranch(initialCommit, "refs/heads/side");
+		checkoutBranch("refs/heads/side");
+
+		writeTrashFile("a", "1(side)\na\n3\n");
+		writeTrashFile("b", "1\nb(side)\n3\n");
+		git.add().addFilepattern("a").addFilepattern("b").call();
+		RevCommit secondCommit = git.commit().setMessage("side").call();
+
+		assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b")));
+		checkoutBranch("refs/heads/master");
+		assertEquals("1\nb\n3\n", read(new File(db.getWorkTree(), "b")));
+
+		writeTrashFile("a", "1\na\n3(main)\n");
+		writeTrashFile("c/c/c", "1\nc(main)\n3\n");
+		git.add().addFilepattern("a").addFilepattern("c/c/c").call();
+		RevCommit thirdCommit = git.commit().setMessage("main").call();
+
+		MergeResult result = git.merge().include(secondCommit.getId())
+				.setCommit(false)
+				.setStrategy(MergeStrategy.RESOLVE).call();
+		assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
+		assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(),
+				thirdCommit.getId());
+
+		assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(),
+				"a")));
+		assertEquals("1\nb(side)\n3\n", read(new File(db.getWorkTree(), "b")));
+		assertEquals("1\nc(main)\n3\n",
+				read(new File(db.getWorkTree(), "c/c/c")));
+
+		assertEquals(null, result.getConflicts());
+
+		assertEquals(2, result.getMergedCommits().length);
+		assertEquals(thirdCommit, result.getMergedCommits()[0]);
+		assertEquals(secondCommit, result.getMergedCommits()[1]);
+		assertNull(result.getNewHead());
+		assertEquals(RepositoryState.MERGING_RESOLVED, db.getRepositoryState());
+	}
+
+	@Test
 	public void testSuccessfulContentMergeAndDirtyworkingTree()
 			throws Exception {
 		Git git = new Git(db);
@@ -1345,6 +1469,7 @@ public class MergeCommandTest extends RepositoryTestCase {
 
 		assertEquals(MergeStatus.FAST_FORWARD, result.getMergeStatus());
 	}
+
 	@Test
 	public void testNoFastForward() throws Exception {
 		Git git = new Git(db);
@@ -1363,6 +1488,33 @@ public class MergeCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testNoFastForwardNoCommit() throws Exception {
+		// given
+		Git git = new Git(db);
+		RevCommit initialCommit = git.commit().setMessage("initial commit")
+				.call();
+		createBranch(initialCommit, "refs/heads/branch1");
+		RevCommit secondCommit = git.commit().setMessage("second commit")
+				.call();
+		checkoutBranch("refs/heads/branch1");
+
+		// when
+		MergeCommand merge = git.merge();
+		merge.setFastForward(FastForwardMode.NO_FF);
+		merge.include(db.getRef(Constants.MASTER));
+		merge.setCommit(false);
+		MergeResult result = merge.call();
+
+		// then
+		assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
+		assertEquals(2, result.getMergedCommits().length);
+		assertEquals(initialCommit, result.getMergedCommits()[0]);
+		assertEquals(secondCommit, result.getMergedCommits()[1]);
+		assertNull(result.getNewHead());
+		assertEquals(RepositoryState.MERGING_RESOLVED, db.getRepositoryState());
+	}
+
+	@Test
 	public void testFastForwardOnlyNotPossible() throws Exception {
 		Git git = new Git(db);
 		RevCommit initialCommit = git.commit().setMessage("initial commit")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
new file mode 100644
index 0000000..b92a636
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NameRevCommandTest extends RepositoryTestCase {
+	private TestRepository<Repository> tr;
+	private Git git;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		tr = new TestRepository<Repository>(db);
+		git = new Git(db);
+	}
+
+	@Test
+	public void nameExact() throws Exception {
+		RevCommit c = tr.commit().create();
+		tr.update("master", c);
+		assertOneResult("master", c);
+	}
+
+	@Test
+	public void prefix() throws Exception {
+		RevCommit c = tr.commit().create();
+		tr.update("refs/heads/master", c);
+		tr.update("refs/tags/tag", c);
+		assertOneResult("master", c);
+		assertOneResult("master",
+				git.nameRev().addPrefix("refs/heads/").addPrefix("refs/tags/"),
+				c);
+		assertOneResult("tag",
+				git.nameRev().addPrefix("refs/tags/").addPrefix("refs/heads/"),
+				c);
+	}
+
+	@Test
+	public void ref() throws Exception {
+		RevCommit c = tr.commit().create();
+		tr.update("refs/heads/master", c);
+		tr.update("refs/tags/tag", c);
+		assertOneResult("master",
+				git.nameRev().addRef(db.getRef("refs/heads/master")), c);
+		assertOneResult("tag",
+				git.nameRev().addRef(db.getRef("refs/tags/tag")), c);
+	}
+
+	@Test
+	public void annotatedTags() throws Exception {
+		RevCommit c = tr.commit().create();
+		tr.update("refs/heads/master", c);
+		tr.update("refs/tags/tag1", c);
+		tr.update("refs/tags/tag2", tr.tag("tag2", c));
+		assertOneResult("tag2", git.nameRev().addAnnotatedTags(), c);
+	}
+
+	@Test
+	public void simpleAncestor() throws Exception {
+		// 0--1--2
+		RevCommit c0 = tr.commit().create();
+		RevCommit c1 = tr.commit().parent(c0).create();
+		RevCommit c2 = tr.commit().parent(c1).create();
+		tr.update("master", c2);
+		Map<ObjectId, String> result = git.nameRev().add(c0).add(c1).add(c2).call();
+		assertEquals(3, result.size());
+		assertEquals("master~2", result.get(c0));
+		assertEquals("master~1", result.get(c1));
+		assertEquals("master", result.get(c2));
+	}
+
+	@Test
+	public void multiplePathsNoMerge() throws Exception {
+		// 0--1    <- master
+		//  \-2--3 <- branch
+		RevCommit c0 = tr.commit().create();
+		RevCommit c1 = tr.commit().parent(c0).create();
+		RevCommit c2 = tr.commit().parent(c0).create();
+		RevCommit c3 = tr.commit().parent(c2).create();
+		tr.update("master", c1);
+		tr.update("branch", c3);
+		assertOneResult("master~1", c0);
+	}
+
+	@Test
+	public void onePathMerge() throws Exception {
+		// 0--1--3
+		//  \-2-/
+		RevCommit c0 = tr.commit().create();
+		RevCommit c1 = tr.commit().parent(c0).create();
+		RevCommit c2 = tr.commit().parent(c0).create();
+		RevCommit c3 = tr.commit().parent(c1).parent(c2).create();
+		tr.update("master", c3);
+		assertOneResult("master~2", c0);
+	}
+
+	@Test
+	public void onePathMergeSecondParent() throws Exception {
+		// 0--1-----4
+		//  \-2--3-/
+		RevCommit c0 = tr.commit().create();
+		RevCommit c1 = tr.commit().parent(c0).create();
+		RevCommit c2 = tr.commit().parent(c0).create();
+		RevCommit c3 = tr.commit().parent(c2).create();
+		RevCommit c4 = tr.commit().parent(c1).parent(c3).create();
+		tr.update("master", c4);
+		assertOneResult("master^2", c3);
+		assertOneResult("master^2~1", c2);
+	}
+
+	@Test
+	public void onePathMergeLongerFirstParentPath() throws Exception {
+		// 0--1--2--4
+		//  \--3---/
+		RevCommit c0 = tr.commit().create();
+		RevCommit c1 = tr.commit().parent(c0).create();
+		RevCommit c2 = tr.commit().parent(c1).create();
+		RevCommit c3 = tr.commit().parent(c0).create();
+		RevCommit c4 = tr.commit().parent(c2).parent(c3).create();
+		tr.update("master", c4);
+		assertOneResult("master^2", c3);
+		assertOneResult("master~3", c0);
+	}
+
+	@Test
+	public void multiplePathsSecondParent() throws Exception {
+		// 0--...--2
+		//  \--1--/
+		RevCommit c0 = tr.commit().create();
+		RevCommit c1 = tr.commit().parent(c0).create();
+		RevCommit c = c0;
+		int mergeCost = 5;
+		for (int i = 0; i < mergeCost; i++) {
+			c = tr.commit().parent(c).create();
+		}
+		RevCommit c2 = tr.commit().parent(c).parent(c1).create();
+		tr.update("master", c2);
+		assertOneResult("master^2~1", git.nameRev().setMergeCost(mergeCost), c0);
+	}
+
+	private static void assertOneResult(String expected, NameRevCommand nameRev,
+			ObjectId id) throws Exception {
+		Map<ObjectId, String> result = nameRev.add(id).call();
+		assertEquals(1, result.size());
+		assertEquals(expected, result.get(id));
+	}
+
+	private void assertOneResult(String expected, ObjectId id) throws Exception {
+		assertOneResult(expected, git.nameRev(), id);
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index 7717697..8d22a1b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -65,7 +65,6 @@ import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.transport.URIish;
@@ -74,7 +73,7 @@ import org.junit.Test;
 
 public class PullCommandTest extends RepositoryTestCase {
 	/** Second Test repository */
-	protected FileRepository dbTarget;
+	protected Repository dbTarget;
 
 	private Git source;
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java
index e87eb11..993e16f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java
@@ -67,7 +67,6 @@ import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.merge.MergeStrategy;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.transport.URIish;
@@ -76,7 +75,7 @@ import org.junit.Test;
 
 public class PullCommandWithRebaseTest extends RepositoryTestCase {
 	/** Second Test repository */
-	protected FileRepository dbTarget;
+	protected Repository dbTarget;
 
 	private Git source;
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index e08fa54..9aa13ca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler at sap.com>
+ * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -42,6 +42,9 @@
  */
 package org.eclipse.jgit.api;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -62,7 +65,6 @@ import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
 import org.eclipse.jgit.api.RebaseCommand.Operation;
 import org.eclipse.jgit.api.RebaseCommand.Step;
 import org.eclipse.jgit.api.RebaseResult.Status;
-import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.api.errors.RefNotFoundException;
 import org.eclipse.jgit.api.errors.UnmergedPathsException;
 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
@@ -77,6 +79,7 @@ import org.eclipse.jgit.merge.MergeStrategy;
 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.FileUtils;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -161,72 +164,89 @@ public class RebaseCommandTest extends RepositoryTestCase {
 		assertEquals(Status.FAST_FORWARD, res.getStatus());
 	}
 
+	/**
+	 * Create the following commits and then attempt to rebase topic onto
+	 * master. This will serialize the branches.
+	 *
+	 * <pre>
+	 * A - B (master)
+	 *   \
+	 *    C - D - F (topic)
+	 *     \      /
+	 *      E  -  (side)
+	 * </pre>
+	 *
+	 * into
+	 *
+	 * <pre>
+	 * A - B - (master)  C' - D' - E' (topic')
+	 *   \
+	 *    C - D - F (topic)
+	 *     \      /
+	 *      E  -  (side)
+	 * </pre>
+	 *
+	 * @throws Exception
+	 */
 	@Test
-	public void testRebaseFailsCantCherryPickMergeCommits()
+	public void testRebaseShouldIgnoreMergeCommits()
 			throws Exception {
-		/**
-		 * Create the following commits and then attempt to rebase topic onto
-		 * master. This will fail as the cherry-pick list C, D, E an F contains
-		 * a merge commit (F).
-		 *
-		 * <pre>
-		 * A - B (master)
-		 *   \
-		 *    C - D - F (topic)
-		 *     \      /
-		 *      E  -  (side)
-		 * </pre>
-		 */
 		// create file1 on master
 		writeTrashFile(FILE1, FILE1);
 		git.add().addFilepattern(FILE1).call();
-		RevCommit first = git.commit().setMessage("Add file1").call();
+		RevCommit a = git.commit().setMessage("Add file1").call();
 		assertTrue(new File(db.getWorkTree(), FILE1).exists());
 
 		// create a topic branch
-		createBranch(first, "refs/heads/topic");
+		createBranch(a, "refs/heads/topic");
 
 		// update FILE1 on master
 		writeTrashFile(FILE1, "blah");
 		git.add().addFilepattern(FILE1).call();
-		git.commit().setMessage("updated file1 on master").call();
+		RevCommit b = git.commit().setMessage("updated file1 on master").call();
 
 		checkoutBranch("refs/heads/topic");
 		writeTrashFile("file3", "more changess");
 		git.add().addFilepattern("file3").call();
-		RevCommit topicCommit = git.commit()
+		RevCommit c = git.commit()
 				.setMessage("update file3 on topic").call();
 
 		// create a branch from the topic commit
-		createBranch(topicCommit, "refs/heads/side");
+		createBranch(c, "refs/heads/side");
 
 		// second commit on topic
 		writeTrashFile("file2", "file2");
 		git.add().addFilepattern("file2").call();
-		git.commit().setMessage("Add file2").call();
+		RevCommit d = git.commit().setMessage("Add file2").call();
 		assertTrue(new File(db.getWorkTree(), "file2").exists());
 
 		// switch to side branch and update file2
 		checkoutBranch("refs/heads/side");
 		writeTrashFile("file3", "more change");
 		git.add().addFilepattern("file3").call();
-		RevCommit sideCommit = git.commit().setMessage("update file2 on side")
+		RevCommit e = git.commit().setMessage("update file2 on side")
 				.call();
 
-		// switch back to topic and merge in side
+		// switch back to topic and merge in side, creating f
 		checkoutBranch("refs/heads/topic");
-		MergeResult result = git.merge().include(sideCommit.getId())
+		MergeResult result = git.merge().include(e.getId())
 				.setStrategy(MergeStrategy.RESOLVE).call();
 		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+		RebaseResult res = git.rebase().setUpstream("refs/heads/master").call();
+		assertEquals(Status.OK, res.getStatus());
 
-		try {
-			RebaseResult rebase = git.rebase().setUpstream("refs/heads/master")
-					.call();
-			fail("MultipleParentsNotAllowedException expected: "
-					+ rebase.getStatus());
-		} catch (JGitInternalException e) {
-			// expected
-		}
+		RevWalk rw = new RevWalk(db);
+		rw.markStart(rw.parseCommit(db.resolve("refs/heads/topic")));
+		assertDerivedFrom(rw.next(), e);
+		assertDerivedFrom(rw.next(), d);
+		assertDerivedFrom(rw.next(), c);
+		assertEquals(b, rw.next());
+		assertEquals(a, rw.next());
+	}
+
+	static void assertDerivedFrom(RevCommit derived, RevCommit original) {
+		assertThat(derived, not(equalTo(original)));
+		assertEquals(original.getFullMessage(), derived.getFullMessage());
 	}
 
 	@Test
@@ -1488,6 +1508,27 @@ public class RebaseCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testAbortShouldAlsoAbortNonInteractiveRebaseWithRebaseApplyDir()
+			throws Exception {
+		writeTrashFile(FILE1, "initial file");
+		git.add().addFilepattern(FILE1).call();
+		git.commit().setMessage("initial commit").call();
+
+		File applyDir = new File(db.getDirectory(), "rebase-apply");
+		File headName = new File(applyDir, "head-name");
+		FileUtils.mkdir(applyDir);
+		write(headName, "master");
+		db.writeOrigHead(db.resolve(Constants.HEAD));
+
+		git.rebase().setOperation(Operation.ABORT).call();
+
+		assertFalse("Abort should clean up .git/rebase-apply",
+				applyDir.exists());
+		assertEquals(RepositoryState.SAFE, git.getRepository()
+				.getRepositoryState());
+	}
+
+	@Test
 	public void testRebaseShouldBeAbleToHandleEmptyLinesInRebaseTodoFile()
 			throws IOException {
 		String emptyLine = "\n";
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ReflogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ReflogCommandTest.java
index a43e036..b07c703 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ReflogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ReflogCommandTest.java
@@ -50,8 +50,8 @@ import java.util.Collection;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.ReflogEntry;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -90,7 +90,8 @@ public class ReflogCommandTest extends RepositoryTestCase {
 		assertNotNull(reflog);
 		assertEquals(3, reflog.size());
 		ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[reflog.size()]);
-		assertEquals(reflogs[2].getComment(), "commit: Initial commit");
+		assertEquals(reflogs[2].getComment(),
+				"commit (initial): Initial commit");
 		assertEquals(reflogs[2].getNewId(), commit1.getId());
 		assertEquals(reflogs[2].getOldId(), ObjectId.zeroId());
 		assertEquals(reflogs[1].getComment(),
@@ -136,7 +137,8 @@ public class ReflogCommandTest extends RepositoryTestCase {
 		assertNotNull(reflog);
 		assertEquals(4, reflog.size());
 		ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[reflog.size()]);
-		assertEquals(reflogs[3].getComment(), "commit: Initial commit");
+		assertEquals(reflogs[3].getComment(),
+				"commit (initial): Initial commit");
 		assertEquals(reflogs[3].getNewId(), commit1.getId());
 		assertEquals(reflogs[3].getOldId(), ObjectId.zeroId());
 		assertEquals(reflogs[2].getComment(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
index 228e11d..bb3e7a9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
@@ -61,10 +61,10 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.junit.Test;
 
 /**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
index 4dfac14..a81beb0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
@@ -543,4 +543,19 @@ public class StashApplyCommandTest extends RepositoryTestCase {
 			assertNotNull(e.getMessage());
 		}
 	}
+
+	@Test
+	public void testApplyStashWithDeletedFile() throws Exception {
+		File file = writeTrashFile("file", "content");
+		git.add().addFilepattern("file").call();
+		git.commit().setMessage("x").call();
+		file.delete();
+		git.rm().addFilepattern("file").call();
+		git.stashCreate().call();
+		file.delete();
+
+		git.stashApply().setStashRef("stash@{0}").call();
+
+		assertFalse(file.exists());
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
index f91229d..030dc9f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
@@ -59,10 +59,10 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.eclipse.jgit.util.FileUtils;
@@ -88,6 +88,7 @@ public class StashCreateCommandTest extends RepositoryTestCase {
 		git.add().addFilepattern("file.txt").call();
 		head = git.commit().setMessage("add file").call();
 		assertNotNull(head);
+		writeTrashFile("untracked.txt", "content");
 	}
 
 	/**
@@ -155,6 +156,18 @@ public class StashCreateCommandTest extends RepositoryTestCase {
 		}
 	}
 
+	private List<DiffEntry> diffIndexAgainstWorking(final RevCommit commit)
+			throws IOException {
+		TreeWalk walk = createTreeWalk();
+		try {
+			walk.addTree(commit.getParent(1).getTree());
+			walk.addTree(commit.getTree());
+			return DiffEntry.scan(walk);
+		} finally {
+			walk.release();
+		}
+	}
+
 	@Test
 	public void noLocalChanges() throws Exception {
 		assertNull(git.stashCreate().call());
@@ -195,6 +208,26 @@ public class StashCreateCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void newFileInIndexThenModifiedInWorkTree() throws Exception {
+		writeTrashFile("file", "content");
+		git.add().addFilepattern("file").call();
+		writeTrashFile("file", "content2");
+		RevCommit stashedWorkTree = Git.wrap(db).stashCreate().call();
+		validateStashedCommit(stashedWorkTree);
+		RevWalk walk = new RevWalk(db);
+		RevCommit stashedIndex = stashedWorkTree.getParent(1);
+		walk.parseBody(stashedIndex);
+		walk.parseBody(stashedIndex.getTree());
+		walk.parseBody(stashedIndex.getParent(0));
+		List<DiffEntry> workTreeStashAgainstWorkTree = diffWorkingAgainstHead(stashedWorkTree);
+		assertEquals(1, workTreeStashAgainstWorkTree.size());
+		List<DiffEntry> workIndexAgainstWorkTree = diffIndexAgainstHead(stashedWorkTree);
+		assertEquals(1, workIndexAgainstWorkTree.size());
+		List<DiffEntry> indexStashAgainstWorkTree = diffIndexAgainstWorking(stashedWorkTree);
+		assertEquals(1, indexStashAgainstWorkTree.size());
+	}
+
+	@Test
 	public void indexDelete() throws Exception {
 		git.rm().addFilepattern("file.txt").call();
 
@@ -409,7 +442,7 @@ public class StashCreateCommandTest extends RepositoryTestCase {
 		assertEquals("content", read(committedFile));
 		validateStashedCommit(stashed);
 
-		ReflogReader reader = new ReflogReader(git.getRepository(),
+		ReflogReader reader = git.getRepository().getReflogReader(
 				Constants.R_STASH);
 		ReflogEntry entry = reader.getLastEntry();
 		assertNotNull(entry);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
index 571ff6d..cfad817 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
@@ -56,9 +56,9 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -126,9 +126,9 @@ public class StashDropCommandTest extends RepositoryTestCase {
 		stashRef = git.getRepository().getRef(Constants.R_STASH);
 		assertNull(stashRef);
 
-		ReflogReader reader = new ReflogReader(git.getRepository(),
+		ReflogReader reader = git.getRepository().getReflogReader(
 				Constants.R_STASH);
-		assertTrue(reader.getReverseEntries().isEmpty());
+		assertNull(reader);
 	}
 
 	@Test
@@ -154,9 +154,9 @@ public class StashDropCommandTest extends RepositoryTestCase {
 		assertNull(git.stashDrop().setAll(true).call());
 		assertNull(git.getRepository().getRef(Constants.R_STASH));
 
-		ReflogReader reader = new ReflogReader(git.getRepository(),
+		ReflogReader reader = git.getRepository().getReflogReader(
 				Constants.R_STASH);
-		assertTrue(reader.getReverseEntries().isEmpty());
+		assertNull(reader);
 	}
 
 	@Test
@@ -184,7 +184,7 @@ public class StashDropCommandTest extends RepositoryTestCase {
 		assertNotNull(stashRef);
 		assertEquals(firstStash, stashRef.getObjectId());
 
-		ReflogReader reader = new ReflogReader(git.getRepository(),
+		ReflogReader reader = git.getRepository().getReflogReader(
 				Constants.R_STASH);
 		List<ReflogEntry> entries = reader.getReverseEntries();
 		assertEquals(1, entries.size());
@@ -226,7 +226,7 @@ public class StashDropCommandTest extends RepositoryTestCase {
 		assertNotNull(stashRef);
 		assertEquals(thirdStash, stashRef.getObjectId());
 
-		ReflogReader reader = new ReflogReader(git.getRepository(),
+		ReflogReader reader = git.getRepository().getReflogReader(
 				Constants.R_STASH);
 		List<ReflogEntry> entries = reader.getReverseEntries();
 		assertEquals(2, entries.size());
@@ -284,7 +284,7 @@ public class StashDropCommandTest extends RepositoryTestCase {
 		assertNotNull(stashRef);
 		assertEquals(thirdStash, stashRef.getObjectId());
 
-		ReflogReader reader = new ReflogReader(git.getRepository(),
+		ReflogReader reader = git.getRepository().getReflogReader(
 				Constants.R_STASH);
 		List<ReflogEntry> entries = reader.getReverseEntries();
 		assertEquals(2, entries.size());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
index b9838b1..061d29f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk at gmail.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -81,6 +81,18 @@ public class TagCommandTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testUnannotatedTagging() throws GitAPIException,
+			JGitInternalException {
+		Git git = new Git(db);
+		git.commit().setMessage("initial commit").call();
+		RevCommit commit = git.commit().setMessage("second commit").call();
+		git.commit().setMessage("third commit").call();
+		Ref tagRef = git.tag().setObjectId(commit).setName("tag")
+				.setAnnotated(false).call();
+		assertEquals(commit.getId(), tagRef.getObjectId());
+	}
+
+	@Test
 	public void testEmptyTagName() throws GitAPIException {
 		Git git = new Git(db);
 		git.commit().setMessage("initial commit").call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
index 820c0c6..00e5f06 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2010, 2013 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -257,6 +257,35 @@ public class DiffFormatterTest extends RepositoryTestCase {
 	}
 
 	@Test
+	public void testCreateFileHeaderWithoutIndexLine() throws Exception {
+		DiffEntry m = DiffEntry.modify(PATH_A);
+		m.oldMode = FileMode.REGULAR_FILE;
+		m.newMode = FileMode.EXECUTABLE_FILE;
+
+		FileHeader fh = df.toFileHeader(m);
+		String expected = DIFF + "a/src/a b/src/a\n" + //
+				"old mode 100644\n" + //
+				"new mode 100755\n";
+		assertEquals(expected, fh.getScriptText());
+	}
+
+	@Test
+	public void testCreateFileHeaderForRenameWithoutContentChange() throws Exception {
+		DiffEntry a = DiffEntry.delete(PATH_A, ObjectId.zeroId());
+		DiffEntry b = DiffEntry.add(PATH_B, ObjectId.zeroId());
+		DiffEntry m = DiffEntry.pair(ChangeType.RENAME, a, b, 100);
+		m.oldId = null;
+		m.newId = null;
+
+		FileHeader fh = df.toFileHeader(m);
+		String expected = DIFF + "a/src/a b/src/b\n" + //
+				"similarity index 100%\n" + //
+				"rename from src/a\n" + //
+				"rename to src/b\n";
+		assertEquals(expected, fh.getScriptText());
+	}
+
+	@Test
 	public void testDiff() throws Exception {
 		write(new File(db.getDirectory().getParent(), "test.txt"), "test");
 		File folder = new File(db.getDirectory().getParent(), "folder");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
similarity index 95%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/AbbreviationTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
index c099bb0..3a0b827 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/AbbreviationTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
@@ -60,11 +60,15 @@ import java.util.Collection;
 import java.util.List;
 
 import org.eclipse.jgit.errors.AmbiguousObjectException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.PackIndexWriterV2;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.transport.PackedObjectInfo;
 import org.eclipse.jgit.util.FileUtils;
@@ -78,14 +82,14 @@ public class AbbreviationTest extends LocalDiskRepositoryTestCase {
 
 	private ObjectReader reader;
 
-	private TestRepository<FileRepository> test;
+	private TestRepository<Repository> test;
 
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
 		db = createBareRepository();
 		reader = db.newObjectReader();
-		test = new TestRepository<FileRepository>(db);
+		test = new TestRepository<Repository>(db);
 	}
 
 	@After
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
index 2e3b1d1..562cde7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ConcurrentRepackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -55,8 +55,10 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
@@ -67,7 +69,7 @@ import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.pack.PackWriter;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
 import org.junit.After;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileRepositoryBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
similarity index 88%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileRepositoryBuilderTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
index 8f3f488..0655415 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileRepositoryBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -55,13 +55,16 @@ import java.io.IOException;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Test;
 
 public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 	@Test
 	public void testShouldAutomagicallyDetectGitDirectory() throws Exception {
-		FileRepository r = createWorkRepository();
+		Repository r = createWorkRepository();
 		File d = new File(r.getDirectory(), "sub-dir");
 		FileUtils.mkdir(d);
 
@@ -71,8 +74,8 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 
 	@Test
 	public void emptyRepositoryFormatVersion() throws Exception {
-		FileRepository r = createWorkRepository();
-		FileBasedConfig config = r.getConfig();
+		Repository r = createWorkRepository();
+		StoredConfig config = r.getConfig();
 		config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, "");
 		config.save();
@@ -82,8 +85,8 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 
 	@Test
 	public void invalidRepositoryFormatVersion() throws Exception {
-		FileRepository r = createWorkRepository();
-		FileBasedConfig config = r.getConfig();
+		Repository r = createWorkRepository();
+		StoredConfig config = r.getConfig();
 		config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, "notanumber");
 		config.save();
@@ -98,8 +101,8 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 
 	@Test
 	public void unknownRepositoryFormatVersion() throws Exception {
-		FileRepository r = createWorkRepository();
-		FileBasedConfig config = r.getConfig();
+		Repository r = createWorkRepository();
+		StoredConfig config = r.getConfig();
 		config.setLong(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 1);
 		config.save();
@@ -115,7 +118,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 	@SuppressWarnings("resource" /* java 7 */)
 	@Test
 	public void absoluteGitDirRef() throws Exception {
-		FileRepository repo1 = createWorkRepository();
+		Repository repo1 = createWorkRepository();
 		File dir = createTempDirectory("dir");
 		File dotGit = new File(dir, Constants.DOT_GIT);
 		new FileWriter(dotGit).append(
@@ -124,7 +127,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 
 		builder.setWorkTree(dir);
 		builder.setMustExist(true);
-		FileRepository repo2 = builder.build();
+		Repository repo2 = builder.build();
 
 		assertEquals(repo1.getDirectory(), repo2.getDirectory());
 		assertEquals(dir, repo2.getWorkTree());
@@ -133,7 +136,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 	@SuppressWarnings("resource" /* java 7 */)
 	@Test
 	public void relativeGitDirRef() throws Exception {
-		FileRepository repo1 = createWorkRepository();
+		Repository repo1 = createWorkRepository();
 		File dir = new File(repo1.getWorkTree(), "dir");
 		assertTrue(dir.mkdir());
 		File dotGit = new File(dir, Constants.DOT_GIT);
@@ -143,7 +146,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 		FileRepositoryBuilder builder = new FileRepositoryBuilder();
 		builder.setWorkTree(dir);
 		builder.setMustExist(true);
-		FileRepository repo2 = builder.build();
+		Repository repo2 = builder.build();
 
 		assertEquals(repo1.getDirectory(), repo2.getDirectory());
 		assertEquals(dir, repo2.getWorkTree());
@@ -152,7 +155,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 	@SuppressWarnings("resource" /* java 7 */)
 	@Test
 	public void scanWithGitDirRef() throws Exception {
-		FileRepository repo1 = createWorkRepository();
+		Repository repo1 = createWorkRepository();
 		File dir = createTempDirectory("dir");
 		File dotGit = new File(dir, Constants.DOT_GIT);
 		new FileWriter(dotGit).append(
@@ -163,7 +166,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
 		builder.findGitDir(dir);
 		assertEquals(repo1.getDirectory(), builder.getGitDir());
 		builder.setMustExist(true);
-		FileRepository repo2 = builder.build();
+		Repository repo2 = builder.build();
 
 		assertEquals(repo1.getDirectory(), repo2.getDirectory());
 		assertEquals(dir, repo2.getWorkTree());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileSnapshotTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
index 22fc4c7..17c44dc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
@@ -40,7 +40,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -51,6 +51,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jgit.internal.storage.file.FileSnapshot;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.After;
 import org.junit.Before;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java
similarity index 95%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java
index c61ea73..9d2a03b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java
@@ -40,7 +40,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static java.lang.Integer.valueOf;
 import static org.junit.Assert.assertEquals;
@@ -49,8 +49,8 @@ import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
-import java.util.Collection;
 import java.io.IOException;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
@@ -62,14 +62,17 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
-import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.EmptyProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref.Storage;
@@ -81,8 +84,6 @@ import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.storage.file.GC.RepoStatistics;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -427,6 +428,27 @@ public class GCTest extends LocalDiskRepositoryTestCase {
 		assertEquals(0, stats.numberOfLooseObjects);
 		assertEquals(4, stats.numberOfPackedObjects);
 		assertEquals(1, stats.numberOfPackFiles);
+
+		// Do the gc again and check that it hasn't changed anything
+		gc.gc();
+		stats = gc.getStatistics();
+		assertEquals(0, stats.numberOfLooseObjects);
+		assertEquals(4, stats.numberOfPackedObjects);
+		assertEquals(1, stats.numberOfPackFiles);
+	}
+
+	@Test
+	public void testPackRepoWithCorruptReflog() throws Exception {
+		// create a reflog entry "0000... 0000... foobar" by doing an initial
+		// refupdate for HEAD which points to a non-existing ref. The
+		// All-Projects repo of gerrit instances had such entries
+		RefUpdate ru = repo.updateRef(Constants.HEAD);
+		ru.link("refs/to/garbage");
+		tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
+				.create();
+		// make sure HEAD exists
+		Git.wrap(repo).checkout().setName("refs/heads/master").call();
+		gc.gc();
 	}
 
 	@Test
@@ -470,9 +492,10 @@ public class GCTest extends LocalDiskRepositoryTestCase {
 		assertEquals(4, ind2.getObjectCount());
 		for (MutableEntry e: ind1)
 			if (ind2.hasObject(e.toObjectId()))
-			assertFalse(
-					"the following object is in both packfiles: "
-							+ e.toObjectId(), ind2.hasObject(e.toObjectId()));
+				assertFalse(
+						"the following object is in both packfiles: "
+								+ e.toObjectId(),
+						ind2.hasObject(e.toObjectId()));
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/InflatingBitSetTest.java
similarity index 55%
copy from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/InflatingBitSetTest.java
index 8231958..f4f66c8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/InflatingBitSetTest.java
@@ -1,7 +1,5 @@
 /*
- * Copyright (C) 2008, Imran M Yousuf <imyousuf at smartitengineering.com>
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski at gmail.com>
- * Copyright (C) 2009, Matthias Sohn <matthias.sohn at sap.com>
+ * Copyright (C) 2012, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,46 +41,67 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
 
-import java.io.File;
+import javaewah.EWAHCompressedBitmap;
 
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.internal.storage.file.InflatingBitSet;
 import org.junit.Test;
 
-public class PackIndexV1Test extends PackIndexTestCase {
-	@Override
-	public File getFileForPack34be9032() {
-		return JGitTestUtil.getTestResourceFile(
-                    "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx");
+public class InflatingBitSetTest {
+
+	@Test
+	public void testMaybeContains() {
+		EWAHCompressedBitmap ecb = new EWAHCompressedBitmap();
+		ecb.set(63);
+		ecb.set(64);
+		ecb.set(128);
+
+		InflatingBitSet ibs = new InflatingBitSet(ecb);
+		assertTrue(ibs.maybeContains(0));
+		assertFalse(ibs.contains(0)); // Advance
+		assertFalse(ibs.maybeContains(0));
+		assertTrue(ibs.maybeContains(63));
+		assertTrue(ibs.maybeContains(64));
+		assertTrue(ibs.maybeContains(65));
+		assertFalse(ibs.maybeContains(129));
 	}
 
-	@Override
-	public File getFileForPackdf2982f28() {
-		return JGitTestUtil.getTestResourceFile(
-                    "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idx");
+	@Test
+	public void testContainsMany() {
+		EWAHCompressedBitmap ecb = new EWAHCompressedBitmap();
+		ecb.set(64);
+		ecb.set(65);
+		ecb.set(1024);
+
+		InflatingBitSet ibs = new InflatingBitSet(ecb);
+		assertFalse(ibs.contains(0));
+		assertTrue(ibs.contains(64));
+		assertTrue(ibs.contains(65));
+		assertFalse(ibs.contains(66));
+		assertTrue(ibs.contains(1024));
+		assertFalse(ibs.contains(1025));
+	}
+
+	@Test
+	public void testContainsOne() {
+		EWAHCompressedBitmap ecb = new EWAHCompressedBitmap();
+		ecb.set(64);
+
+		InflatingBitSet ibs = new InflatingBitSet(ecb);
+		assertTrue(ibs.contains(64));
+		assertTrue(ibs.contains(64));
+		assertFalse(ibs.contains(65));
+		assertFalse(ibs.contains(63));
 	}
 
-	/**
-	 * Verify CRC32 - V1 should not index anything.
-	 *
-	 * @throws MissingObjectException
-	 */
-	@Override
 	@Test
-	public void testCRC32() throws MissingObjectException {
-		assertFalse(smallIdx.hasCRC32Support());
-		try {
-			smallIdx.findCRC32(ObjectId
-					.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"));
-			fail("index V1 shouldn't support CRC");
-		} catch (UnsupportedOperationException x) {
-			// expected
-		}
+	public void testContainsEmpty() {
+		InflatingBitSet ibs = new InflatingBitSet(new EWAHCompressedBitmap());
+		assertFalse(ibs.maybeContains(0));
+		assertFalse(ibs.contains(0));
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/LockFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java
similarity index 96%
copy from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/LockFileTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java
index a5cf5cc..1a3a567 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/LockFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/LockFileTest.java
@@ -40,7 +40,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -49,6 +49,7 @@ import static org.junit.Assert.fail;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.errors.LockFailedException;
+import org.eclipse.jgit.internal.storage.file.LockFile;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Test;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ObjectDirectoryTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index de6cd4c..3226f42 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -40,7 +40,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -49,6 +49,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
index 11082bd..ced4575 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -60,6 +60,7 @@ import java.util.zip.Deflater;
 
 import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.DeltaEncoder;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
@@ -70,8 +71,9 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.storage.pack.DeltaEncoder;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.transport.PackParser;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.NB;
@@ -85,9 +87,9 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
 
 	private TestRng rng;
 
-	private FileRepository repo;
+	private Repository repo;
 
-	private TestRepository<FileRepository> tr;
+	private TestRepository<Repository> tr;
 
 	private WindowCursor wc;
 
@@ -106,7 +108,7 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
 		WindowCache.reconfigure(cfg);
 
 		repo = createBareRepository();
-		tr = new TestRepository<FileRepository>(repo);
+		tr = new TestRepository<Repository>(repo);
 		wc = (WindowCursor) repo.newObjectReader();
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
index 9294172..aa3817e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -52,8 +52,9 @@ import java.util.Iterator;
 import java.util.NoSuchElementException;
 
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
 import org.junit.Test;
 
 public abstract class PackIndexTestCase extends RepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexV1Test.java
similarity index 98%
copy from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexV1Test.java
index 8231958..3de46ca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexV1Test.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexV2Test.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexV2Test.java
index 039cd6f..0e2e2eb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexV2Test.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
similarity index 94%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
index 530636f..2126cac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackReverseIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -50,9 +50,11 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index 4ef9698..75ccb6b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -63,6 +63,9 @@ import java.util.List;
 import java.util.Set;
 
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.pack.PackWriter.ObjectIdSet;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
@@ -75,10 +78,7 @@ import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.storage.pack.PackConfig;
-import org.eclipse.jgit.storage.pack.PackWriter;
-import org.eclipse.jgit.storage.pack.PackWriter.ObjectIdSet;
 import org.eclipse.jgit.transport.PackParser;
 import org.junit.After;
 import org.junit.Before;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
similarity index 86%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index a602f7c..a821e94 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2010, 2013 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.lib.Constants.HEAD;
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
@@ -60,6 +60,8 @@ import static org.junit.Assert.fail;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -70,12 +72,18 @@ import org.eclipse.jgit.events.RefsChangedListener;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceiveCommand.Type;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -1173,6 +1181,109 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
 		assertEquals(1, changeCount.get());
 	}
 
+	@Test
+	public void testBatchRefUpdateSimpleNoForce() throws IOException {
+		writeLooseRef("refs/heads/master", A);
+		writeLooseRef("refs/heads/masters", B);
+		List<ReceiveCommand> commands = Arrays.asList(
+				newCommand(A, B, "refs/heads/master",
+						ReceiveCommand.Type.UPDATE),
+				newCommand(B, A, "refs/heads/masters",
+						ReceiveCommand.Type.UPDATE_NONFASTFORWARD));
+		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
+		batchUpdate.addCommand(commands);
+		batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor());
+		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
+		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
+		assertEquals(ReceiveCommand.Result.REJECTED_NONFASTFORWARD, commands
+				.get(1).getResult());
+		assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs
+				.keySet().toString());
+		assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
+		assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId());
+	}
+
+	@Test
+	public void testBatchRefUpdateSimpleForce() throws IOException {
+		writeLooseRef("refs/heads/master", A);
+		writeLooseRef("refs/heads/masters", B);
+		List<ReceiveCommand> commands = Arrays.asList(
+				newCommand(A, B, "refs/heads/master",
+						ReceiveCommand.Type.UPDATE),
+				newCommand(B, A, "refs/heads/masters",
+						ReceiveCommand.Type.UPDATE_NONFASTFORWARD));
+		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
+		batchUpdate.setAllowNonFastForwards(true);
+		batchUpdate.addCommand(commands);
+		batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor());
+		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
+		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
+		assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult());
+		assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs
+				.keySet().toString());
+		assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
+		assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId());
+	}
+
+	@Test
+	public void testBatchRefUpdateConflict() throws IOException {
+		writeLooseRef("refs/heads/master", A);
+		writeLooseRef("refs/heads/masters", B);
+		List<ReceiveCommand> commands = Arrays.asList(
+				newCommand(A, B, "refs/heads/master",
+						ReceiveCommand.Type.UPDATE),
+				newCommand(null, A, "refs/heads/master/x",
+						ReceiveCommand.Type.CREATE),
+				newCommand(null, A, "refs/heads", ReceiveCommand.Type.CREATE));
+		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
+		batchUpdate.setAllowNonFastForwards(true);
+		batchUpdate.addCommand(commands);
+		batchUpdate
+				.execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE);
+		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
+		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
+		assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(1)
+				.getResult());
+		assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(2)
+				.getResult());
+		assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs
+				.keySet().toString());
+		assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
+		assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId());
+	}
+
+	@Test
+	public void testBatchRefUpdateConflictThanksToDelete() throws IOException {
+		writeLooseRef("refs/heads/master", A);
+		writeLooseRef("refs/heads/masters", B);
+		List<ReceiveCommand> commands = Arrays.asList(
+				newCommand(A, B, "refs/heads/master",
+						ReceiveCommand.Type.UPDATE),
+				newCommand(null, A, "refs/heads/masters/x",
+						ReceiveCommand.Type.CREATE),
+				newCommand(B, null, "refs/heads/masters",
+						ReceiveCommand.Type.DELETE));
+		BatchRefUpdate batchUpdate = refdir.newBatchUpdate();
+		batchUpdate.setAllowNonFastForwards(true);
+		batchUpdate.addCommand(commands);
+		batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor());
+		Map<String, Ref> refs = refdir.getRefs(RefDatabase.ALL);
+		assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult());
+		assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult());
+		assertEquals(ReceiveCommand.Result.OK, commands.get(2).getResult());
+		assertEquals("[HEAD, refs/heads/master, refs/heads/masters/x]", refs
+				.keySet().toString());
+		assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId());
+	}
+
+	private static ReceiveCommand newCommand(RevCommit a, RevCommit b,
+			String string, Type update) {
+		ReceiveCommand ret = new ReceiveCommand(a != null ? a.getId() : null,
+				b != null ? b.getId() : null, string, update);
+		ret.setResult(ReceiveCommand.Result.NOT_ATTEMPTED);
+		return ret;
+	}
+
 	private void writeLooseRef(String name, AnyObjectId id) throws IOException {
 		writeLooseRef(name, id.name() + "\n");
 	}
@@ -1198,4 +1309,29 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
 		File path = new File(diskRepo.getDirectory(), name);
 		assertTrue("deleted " + name, path.delete());
 	}
+
+	private static final class StrictWorkMonitor implements ProgressMonitor {
+		private int lastWork, totalWork;
+
+		public void start(int totalTasks) {
+			// empty
+		}
+
+		public void beginTask(String title, int totalWork) {
+			this.totalWork = totalWork;
+			lastWork = 0;
+		}
+
+		public void update(int completed) {
+			lastWork += completed;
+		}
+
+		public void endTask() {
+			assertEquals("Units of work recorded", totalWork, lastWork);
+		}
+
+		public boolean isCancelled() {
+			return false;
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index cd56d28..21df179 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008, Charles O'Farrell <charleso at charleso.org>
  * Copyright (C) 2009-2010, Google Inc.
- * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg at dewire.com>
+ * Copyright (C) 2008-2013, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.junit.Assert.assertEquals;
 import static org.junit.Assert.assertEquals;
@@ -70,6 +70,8 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefRename;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -122,14 +124,16 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
 		assertNotSame(newid, r.getObjectId());
 		assertSame(ObjectId.class, r.getObjectId().getClass());
 		assertEquals(newid, r.getObjectId());
-		List<org.eclipse.jgit.storage.file.ReflogEntry> reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries();
-		org.eclipse.jgit.storage.file.ReflogEntry entry1 = reverseEntries1.get(0);
+		List<ReflogEntry> reverseEntries1 = db
+				.getReflogReader("refs/heads/abc").getReverseEntries();
+		ReflogEntry entry1 = reverseEntries1.get(0);
 		assertEquals(1, reverseEntries1.size());
 		assertEquals(ObjectId.zeroId(), entry1.getOldId());
 		assertEquals(r.getObjectId(), entry1.getNewId());
 		assertEquals(new PersonIdent(db).toString(),  entry1.getWho().toString());
 		assertEquals("", entry1.getComment());
-		List<org.eclipse.jgit.storage.file.ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries();
+		List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
+				.getReverseEntries();
 		assertEquals(0, reverseEntries2.size());
 	}
 
@@ -280,6 +284,21 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
 	}
 
 	@Test
+	public void testDeleteWithoutHead() throws IOException {
+		// Prepare repository without HEAD
+		RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
+		refUpdate.setForceUpdate(true);
+		refUpdate.setNewObjectId(ObjectId.zeroId());
+		Result updateResult = refUpdate.update();
+		assertEquals(Result.FORCED, updateResult);
+		Result deleteHeadResult = db.updateRef(Constants.HEAD).delete();
+		assertEquals(Result.NO_CHANGE, deleteHeadResult);
+
+		// Any result is ok as long as it's not an NPE
+		db.updateRef(Constants.R_HEADS + "master").delete();
+	}
+
+	@Test
 	public void testRefKeySameAsName() {
 		Map<String, Ref> allRefs = db.getAllRefs();
 		for (Entry<String, Ref> e : allRefs.entrySet()) {
@@ -334,8 +353,8 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
 
 		// the branch HEAD referred to is left untouched
 		assertEquals(pid, db.resolve("refs/heads/master"));
-		ReflogReader reflogReader = new  ReflogReader(db, "HEAD");
-		org.eclipse.jgit.storage.file.ReflogEntry e = reflogReader.getReverseEntries().get(0);
+		ReflogReader reflogReader = db.getReflogReader("HEAD");
+		ReflogEntry e = reflogReader.getReverseEntries().get(0);
 		assertEquals(pid, e.getOldId());
 		assertEquals(ppid, e.getNewId());
 		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -364,8 +383,8 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
 
 		// the branch HEAD referred to is left untouched
 		assertNull(db.resolve("refs/heads/unborn"));
-		ReflogReader reflogReader = new  ReflogReader(db, "HEAD");
-		org.eclipse.jgit.storage.file.ReflogEntry e = reflogReader.getReverseEntries().get(0);
+		ReflogReader reflogReader = db.getReflogReader("HEAD");
+		ReflogEntry e = reflogReader.getReverseEntries().get(0);
 		assertEquals(ObjectId.zeroId(), e.getOldId());
 		assertEquals(ppid, e.getNewId());
 		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -701,9 +720,9 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
 		ObjectId oldfromId = db.resolve(fromName);
 		ObjectId oldHeadId = db.resolve(Constants.HEAD);
 		writeReflog(db, oldfromId, "Just a message", fromName);
-		List<org.eclipse.jgit.storage.file.ReflogEntry> oldFromLog = db
+		List<ReflogEntry> oldFromLog = db
 				.getReflogReader(fromName).getReverseEntries();
-		List<org.eclipse.jgit.storage.file.ReflogEntry> oldHeadLog = oldHeadId != null ? db
+		List<ReflogEntry> oldHeadLog = oldHeadId != null ? db
 				.getReflogReader(Constants.HEAD).getReverseEntries() : null;
 
 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
index e52c89f..a0dff7e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -56,9 +56,12 @@ import java.text.SimpleDateFormat;
 import java.util.List;
 
 import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.lib.CheckoutEntry;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.junit.Test;
 
 public class ReflogReaderTest extends SampleDataRepositoryTestCase {
@@ -94,7 +97,7 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
 	public void testReadOneLine() throws Exception {
 		setupReflog("logs/refs/heads/master", oneLine);
 
-		ReflogReader reader = new ReflogReader(db, "refs/heads/master");
+		ReflogReader reader = new ReflogReaderImpl(db, "refs/heads/master");
 		ReflogEntry e = reader.getLastEntry();
 		assertEquals(ObjectId
 				.fromString("da85355dfc525c9f6f3927b876f379f46ccf826e"), e
@@ -121,7 +124,7 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
 	public void testReadTwoLine() throws Exception {
 		setupReflog("logs/refs/heads/master", twoLine);
 
-		ReflogReader reader = new ReflogReader(db, "refs/heads/master");
+		ReflogReader reader = new ReflogReaderImpl(db, "refs/heads/master");
 		List<ReflogEntry> reverseEntries = reader.getReverseEntries();
 		assertEquals(2, reverseEntries.size());
 		ReflogEntry e = reverseEntries.get(0);
@@ -156,7 +159,7 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
 	@Test
 	public void testReadWhileAppendIsInProgress() throws Exception {
 		setupReflog("logs/refs/heads/master", twoLineWithAppendInProgress);
-		ReflogReader reader = new ReflogReader(db, "refs/heads/master");
+		ReflogReader reader = new ReflogReaderImpl(db, "refs/heads/master");
 		List<ReflogEntry> reverseEntries = reader.getReverseEntries();
 		assertEquals(2, reverseEntries.size());
 		ReflogEntry e = reverseEntries.get(0);
@@ -233,7 +236,7 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase {
 	public void testSpecificEntryNumber() throws Exception {
 		setupReflog("logs/refs/heads/master", twoLine);
 
-		ReflogReader reader = new ReflogReader(db, "refs/heads/master");
+		ReflogReader reader = new ReflogReaderImpl(db, "refs/heads/master");
 		ReflogEntry e = reader.getReverseEntry(0);
 		assertEquals(
 				ObjectId.fromString("c6734895958052a9dbc396cff4459dc1a25029ab"),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RepositorySetupWorkDirTest.java
similarity index 92%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RepositorySetupWorkDirTest.java
index 8aaeb49..bc47782 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RepositorySetupWorkDirTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RepositorySetupWorkDirTest.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -58,6 +58,8 @@ import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Test;
@@ -95,7 +97,8 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
 	@Test
 	public void testNotBare_CreateRepositoryFromWorkDirOnly() throws Exception {
 		File workdir = getFile("workdir", "repo");
-		FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build();
+		Repository repo = new FileRepositoryBuilder().setWorkTree(workdir)
+				.build();
 		assertFalse(repo.isBare());
 		assertWorkdirPath(repo, "workdir", "repo");
 		assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT);
@@ -105,7 +108,8 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
 	public void testWorkdirIsDotGit_CreateRepositoryFromWorkDirOnly()
 			throws Exception {
 		File workdir = getFile("workdir", "repo");
-		FileRepository repo = new FileRepositoryBuilder().setWorkTree(workdir).build();
+		Repository repo = new FileRepositoryBuilder().setWorkTree(workdir)
+				.build();
 		assertGitdirPath(repo, "workdir", "repo", Constants.DOT_GIT);
 	}
 
@@ -115,7 +119,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
 		File gitDir = getFile("workdir", "repoWithConfig");
 		File workTree = getFile("workdir", "treeRoot");
 		setWorkTree(gitDir, workTree);
-		FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
+		Repository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
 		assertFalse(repo.isBare());
 		assertWorkdirPath(repo, "workdir", "treeRoot");
 		assertGitdirPath(repo, "workdir", "repoWithConfig");
@@ -126,7 +130,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
 			throws Exception {
 		File gitDir = getFile("workdir", "repoWithConfig");
 		setBare(gitDir, true);
-		FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
+		Repository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
 		assertTrue(repo.isBare());
 	}
 
@@ -135,7 +139,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
 			throws Exception {
 		File gitDir = getFile("workdir", "repoWithBareConfigTrue", "child");
 		setBare(gitDir, false);
-		FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
+		Repository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
 		assertWorkdirPath(repo, "workdir", "repoWithBareConfigTrue");
 	}
 
@@ -144,7 +148,7 @@ public class RepositorySetupWorkDirTest extends LocalDiskRepositoryTestCase {
 			throws Exception {
 		File gitDir = getFile("workdir", "repoWithBareConfigFalse", "child");
 		setBare(gitDir, false);
-		FileRepository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
+		Repository repo = new FileRepositoryBuilder().setGitDir(gitDir).build();
 		assertFalse(repo.isBare());
 		assertWorkdirPath(repo, "workdir", "repoWithBareConfigFalse");
 		assertGitdirPath(repo, "workdir", "repoWithBareConfigFalse", "child");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/StoredBitmapTest.java
similarity index 60%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/StoredBitmapTest.java
index 8231958..dc4c25e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV1Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/StoredBitmapTest.java
@@ -1,7 +1,5 @@
 /*
- * Copyright (C) 2008, Imran M Yousuf <imyousuf at smartitengineering.com>
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski at gmail.com>
- * Copyright (C) 2009, Matthias Sohn <matthias.sohn at sap.com>
+ * Copyright (C) 2012, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,46 +41,54 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 
-import java.io.File;
+import javaewah.EWAHCompressedBitmap;
 
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.internal.storage.file.BasePackBitmapIndex.StoredBitmap;
 import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Test;
 
-public class PackIndexV1Test extends PackIndexTestCase {
-	@Override
-	public File getFileForPack34be9032() {
-		return JGitTestUtil.getTestResourceFile(
-                    "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx");
+public class StoredBitmapTest {
+
+	@Test
+	public void testGetBitmapWithoutXor() {
+		EWAHCompressedBitmap b = bitmapOf(100);
+		StoredBitmap sb = newStoredBitmap(bitmapOf(100));
+		assertEquals(b, sb.getBitmap());
 	}
 
-	@Override
-	public File getFileForPackdf2982f28() {
-		return JGitTestUtil.getTestResourceFile(
-                    "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idx");
+	@Test
+	public void testGetBitmapWithOneXor() {
+		StoredBitmap sb = newStoredBitmap(bitmapOf(100), bitmapOf(100, 101));
+		assertEquals(bitmapOf(101), sb.getBitmap());
 	}
 
-	/**
-	 * Verify CRC32 - V1 should not index anything.
-	 *
-	 * @throws MissingObjectException
-	 */
-	@Override
 	@Test
-	public void testCRC32() throws MissingObjectException {
-		assertFalse(smallIdx.hasCRC32Support());
-		try {
-			smallIdx.findCRC32(ObjectId
-					.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"));
-			fail("index V1 shouldn't support CRC");
-		} catch (UnsupportedOperationException x) {
-			// expected
-		}
+	public void testGetBitmapWithThreeXor() {
+		StoredBitmap sb = newStoredBitmap(
+				bitmapOf(100),
+				bitmapOf(90, 101),
+				bitmapOf(100, 101),
+				bitmapOf(50));
+		assertEquals(bitmapOf(50, 90), sb.getBitmap());
+		assertEquals(bitmapOf(50, 90), sb.getBitmap());
+	}
+
+	private static final StoredBitmap newStoredBitmap(
+			EWAHCompressedBitmap... bitmaps) {
+		StoredBitmap sb = null;
+		for (EWAHCompressedBitmap bitmap : bitmaps)
+			sb = new StoredBitmap(ObjectId.zeroId(), bitmap, sb, 0);
+		return sb;
+	}
+
+	private static final EWAHCompressedBitmap bitmapOf(int... bits) {
+		EWAHCompressedBitmap b = new EWAHCompressedBitmap();
+		for (int bit : bits)
+			b.set(bit);
+		return b;
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_BasicTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index f97bdd1..921ec29 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -44,7 +44,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -81,6 +81,8 @@ import org.eclipse.jgit.lib.TreeFormatter;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Test;
 
@@ -138,8 +140,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
 		repo1initial.close();
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = new FileRepositoryBuilder().setGitDir(theDir)
-				.build();
+		FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).build();
 		assertEqualsPath(theDir, r.getDirectory());
 		assertEqualsPath(repo1Parent, r.getWorkTree());
 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
@@ -163,8 +165,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
 		repo1initial.close();
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = new FileRepositoryBuilder().setGitDir(theDir)
-				.setWorkTree(repo1Parent.getParentFile()).build();
+		FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).setWorkTree(repo1Parent.getParentFile())
+				.build();
 		assertEqualsPath(theDir, r.getDirectory());
 		assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree());
 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
@@ -187,8 +190,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
 		repo1initial.close();
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = new FileRepositoryBuilder().setWorkTree(repo1Parent)
-				.build();
+		FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setWorkTree(repo1Parent).build();
 		assertEqualsPath(theDir, r.getDirectory());
 		assertEqualsPath(repo1Parent, r.getWorkTree());
 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
@@ -216,8 +219,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
 		repo1initial.close();
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = new FileRepositoryBuilder().setGitDir(theDir)
-				.build();
+		FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).build();
 		assertEqualsPath(theDir, r.getDirectory());
 		assertEqualsPath(workdir, r.getWorkTree());
 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
@@ -245,8 +248,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
 		repo1initial.close();
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = new FileRepositoryBuilder().setGitDir(theDir)
-				.build();
+		FileRepository r = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(theDir).build();
 		assertEqualsPath(theDir, r.getDirectory());
 		assertEqualsPath(workdir, r.getWorkTree());
 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
@@ -273,7 +276,7 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
 		repo1initial.close();
 
 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
-		FileRepository r = new FileRepositoryBuilder() //
+		FileRepository r = (FileRepository) new FileRepositoryBuilder() //
 				.setGitDir(theDir).setObjectDirectory(objDir) //
 				.addAlternateObjectDirectory(altObjDir) //
 				.setIndexFile(indexFile) //
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
similarity index 90%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReaderTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
index 67861f6..dc78909 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/T0004_PackReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0004_PackReaderTest.java
@@ -44,14 +44,18 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import java.io.File;
 import java.io.IOException;
 
+import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.WindowCursor;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
@@ -70,7 +74,7 @@ public class T0004_PackReaderTest extends SampleDataRepositoryTestCase {
 		final ObjectLoader or;
 
 		id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327");
-		pr = new PackFile(TEST_PACK);
+		pr = new PackFile(TEST_PACK, PACK.getBit() | INDEX.getBit());
 		or = pr.get(new WindowCursor(null), id);
 		assertNotNull(or);
 		assertEquals(Constants.OBJ_TREE, or.getType());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
similarity index 99%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
index 16580b4..10d2b6b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/UnpackedObjectTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -71,6 +71,7 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.IO;
 import org.junit.After;
@@ -98,7 +99,7 @@ public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
 
 		WindowCacheConfig cfg = new WindowCacheConfig();
 		cfg.setStreamFileThreshold(streamThreshold);
-		WindowCache.reconfigure(cfg);
+		cfg.install();
 
 		repo = createBareRepository();
 		wc = (WindowCursor) repo.newObjectReader();
@@ -108,7 +109,7 @@ public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
 	public void tearDown() throws Exception {
 		if (wc != null)
 			wc.release();
-		WindowCache.reconfigure(new WindowCacheConfig());
+		new WindowCacheConfig().install();
 		super.tearDown();
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
index 98ae3da..f094220 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -60,6 +60,7 @@ import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.util.MutableInteger;
 import org.junit.Before;
 import org.junit.Test;
@@ -97,8 +98,8 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
 
 	@Test
 	public void testCache_Defaults() throws IOException {
-		final WindowCacheConfig cfg = new WindowCacheConfig();
-		WindowCache.reconfigure(cfg);
+		WindowCacheConfig cfg = new WindowCacheConfig();
+		cfg.install();
 		doCacheTests();
 		checkLimits(cfg);
 
@@ -111,7 +112,7 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
 	public void testCache_TooFewFiles() throws IOException {
 		final WindowCacheConfig cfg = new WindowCacheConfig();
 		cfg.setPackedGitOpenFiles(2);
-		WindowCache.reconfigure(cfg);
+		cfg.install();
 		doCacheTests();
 		checkLimits(cfg);
 	}
@@ -121,7 +122,7 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase {
 		final WindowCacheConfig cfg = new WindowCacheConfig();
 		cfg.setPackedGitWindowSize(4096);
 		cfg.setPackedGitLimit(4096);
-		WindowCache.reconfigure(cfg);
+		cfg.install();
 		doCacheTests();
 		checkLimits(cfg);
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheReconfigureTest.java
similarity index 94%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheReconfigureTest.java
index 467c1ac..2b7ee64 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/WindowCacheReconfigureTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheReconfigureTest.java
@@ -41,12 +41,13 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.junit.Test;
 
 public class WindowCacheReconfigureTest extends RepositoryTestCase {
@@ -55,7 +56,7 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 		try {
 			final WindowCacheConfig cfg = new WindowCacheConfig();
 			cfg.setPackedGitLimit(0);
-			WindowCache.reconfigure(cfg);
+			cfg.install();
 			fail("incorrectly permitted PackedGitLimit = 0");
 		} catch (IllegalArgumentException e) {
 			//
@@ -67,7 +68,7 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 		try {
 			final WindowCacheConfig cfg = new WindowCacheConfig();
 			cfg.setPackedGitWindowSize(0);
-			WindowCache.reconfigure(cfg);
+			cfg.install();
 			fail("incorrectly permitted PackedGitWindowSize = 0");
 		} catch (IllegalArgumentException e) {
 			assertEquals("Invalid window size", e.getMessage());
@@ -79,7 +80,7 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 		try {
 			final WindowCacheConfig cfg = new WindowCacheConfig();
 			cfg.setPackedGitWindowSize(512);
-			WindowCache.reconfigure(cfg);
+			cfg.install();
 			fail("incorrectly permitted PackedGitWindowSize = 512");
 		} catch (IllegalArgumentException e) {
 			assertEquals("Invalid window size", e.getMessage());
@@ -91,7 +92,7 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 		try {
 			final WindowCacheConfig cfg = new WindowCacheConfig();
 			cfg.setPackedGitWindowSize(4097);
-			WindowCache.reconfigure(cfg);
+			cfg.install();
 			fail("incorrectly permitted PackedGitWindowSize = 4097");
 		} catch (IllegalArgumentException e) {
 			assertEquals("Window size must be power of 2", e.getMessage());
@@ -103,7 +104,7 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 		try {
 			final WindowCacheConfig cfg = new WindowCacheConfig();
 			cfg.setPackedGitOpenFiles(0);
-			WindowCache.reconfigure(cfg);
+			cfg.install();
 			fail("incorrectly permitted PackedGitOpenFiles = 0");
 		} catch (IllegalArgumentException e) {
 			assertEquals("Open files must be >= 1", e.getMessage());
@@ -116,7 +117,7 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 			final WindowCacheConfig cfg = new WindowCacheConfig();
 			cfg.setPackedGitLimit(1024);
 			cfg.setPackedGitWindowSize(8192);
-			WindowCache.reconfigure(cfg);
+			cfg.install();
 			fail("incorrectly permitted PackedGitWindowSize > PackedGitLimit");
 		} catch (IllegalArgumentException e) {
 			assertEquals("Window size must be < limit", e.getMessage());
@@ -133,6 +134,6 @@ public class WindowCacheReconfigureTest extends RepositoryTestCase {
 		final WindowCacheConfig cfg = new WindowCacheConfig();
 		cfg.setPackedGitLimit(6 * 4096 / 5);
 		cfg.setPackedGitWindowSize(4096);
-		WindowCache.reconfigure(cfg);
+		cfg.install();
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/XInputStream.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/XInputStream.java
index 9978c8e..46f9ee3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/XInputStream.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/XInputStream.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.BufferedInputStream;
 import java.io.EOFException;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/DeltaIndexTest.java
similarity index 96%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/DeltaIndexTest.java
index 59acaf6..bb94a37 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/DeltaIndexTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -50,6 +50,10 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
+import org.eclipse.jgit.internal.storage.pack.DeltaEncoder;
+import org.eclipse.jgit.internal.storage.pack.DeltaIndex;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.TestRng;
 import org.eclipse.jgit.lib.Constants;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/DeltaStreamTest.java
similarity index 97%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/DeltaStreamTest.java
index 9b39d79..3ea0917 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/DeltaStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/DeltaStreamTest.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -57,6 +57,9 @@ import java.util.Arrays;
 
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
+import org.eclipse.jgit.internal.storage.pack.DeltaEncoder;
+import org.eclipse.jgit.internal.storage.pack.DeltaStream;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.TestRng;
 import org.eclipse.jgit.lib.Constants;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/IntSetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/IntSetTest.java
similarity index 94%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/IntSetTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/IntSetTest.java
index f8838f8..7a45006 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/pack/IntSetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/IntSetTest.java
@@ -41,9 +41,10 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index f074273..e5e1e7a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -556,6 +556,15 @@ public class ConfigTest {
 		assertEquals(result, config.toText());
 	}
 
+	@Test
+	public void testNoFinalNewline() throws ConfigInvalidException {
+		Config c = parse("[a]\n"
+				+ "x = 0\n"
+				+ "y = 1");
+		assertEquals("0", c.getString("a", null, "x"));
+		assertEquals("1", c.getString("a", null, "y"));
+	}
+
 	private static void assertReadLong(long exp) throws ConfigInvalidException {
 		assertReadLong(exp, String.valueOf(exp));
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
index d95eee2..d263f62 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java
@@ -37,8 +37,7 @@
  */
 package org.eclipse.jgit.lib;
 
-import static org.hamcrest.Matchers.startsWith;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.File;
@@ -399,7 +398,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase {
 		} catch (InvalidPathException e) {
 			if (good)
 				throw e;
-			assertThat(e.getMessage(), startsWith("Invalid path: "));
+			assertTrue(e.getMessage().startsWith("Invalid path: "));
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index 6040dfe..51ba5f1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -2,6 +2,7 @@
  * Copyright (C) 2007, Dave Watson <dwatson at mimvista.com>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg at dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2013, Robin Stocker <robin at nibor.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -46,6 +47,7 @@
 package org.eclipse.jgit.lib;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
@@ -61,10 +63,12 @@ import org.eclipse.jgit.api.MergeResult;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheEditor;
 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.IndexDiff.StageState;
 import org.eclipse.jgit.merge.MergeStrategy;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
@@ -212,6 +216,8 @@ public class IndexDiffTest extends RepositoryTestCase {
 		assertEquals("[]", diff.getMissing().toString());
 		assertEquals("[]", diff.getModified().toString());
 		assertEquals("[a]", diff.getConflicting().toString());
+		assertEquals(StageState.BOTH_MODIFIED,
+				diff.getConflictingStageStates().get("a"));
 		assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
 	}
 
@@ -251,6 +257,8 @@ public class IndexDiffTest extends RepositoryTestCase {
 		assertEquals("[]", diff.getMissing().toString());
 		assertEquals("[]", diff.getModified().toString());
 		assertEquals("[a]", diff.getConflicting().toString());
+		assertEquals(StageState.DELETED_BY_THEM,
+				diff.getConflictingStageStates().get("a"));
 		assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
 	}
 
@@ -500,6 +508,46 @@ public class IndexDiffTest extends RepositoryTestCase {
 		assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
 	}
 
+	@Test
+	public void testStageState() throws IOException {
+		final int base = DirCacheEntry.STAGE_1;
+		final int ours = DirCacheEntry.STAGE_2;
+		final int theirs = DirCacheEntry.STAGE_3;
+		verifyStageState(StageState.BOTH_DELETED, base);
+		verifyStageState(StageState.DELETED_BY_THEM, ours, base);
+		verifyStageState(StageState.DELETED_BY_US, base, theirs);
+		verifyStageState(StageState.BOTH_MODIFIED, base, ours, theirs);
+		verifyStageState(StageState.ADDED_BY_US, ours);
+		verifyStageState(StageState.BOTH_ADDED, ours, theirs);
+		verifyStageState(StageState.ADDED_BY_THEM, theirs);
+
+		assertTrue(StageState.BOTH_DELETED.hasBase());
+		assertFalse(StageState.BOTH_DELETED.hasOurs());
+		assertFalse(StageState.BOTH_DELETED.hasTheirs());
+		assertFalse(StageState.BOTH_ADDED.hasBase());
+		assertTrue(StageState.BOTH_ADDED.hasOurs());
+		assertTrue(StageState.BOTH_ADDED.hasTheirs());
+	}
+
+	private void verifyStageState(StageState expected, int... stages)
+			throws IOException {
+		DirCacheBuilder builder = db.lockDirCache().builder();
+		for (int stage : stages) {
+			DirCacheEntry entry = createEntry("a", FileMode.REGULAR_FILE,
+					stage, "content");
+			builder.add(entry);
+		}
+		builder.commit();
+
+		IndexDiff diff = new IndexDiff(db, Constants.HEAD,
+				new FileTreeIterator(db));
+		diff.diff();
+
+		assertEquals(
+				"Conflict for entries in stages " + Arrays.toString(stages),
+				expected, diff.getConflictingStageStates().get("a"));
+	}
+
 	private void removeFromIndex(String path) throws IOException {
 		final DirCache dirc = db.lockDirCache();
 		final DirCacheEditor edit = dirc.editor();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
index b44970e..2845f8a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
@@ -104,9 +104,11 @@ public class ThreadSafeProgressMonitorTest {
 		assertEquals(42, mock.value);
 
 		pm.update(1);
+		pm.pollForUpdates();
 		assertEquals(43, mock.value);
 
 		pm.update(2);
+		pm.pollForUpdates();
 		assertEquals(45, mock.value);
 
 		pm.endTask();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
new file mode 100644
index 0000000..8f43f7f
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick at sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.merge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.NoMergeBaseException;
+import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.junit.Before;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+ at RunWith(Theories.class)
+public class RecursiveMergerTest extends RepositoryTestCase {
+	static int counter = 0;
+
+	@DataPoints
+	public static MergeStrategy[] strategiesUnderTest = new MergeStrategy[] {
+			MergeStrategy.RECURSIVE, MergeStrategy.RESOLVE };
+
+	public enum IndexState {
+		Bare, Missing, SameAsHead, SameAsOther, SameAsWorkTree, DifferentFromHeadAndOtherAndWorktree
+	}
+
+	@DataPoints
+	public static IndexState[] indexStates = IndexState.values();
+
+	public enum WorktreeState {
+		Bare, Missing, SameAsHead, DifferentFromHeadAndOther, SameAsOther;
+	}
+
+	@DataPoints
+	public static WorktreeState[] worktreeStates = WorktreeState.values();
+
+	private TestRepository<FileRepository> db_t;
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+		super.setUp();
+		db_t = new TestRepository<FileRepository>(db);
+	}
+
+	@Theory
+	/**
+	 * Merging m2,s2 from the following topology. In master and side different
+	 * files are touched. No need to do a real content merge.
+	 *
+	 * <pre>
+	 * m0--m1--m2
+	 *   \   \/
+	 *    \  /\
+	 *     s1--s2
+	 * </pre>
+	 */
+	public void crissCrossMerge(MergeStrategy strategy, IndexState indexState,
+			WorktreeState worktreeState) throws Exception {
+		if (!validateStates(indexState, worktreeState))
+			return;
+		// fill the repo
+		BranchBuilder master = db_t.branch("master");
+		RevCommit m0 = master.commit().add("m", ",m0").message("m0").create();
+		RevCommit m1 = master.commit().add("m", "m1").message("m1").create();
+		db_t.getRevWalk().parseCommit(m1);
+
+		BranchBuilder side = db_t.branch("side");
+		RevCommit s1 = side.commit().parent(m0).add("s", "s1").message("s1")
+				.create();
+		RevCommit s2 = side.commit().parent(m1).add("m", "m1")
+				.message("s2(merge)").create();
+		RevCommit m2 = master.commit().parent(s1).add("s", "s1")
+				.message("m2(merge)").create();
+
+		Git git = Git.wrap(db);
+		git.checkout().setName("master").call();
+		modifyWorktree(worktreeState, "m", "side");
+		modifyWorktree(worktreeState, "s", "side");
+		modifyIndex(indexState, "m", "side");
+		modifyIndex(indexState, "s", "side");
+
+		ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
+				worktreeState == WorktreeState.Bare);
+		if (worktreeState != WorktreeState.Bare)
+			merger.setWorkingTreeIterator(new FileTreeIterator(db));
+		try {
+			boolean expectSuccess = true;
+			if (!(indexState == IndexState.Bare
+					|| indexState == IndexState.Missing
+					|| indexState == IndexState.SameAsHead || indexState == IndexState.SameAsOther))
+				// index is dirty
+				expectSuccess = false;
+
+			assertEquals(Boolean.valueOf(expectSuccess),
+					Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
+			assertEquals(MergeStrategy.RECURSIVE, strategy);
+			assertEquals("m1",
+					contentAsString(db, merger.getResultTreeId(), "m"));
+			assertEquals("s1",
+					contentAsString(db, merger.getResultTreeId(), "s"));
+		} catch (NoMergeBaseException e) {
+			assertEquals(MergeStrategy.RESOLVE, strategy);
+			assertEquals(e.getReason(),
+					MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
+		}
+	}
+
+	@Theory
+	/**
+	 * Merging m2,s2 from the following topology. The same file is modified
+	 * in both branches. The modifications should be mergeable. m2 and s2
+	 * contain branch specific conflict resolutions. Therefore m2 and don't contain the same content.
+	 *
+	 * <pre>
+	 * m0--m1--m2
+	 *   \   \/
+	 *    \  /\
+	 *     s1--s2
+	 * </pre>
+	 */
+	public void crissCrossMerge_mergeable(MergeStrategy strategy,
+			IndexState indexState, WorktreeState worktreeState)
+			throws Exception {
+		if (!validateStates(indexState, worktreeState))
+			return;
+
+		BranchBuilder master = db_t.branch("master");
+		RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
+				.message("m0").create();
+		RevCommit m1 = master.commit()
+				.add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
+				.create();
+		db_t.getRevWalk().parseCommit(m1);
+
+		BranchBuilder side = db_t.branch("side");
+		RevCommit s1 = side.commit().parent(m0)
+				.add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
+				.create();
+		RevCommit s2 = side.commit().parent(m1)
+				.add("f", "1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n")
+				.message("s2(merge)").create();
+		RevCommit m2 = master
+				.commit()
+				.parent(s1)
+				.add("f", "1-master\n2\n3-res(master)\n4\n5\n6\n7\n8\n9-side\n")
+				.message("m2(merge)").create();
+
+		Git git = Git.wrap(db);
+		git.checkout().setName("master").call();
+		modifyWorktree(worktreeState, "f", "side");
+		modifyIndex(indexState, "f", "side");
+
+		ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
+				worktreeState == WorktreeState.Bare);
+		if (worktreeState != WorktreeState.Bare)
+			merger.setWorkingTreeIterator(new FileTreeIterator(db));
+		try {
+			boolean expectSuccess = true;
+			if (!(indexState == IndexState.Bare
+					|| indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
+				// index is dirty
+				expectSuccess = false;
+			else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
+					|| worktreeState == WorktreeState.SameAsOther)
+				expectSuccess = false;
+			assertEquals(Boolean.valueOf(expectSuccess),
+					Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
+			assertEquals(MergeStrategy.RECURSIVE, strategy);
+			if (!expectSuccess)
+				// if the merge was not successful skip testing the state of index and workingtree
+				return;
+			assertEquals(
+					"1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side",
+					contentAsString(db, merger.getResultTreeId(), "f"));
+			if (indexState != IndexState.Bare)
+				assertEquals(
+						"[f, mode:100644, content:1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side\n]",
+						indexState(RepositoryTestCase.CONTENT));
+			if (worktreeState != WorktreeState.Bare
+					&& worktreeState != WorktreeState.Missing)
+				assertEquals(
+						"1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side\n",
+						read("f"));
+		} catch (NoMergeBaseException e) {
+			assertEquals(MergeStrategy.RESOLVE, strategy);
+			assertEquals(e.getReason(),
+					MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
+		}
+	}
+
+	@Theory
+	/**
+	 * Merging m2,s2 from the following topology. The same file is modified
+	 * in both branches. The modifications are not automatically
+	 * mergeable. m2 and s2 contain branch specific conflict resolutions.
+	 * Therefore m2 and s2 don't contain the same content.
+	 *
+	 * <pre>
+	 * m0--m1--m2
+	 *   \   \/
+	 *    \  /\
+	 *     s1--s2
+	 * </pre>
+	 */
+	public void crissCrossMerge_nonmergeable(MergeStrategy strategy,
+			IndexState indexState, WorktreeState worktreeState)
+			throws Exception {
+		if (!validateStates(indexState, worktreeState))
+			return;
+
+		BranchBuilder master = db_t.branch("master");
+		RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
+				.message("m0").create();
+		RevCommit m1 = master.commit()
+				.add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
+				.create();
+		db_t.getRevWalk().parseCommit(m1);
+
+		BranchBuilder side = db_t.branch("side");
+		RevCommit s1 = side.commit().parent(m0)
+				.add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
+				.create();
+		RevCommit s2 = side.commit().parent(m1)
+				.add("f", "1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n")
+				.message("s2(merge)").create();
+		RevCommit m2 = master.commit().parent(s1)
+				.add("f", "1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n")
+				.message("m2(merge)").create();
+
+		Git git = Git.wrap(db);
+		git.checkout().setName("master").call();
+		modifyWorktree(worktreeState, "f", "side");
+		modifyIndex(indexState, "f", "side");
+
+		ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
+				worktreeState == WorktreeState.Bare);
+		if (worktreeState != WorktreeState.Bare)
+			merger.setWorkingTreeIterator(new FileTreeIterator(db));
+		try {
+			assertFalse(merger.merge(new RevCommit[] { m2, s2 }));
+			assertEquals(MergeStrategy.RECURSIVE, strategy);
+			if (indexState == IndexState.SameAsHead
+					&& worktreeState == WorktreeState.SameAsHead) {
+				assertEquals(
+						"[f, mode:100644, stage:1, content:1-master\n2\n3\n4\n5\n6\n7\n8\n9-side\n]"
+								+ "[f, mode:100644, stage:2, content:1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n]"
+								+ "[f, mode:100644, stage:3, content:1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n]",
+						indexState(RepositoryTestCase.CONTENT));
+				assertEquals(
+						"1-master\n2\n3\n4\n5\n6\n<<<<<<< OURS\n7-conflict\n=======\n7-res(side)\n>>>>>>> THEIRS\n8\n9-side\n",
+						read("f"));
+			}
+		} catch (NoMergeBaseException e) {
+			assertEquals(MergeStrategy.RESOLVE, strategy);
+			assertEquals(e.getReason(),
+					MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
+		}
+	}
+
+	@Theory
+	/**
+	 * Merging m2,s2 which have three common predecessors.The same file is modified
+	 * in all branches. The modifications should be mergeable. m2 and s2
+	 * contain branch specific conflict resolutions. Therefore m2 and s2
+	 * don't contain the same content.
+	 *
+	 * <pre>
+	 *     m1-----m2
+	 *    /  \/  /
+	 *   /   /\ /
+	 * m0--o1  x
+	 *   \   \/ \
+	 *    \  /\  \
+	 *     s1-----s2
+	 * </pre>
+	 */
+	public void crissCrossMerge_ThreeCommonPredecessors(MergeStrategy strategy,
+			IndexState indexState, WorktreeState worktreeState)
+			throws Exception {
+		if (!validateStates(indexState, worktreeState))
+			return;
+
+		BranchBuilder master = db_t.branch("master");
+		RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
+				.message("m0").create();
+		RevCommit m1 = master.commit()
+				.add("f", "1-master\n2\n3\n4\n5\n6\n7\n8\n9\n").message("m1")
+				.create();
+		BranchBuilder side = db_t.branch("side");
+		RevCommit s1 = side.commit().parent(m0)
+				.add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9-side\n").message("s1")
+				.create();
+		BranchBuilder other = db_t.branch("other");
+		RevCommit o1 = other.commit().parent(m0)
+				.add("f", "1\n2\n3\n4\n5-other\n6\n7\n8\n9\n").message("o1")
+				.create();
+
+		RevCommit m2 = master
+				.commit()
+				.parent(s1)
+				.parent(o1)
+				.add("f",
+						"1-master\n2\n3-res(master)\n4\n5-other\n6\n7\n8\n9-side\n")
+				.message("m2(merge)").create();
+
+		RevCommit s2 = side
+				.commit()
+				.parent(m1)
+				.parent(o1)
+				.add("f",
+						"1-master\n2\n3\n4\n5-other\n6\n7-res(side)\n8\n9-side\n")
+				.message("s2(merge)").create();
+
+		Git git = Git.wrap(db);
+		git.checkout().setName("master").call();
+		modifyWorktree(worktreeState, "f", "side");
+		modifyIndex(indexState, "f", "side");
+
+		ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
+				worktreeState == WorktreeState.Bare);
+		if (worktreeState != WorktreeState.Bare)
+			merger.setWorkingTreeIterator(new FileTreeIterator(db));
+		try {
+			boolean expectSuccess = true;
+			if (!(indexState == IndexState.Bare
+					|| indexState == IndexState.Missing || indexState == IndexState.SameAsHead))
+				// index is dirty
+				expectSuccess = false;
+			else if (worktreeState == WorktreeState.DifferentFromHeadAndOther
+					|| worktreeState == WorktreeState.SameAsOther)
+				// workingtree is dirty
+				expectSuccess = false;
+
+			assertEquals(Boolean.valueOf(expectSuccess),
+					Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
+			assertEquals(MergeStrategy.RECURSIVE, strategy);
+			if (!expectSuccess)
+				// if the merge was not successful skip testing the state of index and workingtree
+				return;
+			assertEquals(
+					"1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side",
+					contentAsString(db, merger.getResultTreeId(), "f"));
+			if (indexState != IndexState.Bare)
+				assertEquals(
+						"[f, mode:100644, content:1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side\n]",
+						indexState(RepositoryTestCase.CONTENT));
+			if (worktreeState != WorktreeState.Bare
+					&& worktreeState != WorktreeState.Missing)
+				assertEquals(
+						"1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side\n",
+						read("f"));
+		} catch (NoMergeBaseException e) {
+			assertEquals(MergeStrategy.RESOLVE, strategy);
+			assertEquals(e.getReason(),
+					MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
+		}
+	}
+
+	void modifyIndex(IndexState indexState, String path, String other)
+			throws Exception {
+		RevBlob blob;
+		switch (indexState) {
+		case Missing:
+			setIndex(null, path);
+			break;
+		case SameAsHead:
+			setIndex(contentId(Constants.HEAD, path), path);
+			break;
+		case SameAsOther:
+			setIndex(contentId(other, path), path);
+			break;
+		case SameAsWorkTree:
+			blob = db_t.blob(read(path));
+			setIndex(blob, path);
+			break;
+		case DifferentFromHeadAndOtherAndWorktree:
+			blob = db_t.blob(Integer.toString(counter++));
+			setIndex(blob, path);
+			break;
+		case Bare:
+			File file = new File(db.getDirectory(), "index");
+			if (!file.exists())
+				return;
+			db.close();
+			file.delete();
+			db = new FileRepository(db.getDirectory());
+			db_t = new TestRepository<FileRepository>(db);
+			break;
+		}
+	}
+
+	private void setIndex(final ObjectId id, String path)
+			throws MissingObjectException, IOException {
+		DirCache lockedDircache;
+		DirCacheEditor dcedit;
+
+		lockedDircache = db.lockDirCache();
+		dcedit = lockedDircache.editor();
+		try {
+			if (id != null) {
+				final ObjectLoader contLoader = db.newObjectReader().open(id);
+				dcedit.add(new DirCacheEditor.PathEdit(path) {
+					@Override
+					public void apply(DirCacheEntry ent) {
+						ent.setFileMode(FileMode.REGULAR_FILE);
+						ent.setLength(contLoader.getSize());
+						ent.setObjectId(id);
+					}
+				});
+			} else
+				dcedit.add(new DirCacheEditor.DeletePath(path));
+		} finally {
+			dcedit.commit();
+		}
+	}
+
+	private ObjectId contentId(String revName, String path) throws Exception {
+		RevCommit headCommit = db_t.getRevWalk().parseCommit(
+				db.resolve(revName));
+		db_t.parseBody(headCommit);
+		return db_t.get(headCommit.getTree(), path).getId();
+	}
+
+	void modifyWorktree(WorktreeState worktreeState, String path, String other)
+			throws Exception {
+		FileOutputStream fos = null;
+		ObjectId bloblId;
+
+		try {
+			switch (worktreeState) {
+			case Missing:
+				new File(db.getWorkTree(), path).delete();
+				break;
+			case DifferentFromHeadAndOther:
+				write(new File(db.getWorkTree(), path),
+						Integer.toString(counter++));
+				break;
+			case SameAsHead:
+				bloblId = contentId(Constants.HEAD, path);
+				fos = new FileOutputStream(new File(db.getWorkTree(), path));
+				db.newObjectReader().open(bloblId).copyTo(fos);
+				break;
+			case SameAsOther:
+				bloblId = contentId(other, path);
+				fos = new FileOutputStream(new File(db.getWorkTree(), path));
+				db.newObjectReader().open(bloblId).copyTo(fos);
+				break;
+			case Bare:
+				if (db.isBare())
+					return;
+				File workTreeFile = db.getWorkTree();
+				db.getConfig().setBoolean("core", null, "bare", true);
+				db.getDirectory().renameTo(new File(workTreeFile, "test.git"));
+				db = new FileRepository(new File(workTreeFile, "test.git"));
+				db_t = new TestRepository<FileRepository>(db);
+			}
+		} finally {
+			if (fos != null)
+				fos.close();
+		}
+	}
+
+	private boolean validateStates(IndexState indexState,
+			WorktreeState worktreeState) {
+		if (worktreeState == WorktreeState.Bare
+				&& indexState != IndexState.Bare)
+			return false;
+		if (worktreeState != WorktreeState.Bare
+				&& indexState == IndexState.Bare)
+			return false;
+		if (worktreeState != WorktreeState.DifferentFromHeadAndOther
+				&& indexState == IndexState.SameAsWorkTree)
+			// would be a duplicate: the combination WorktreeState.X and
+			// IndexState.X already covered this
+			return false;
+		return true;
+	}
+
+	private String contentAsString(Repository r, ObjectId treeId, String path)
+			throws MissingObjectException, IOException {
+		TreeWalk tw = new TreeWalk(r);
+		tw.addTree(treeId);
+		tw.setFilter(PathFilter.create(path));
+		tw.setRecursive(true);
+		if (!tw.next())
+			return null;
+		AnyObjectId blobId = tw.getObjectId(0);
+
+		StringBuilder result = new StringBuilder();
+		BufferedReader br = null;
+		ObjectReader or = r.newObjectReader();
+		try {
+			br = new BufferedReader(new InputStreamReader(or.open(blobId)
+					.openStream()));
+			String line;
+			boolean first = true;
+			while ((line = br.readLine()) != null) {
+				if (!first)
+					result.append('\n');
+				result.append(line);
+				first = false;
+			}
+			return result.toString();
+		} finally {
+			if (br != null)
+				br.close();
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
index 8c10c73..7276243 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
@@ -54,7 +54,10 @@ import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.MergeResult;
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 import org.eclipse.jgit.api.errors.CheckoutConflictException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.errors.NoMergeBaseException;
+import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -72,6 +75,9 @@ public class ResolveMergerTest extends RepositoryTestCase {
 	@DataPoint
 	public static MergeStrategy resolve = MergeStrategy.RESOLVE;
 
+	@DataPoint
+	public static MergeStrategy recursive = MergeStrategy.RECURSIVE;
+
 	@Theory
 	public void failingPathsShouldNotResultInOKReturnValue(
 			MergeStrategy strategy) throws Exception {
@@ -183,6 +189,83 @@ public class ResolveMergerTest extends RepositoryTestCase {
 	}
 
 	/**
+	 * An existing directory without tracked content should not prevent merging
+	 * a tree where that directory exists.
+	 *
+	 * @param strategy
+	 * @throws Exception
+	 */
+	@Theory
+	public void checkUntrackedFolderIsNotAConflict(
+			MergeStrategy strategy) throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("d/1", "1");
+		git.add().addFilepattern("d/1").call();
+		RevCommit first = git.commit().setMessage("added d/1").call();
+
+		writeTrashFile("e/1", "4");
+		git.add().addFilepattern("e/1").call();
+		RevCommit masterCommit = git.commit().setMessage("added e/1").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("f/1", "5");
+		git.add().addFilepattern("f/1").call();
+		git.commit().setAll(true).setMessage("added f/1")
+				.call();
+
+		// Untracked directory e shall not conflict with merged e/1
+		writeTrashFile("e/2", "d two");
+
+		MergeResult mergeRes = git.merge().setStrategy(strategy)
+				.include(masterCommit).call();
+		assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus());
+		assertEquals(
+				"[d/1, mode:100644, content:1][e/1, mode:100644, content:4][f/1, mode:100644, content:5]",
+				indexState(CONTENT));
+	}
+
+	/**
+	 * An existing directory without tracked content should not prevent merging
+	 * a file with that name.
+	 *
+	 * @param strategy
+	 * @throws Exception
+	 */
+	@Theory
+	public void checkUntrackedEmpytFolderIsNotAConflictWithFile(
+			MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("d/1", "1");
+		git.add().addFilepattern("d/1").call();
+		RevCommit first = git.commit().setMessage("added d/1").call();
+
+		writeTrashFile("e", "4");
+		git.add().addFilepattern("e").call();
+		RevCommit masterCommit = git.commit().setMessage("added e").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("f/1", "5");
+		git.add().addFilepattern("f/1").call();
+		git.commit().setAll(true).setMessage("added f/1").call();
+
+		// Untracked empty directory hierarcy e/1 shall not conflict with merged
+		// e/1
+		FileUtils.mkdirs(new File(trash, "e/1"), true);
+
+		MergeResult mergeRes = git.merge().setStrategy(strategy)
+				.include(masterCommit).call();
+		assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus());
+		assertEquals(
+				"[d/1, mode:100644, content:1][e, mode:100644, content:4][f/1, mode:100644, content:5]",
+				indexState(CONTENT));
+	}
+
+	/**
 	 * Merging two equal subtrees when the index does not contain any file in
 	 * that subtree should lead to a merged state.
 	 *
@@ -396,6 +479,66 @@ public class ResolveMergerTest extends RepositoryTestCase {
 		}
 	}
 
+	/**
+	 * Merging after criss-cross merges. In this case we merge together two
+	 * commits which have two equally good common ancestors
+	 *
+	 * @param strategy
+	 * @throws Exception
+	 */
+	@Theory
+	public void checkMergeCrissCross(MergeStrategy strategy) throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("1", "1\n2\n3");
+		git.add().addFilepattern("1").call();
+		RevCommit first = git.commit().setMessage("added 1").call();
+
+		writeTrashFile("1", "1master\n2\n3");
+		RevCommit masterCommit = git.commit().setAll(true)
+				.setMessage("modified 1 on master").call();
+
+		writeTrashFile("1", "1master2\n2\n3");
+		git.commit().setAll(true)
+				.setMessage("modified 1 on master again").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("1", "1\n2\na\nb\nc\n3side");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified 1 on side").call();
+
+		writeTrashFile("1", "1\n2\n3side2");
+		git.commit().setAll(true)
+				.setMessage("modified 1 on side again").call();
+
+		MergeResult result = git.merge().setStrategy(strategy)
+				.include(masterCommit).call();
+		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+		result.getNewHead();
+		git.checkout().setName("master").call();
+		result = git.merge().setStrategy(strategy).include(sideCommit).call();
+		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+
+		// we have two branches which are criss-cross merged. Try to merge the
+		// tips. This should succeed with RecursiveMerge and fail with
+		// ResolveMerge
+		try {
+			MergeResult mergeResult = git.merge().setStrategy(strategy)
+					.include(git.getRepository().getRef("refs/heads/side"))
+					.call();
+			assertEquals(MergeStrategy.RECURSIVE, strategy);
+			assertEquals(MergeResult.MergeStatus.MERGED,
+					mergeResult.getMergeStatus());
+			assertEquals("1master2\n2\n3side2\n", read("1"));
+		} catch (JGitInternalException e) {
+			assertEquals(MergeStrategy.RESOLVE, strategy);
+			assertTrue(e.getCause() instanceof NoMergeBaseException);
+			assertEquals(((NoMergeBaseException) e.getCause()).getReason(),
+					MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
+		}
+	}
+
 	@Theory
 	public void checkLockedFilesToBeDeleted(MergeStrategy strategy)
 			throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
index 1a1482c..cae006c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
@@ -46,7 +46,7 @@ package org.eclipse.jgit.nls;
 import org.eclipse.jgit.awtui.UIText;
 import org.eclipse.jgit.console.ConsoleText;
 import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.pgm.CLIText;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
index 1f70a71..05e552e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
@@ -47,8 +47,10 @@ import static org.junit.Assert.assertNull;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.jgit.diff.DiffConfig;
 import org.eclipse.jgit.diff.DiffEntry;
 import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -74,7 +76,8 @@ public class RevWalkFollowFilterTest extends RevWalkTestCase {
 	}
 
 	protected FollowFilter follow(final String followPath) {
-		FollowFilter followFilter = FollowFilter.create(followPath);
+		FollowFilter followFilter =
+			FollowFilter.create(followPath, new Config().get(DiffConfig.KEY));
 		followFilter.setRenameCallback(diffCollector);
 		rw.setTreeFilter(followFilter);
 		return followFilter;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java
index 479d9d3..3e45d2e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkShallowTest.java
@@ -189,7 +189,7 @@ public class RevWalkShallowTest extends RevWalkTestCase {
 		final StringBuilder builder = new StringBuilder();
 		for (ObjectId commit : shallowCommits)
 			builder.append(commit.getName() + "\n");
-		JGitTestUtil.write(new File(rw.repository.getDirectory(), "shallow"),
+		JGitTestUtil.write(new File(db.getDirectory(), "shallow"),
 				builder.toString());
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
index 29c953f..f44f67f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
@@ -70,7 +70,6 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
@@ -81,12 +80,12 @@ import org.junit.Test;
  * Unit tests of {@link SubmoduleWalk}
  */
 public class SubmoduleWalkTest extends RepositoryTestCase {
-	private TestRepository<FileRepository> testDb;
+	private TestRepository<Repository> testDb;
 
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		testDb = new TestRepository<FileRepository>(db);
+		testDb = new TestRepository<Repository>(db);
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
index bb71e72..6859dd5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
@@ -62,6 +62,8 @@ import java.util.zip.Deflater;
 
 import org.eclipse.jgit.errors.TooLargeObjectInPackException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectoryPackParser;
+import org.eclipse.jgit.internal.storage.file.PackFile;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
@@ -71,8 +73,6 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevBlob;
-import org.eclipse.jgit.storage.file.ObjectDirectoryPackParser;
-import org.eclipse.jgit.storage.file.PackFile;
 import org.eclipse.jgit.util.NB;
 import org.eclipse.jgit.util.TemporaryBuffer;
 import org.eclipse.jgit.util.io.UnionInputStream;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
index 69824b2..e523db9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
@@ -48,6 +48,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -432,6 +433,12 @@ public class PushProcessTest extends SampleDataRepositoryTestCase {
 		}
 
 		public void push(ProgressMonitor monitor,
+				Map<String, RemoteRefUpdate> refsToUpdate, OutputStream out)
+				throws TransportException {
+			push(monitor, refsToUpdate);
+		}
+
+		public void push(ProgressMonitor monitor,
 				Map<String, RemoteRefUpdate> refsToUpdate)
 				throws TransportException {
 			for (final RemoteRefUpdate rru : refsToUpdate.values()) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
index 8c3ce61..2bd9077 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
@@ -62,6 +62,8 @@ import java.util.zip.Deflater;
 
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.UnpackException;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Constants;
@@ -75,8 +77,6 @@ import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.ObjectDirectory;
-import org.eclipse.jgit.storage.pack.BinaryDelta;
 import org.eclipse.jgit.util.NB;
 import org.eclipse.jgit.util.TemporaryBuffer;
 import org.junit.After;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
index 871741f..3f5fcbb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg at dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2013, Robin Stocker <robin at nibor.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -46,6 +47,7 @@ package org.eclipse.jgit.transport;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
@@ -294,4 +296,152 @@ public class RefSpecTest {
 		assertEquals(src, r.getSource());
 		assertEquals(dst, r.getDestination());
 	}
+
+	@Test
+	public void isWildcardShouldWorkForWildcardSuffixAndComponent() {
+		assertTrue(RefSpec.isWildcard("refs/heads/*"));
+		assertTrue(RefSpec.isWildcard("refs/pull/*/head"));
+		assertFalse(RefSpec.isWildcard("refs/heads/a"));
+	}
+
+	@Test
+	public void testWildcardInMiddleOfSource() {
+		RefSpec a = new RefSpec("+refs/pull/*/head:refs/remotes/origin/pr/*");
+		assertTrue(a.isWildcard());
+		assertTrue(a.matchSource("refs/pull/a/head"));
+		assertTrue(a.matchSource("refs/pull/foo/head"));
+		assertTrue(a.matchSource("refs/pull/foo/bar/head"));
+		assertFalse(a.matchSource("refs/pull/foo"));
+		assertFalse(a.matchSource("refs/pull/head"));
+		assertFalse(a.matchSource("refs/pull/foo/head/more"));
+		assertFalse(a.matchSource("refs/pullx/head"));
+
+		RefSpec b = a.expandFromSource("refs/pull/foo/head");
+		assertEquals("refs/remotes/origin/pr/foo", b.getDestination());
+		RefSpec c = a.expandFromDestination("refs/remotes/origin/pr/foo");
+		assertEquals("refs/pull/foo/head", c.getSource());
+	}
+
+	@Test
+	public void testWildcardInMiddleOfDestionation() {
+		RefSpec a = new RefSpec("+refs/heads/*:refs/remotes/origin/*/head");
+		assertTrue(a.isWildcard());
+		assertTrue(a.matchDestination("refs/remotes/origin/a/head"));
+		assertTrue(a.matchDestination("refs/remotes/origin/foo/head"));
+		assertTrue(a.matchDestination("refs/remotes/origin/foo/bar/head"));
+		assertFalse(a.matchDestination("refs/remotes/origin/foo"));
+		assertFalse(a.matchDestination("refs/remotes/origin/head"));
+		assertFalse(a.matchDestination("refs/remotes/origin/foo/head/more"));
+		assertFalse(a.matchDestination("refs/remotes/originx/head"));
+
+		RefSpec b = a.expandFromSource("refs/heads/foo");
+		assertEquals("refs/remotes/origin/foo/head", b.getDestination());
+		RefSpec c = a.expandFromDestination("refs/remotes/origin/foo/head");
+		assertEquals("refs/heads/foo", c.getSource());
+	}
+
+	@Test
+	public void testWildcardMirror() {
+		RefSpec a = new RefSpec("*:*");
+		assertTrue(a.isWildcard());
+		assertTrue(a.matchSource("a"));
+		assertTrue(a.matchSource("foo"));
+		assertTrue(a.matchSource("foo/bar"));
+		assertTrue(a.matchDestination("a"));
+		assertTrue(a.matchDestination("foo"));
+		assertTrue(a.matchDestination("foo/bar"));
+
+		RefSpec b = a.expandFromSource("refs/heads/foo");
+		assertEquals("refs/heads/foo", b.getDestination());
+		RefSpec c = a.expandFromDestination("refs/heads/foo");
+		assertEquals("refs/heads/foo", c.getSource());
+	}
+
+	@Test
+	public void testWildcardAtStart() {
+		RefSpec a = new RefSpec("*/head:refs/heads/*");
+		assertTrue(a.isWildcard());
+		assertTrue(a.matchSource("a/head"));
+		assertTrue(a.matchSource("foo/head"));
+		assertTrue(a.matchSource("foo/bar/head"));
+		assertFalse(a.matchSource("/head"));
+		assertFalse(a.matchSource("a/head/extra"));
+
+		RefSpec b = a.expandFromSource("foo/head");
+		assertEquals("refs/heads/foo", b.getDestination());
+		RefSpec c = a.expandFromDestination("refs/heads/foo");
+		assertEquals("foo/head", c.getSource());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenSourceOnlyAndWildcard() {
+		assertNotNull(new RefSpec("refs/heads/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenDestinationOnlyAndWildcard() {
+		assertNotNull(new RefSpec(":refs/heads/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenOnlySourceWildcard() {
+		assertNotNull(new RefSpec("refs/heads/*:refs/heads/foo"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenOnlyDestinationWildcard() {
+		assertNotNull(new RefSpec("refs/heads/foo:refs/heads/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenMoreThanOneWildcardInSource() {
+		assertNotNull(new RefSpec("refs/heads/*/*:refs/heads/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenMoreThanOneWildcardInDestination() {
+		assertNotNull(new RefSpec("refs/heads/*:refs/heads/*/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenWildcardAfterText() {
+		assertNotNull(new RefSpec("refs/heads/wrong*:refs/heads/right/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenWildcardBeforeText() {
+		assertNotNull(new RefSpec("*wrong:right/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidWhenWildcardBeforeTextAtEnd() {
+		assertNotNull(new RefSpec("refs/heads/*wrong:right/*"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidSourceDoubleSlashes() {
+		assertNotNull(new RefSpec("refs/heads//wrong"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidSlashAtStart() {
+		assertNotNull(new RefSpec("/foo:/foo"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidDestinationDoubleSlashes() {
+		assertNotNull(new RefSpec(":refs/heads//wrong"));
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidSetSource() {
+		RefSpec a = new RefSpec("refs/heads/*:refs/remotes/origin/*");
+		a.setSource("refs/heads/*/*");
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void invalidSetDestination() {
+		RefSpec a = new RefSpec("refs/heads/*:refs/remotes/origin/*");
+		a.setDestination("refs/remotes/origin/*/*");
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
index 60310b3..aba4e6a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
@@ -58,7 +58,7 @@ import java.util.List;
 import org.eclipse.jgit.junit.SampleDataRepositoryTestCase;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.lib.Repository;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -216,7 +216,7 @@ public class TransportTest extends SampleDataRepositoryTestCase {
 
 	@Test
 	public void testLocalTransportWithRelativePath() throws Exception {
-		FileRepository other = createWorkRepository();
+		Repository other = createWorkRepository();
 		String otherDir = other.getWorkTree().getName();
 
 		RemoteConfig config = new RemoteConfig(db.getConfig(), "other");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index b5a753b..8c7c992 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -2,6 +2,7 @@
  * Copyright (C) 2009, Mykola Nikishov <mn at mn.com.ua>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg at dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2013, Robin Stocker <robin at nibor.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -611,7 +612,7 @@ public class URIishTest {
 
 	@Test
 	public void testGetTwoSlashesDotGitHumanishName() throws URISyntaxException {
-		assertEquals("", new URIish("/.git").getHumanishName());
+		assertEquals("", new URIish("//.git").getHumanishName());
 	}
 
 	@Test
@@ -646,7 +647,7 @@ public class URIishTest {
 	@Test
 	public void testGetSlashSlashDotGitSlashHumanishName()
 			throws IllegalArgumentException, URISyntaxException {
-		final String humanishName = new URIish(GIT_SCHEME + "/abc//.git")
+		final String humanishName = new URIish(GIT_SCHEME + "/.git")
 				.getHumanishName();
 		assertEquals("may return an empty humanish name", "", humanishName);
 	}
@@ -703,6 +704,21 @@ public class URIishTest {
 	}
 
 	@Test
+	public void testGetValidLocalWithTwoSlashesHumanishName()
+			throws IllegalArgumentException, URISyntaxException {
+		String humanishName = new URIish("/a/b/c//").getHumanishName();
+		assertEquals("c", humanishName);
+	}
+
+	@Test
+	public void testGetValidGitSchemeWithTwoSlashesHumanishName()
+			throws IllegalArgumentException, URISyntaxException {
+		String humanishName = new URIish(GIT_SCHEME + "/a/b/c//")
+				.getHumanishName();
+		assertEquals("c", humanishName);
+	}
+
+	@Test
 	public void testGetWindowsPathHumanishName()
 			throws IllegalArgumentException,
 			URISyntaxException {
@@ -822,7 +838,7 @@ public class URIishTest {
 		String[] users = new String[] { "me", "l usr\\example.com",
 				"lusr\\example" };
 		String[] passes = new String[] { "wtf", };
-		String[] hosts = new String[] { "example.com", "1.2.3.4" };
+		String[] hosts = new String[] { "example.com", "1.2.3.4", "[::1]" };
 		String[] ports = new String[] { "1234", "80" };
 		String[] paths = new String[] { "/", "/abc", "D:/x", "D:\\x" };
 		for (String[] test : tests) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
index 13fe426..b3aa0ee 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
@@ -50,21 +50,21 @@ import java.io.IOException;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.dircache.DirCacheIterator;
-import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.junit.Before;
 import org.junit.Test;
 
 public class InterIndexDiffFilterTest extends LocalDiskRepositoryTestCase {
 
-	private FileRepository db;
+	private Repository db;
 
 	@Before
 	public void setUp() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
index 8038206..4c329cb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
@@ -76,7 +76,8 @@ public class PathFilterGroupTest {
 				"b/c",
 				"c/d/e",
 				"c/d/f",
-				"d/e/f/g"
+				"d/e/f/g",
+				"d/e/f/g.x"
 				};
 		// @formatter:on
 		filter = PathFilterGroup.createFromStrings(paths);
@@ -90,6 +91,7 @@ public class PathFilterGroupTest {
 		assertTrue(filter.include(fakeWalk("c/d/e")));
 		assertTrue(filter.include(fakeWalk("c/d/f")));
 		assertTrue(filter.include(fakeWalk("d/e/f/g")));
+		assertTrue(filter.include(fakeWalk("d/e/f/g.x")));
 	}
 
 	@Test
@@ -132,6 +134,11 @@ public class PathFilterGroupTest {
 		assertTrue(filter.include(fakeWalk("c/d/e/f")));
 		assertTrue(filter.include(fakeWalk("c/d/f/g")));
 		assertTrue(filter.include(fakeWalk("d/e/f/g/h")));
+		assertTrue(filter.include(fakeWalk("d/e/f/g/y")));
+		assertTrue(filter.include(fakeWalk("d/e/f/g.x/h")));
+		// listed before g/y, so can't StopWalk here, but it's not included
+		// either
+		assertFalse(filter.include(fakeWalk("d/e/f/g.y")));
 	}
 
 	@Test
@@ -172,6 +179,9 @@ public class PathFilterGroupTest {
 			// good
 		}
 
+		// less obvious #2 due to git sorting order
+		filter.include(fakeWalk("d/e/f/g/h.txt"));
+
 		// non-ascii
 		try {
 			filter.include(fakeWalk("\u00C0"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
index 08e7557..df39f2b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Matthias Sohn <matthias.sohn at sap.com>
+ * Copyright (C) 2010, 2013 Matthias Sohn <matthias.sohn at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,6 +43,7 @@
 
 package org.eclipse.jgit.util;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -50,6 +51,7 @@ import static org.junit.Assert.fail;
 import java.io.File;
 import java.io.IOException;
 
+import org.eclipse.jgit.junit.JGitTestUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -115,6 +117,121 @@ public class FileUtilTest {
 	}
 
 	@Test
+
+	public void testDeleteRecursiveEmpty() throws IOException {
+		File f1 = new File(trash, "test/test/a");
+		File f2 = new File(trash, "test/a");
+		File d1 = new File(trash, "test");
+		File d2 = new File(trash, "test/test");
+		File d3 = new File(trash, "test/b");
+		FileUtils.mkdirs(f1.getParentFile());
+		FileUtils.createNewFile(f2);
+		FileUtils.createNewFile(f1);
+		FileUtils.mkdirs(d3);
+
+		// Cannot delete hierarchy since files exist
+		try {
+			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY);
+			fail("delete should fail");
+		} catch (IOException e1) {
+			try {
+				FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY|FileUtils.RECURSIVE);
+				fail("delete should fail");
+			} catch (IOException e2) {
+				// Everything still there
+				assertTrue(f1.exists());
+				assertTrue(f2.exists());
+				assertTrue(d1.exists());
+				assertTrue(d2.exists());
+				assertTrue(d3.exists());
+			}
+		}
+
+		// setup: delete files, only directories left
+		assertTrue(f1.delete());
+		assertTrue(f2.delete());
+
+		// Shall not delete hierarchy without recursive
+		try {
+			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY);
+			fail("delete should fail");
+		} catch (IOException e2) {
+			// Everything still there
+			assertTrue(d1.exists());
+			assertTrue(d2.exists());
+			assertTrue(d3.exists());
+		}
+
+		// Now delete the empty hierarchy
+		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
+				| FileUtils.RECURSIVE);
+		assertFalse(d2.exists());
+
+		// Will fail to delete non-existing without SKIP_MISSING
+		try {
+			FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY);
+			fail("Cannot delete non-existent entity");
+		} catch (IOException e) {
+			// ok
+		}
+
+		// ..with SKIP_MISSING there is no exception
+		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
+				| FileUtils.SKIP_MISSING);
+		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
+				| FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
+
+		// essentially the same, using IGNORE_ERRORS
+		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
+				| FileUtils.IGNORE_ERRORS);
+		FileUtils.delete(d2, FileUtils.EMPTY_DIRECTORIES_ONLY
+				| FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS);
+	}
+
+	@Test
+	public void testDeleteRecursiveEmptyNeedsToCheckFilesFirst()
+			throws IOException {
+		File d1 = new File(trash, "test");
+		File d2 = new File(trash, "test/a");
+		File d3 = new File(trash, "test/b");
+		File f1 = new File(trash, "test/c");
+		File d4 = new File(trash, "test/d");
+		FileUtils.mkdirs(d1);
+		FileUtils.mkdirs(d2);
+		FileUtils.mkdirs(d3);
+		FileUtils.mkdirs(d4);
+		FileUtils.createNewFile(f1);
+
+		// Cannot delete hierarchy since file exists
+		try {
+			FileUtils.delete(d1, FileUtils.EMPTY_DIRECTORIES_ONLY
+					| FileUtils.RECURSIVE);
+			fail("delete should fail");
+		} catch (IOException e) {
+			// Everything still there
+			assertTrue(f1.exists());
+			assertTrue(d1.exists());
+			assertTrue(d2.exists());
+			assertTrue(d3.exists());
+			assertTrue(d4.exists());
+		}
+	}
+
+	@Test
+	public void testDeleteRecursiveEmptyDirectoriesOnlyButIsFile()
+			throws IOException {
+		File f1 = new File(trash, "test/test/a");
+		FileUtils.mkdirs(f1.getParentFile());
+		FileUtils.createNewFile(f1);
+		try {
+			FileUtils.delete(f1, FileUtils.EMPTY_DIRECTORIES_ONLY);
+			fail("delete should fail");
+		} catch (IOException e) {
+			assertTrue(f1.exists());
+		}
+	}
+
+	@Test
 	public void testMkdir() throws IOException {
 		File d = new File(trash, "test");
 		FileUtils.mkdir(d);
@@ -175,6 +292,7 @@ public class FileUtilTest {
 		assertTrue(f.delete());
 	}
 
+	@Test
 	public void testCreateNewFile() throws IOException {
 		File f = new File(trash, "x");
 		FileUtils.createNewFile(f);
@@ -190,4 +308,129 @@ public class FileUtilTest {
 		FileUtils.delete(f);
 	}
 
+	@Test
+	public void testDeleteEmptyTreeOk() throws IOException {
+		File t = new File(trash, "t");
+		FileUtils.mkdir(t);
+		FileUtils.mkdir(new File(t, "d"));
+		FileUtils.mkdir(new File(new File(t, "d"), "e"));
+		FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE);
+		assertFalse(t.exists());
+	}
+
+	@Test
+	public void testDeleteNotEmptyTreeNotOk() throws IOException {
+		File t = new File(trash, "t");
+		FileUtils.mkdir(t);
+		FileUtils.mkdir(new File(t, "d"));
+		File f = new File(new File(t, "d"), "f");
+		FileUtils.createNewFile(f);
+		FileUtils.mkdir(new File(new File(t, "d"), "e"));
+		try {
+			FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE);
+			fail("expected failure to delete f");
+		} catch (IOException e) {
+			assertTrue(e.getMessage().endsWith(f.getAbsolutePath()));
+		}
+		assertTrue(t.exists());
+	}
+
+	@Test
+	public void testDeleteNotEmptyTreeNotOkButIgnoreFail() throws IOException {
+		File t = new File(trash, "t");
+		FileUtils.mkdir(t);
+		FileUtils.mkdir(new File(t, "d"));
+		File f = new File(new File(t, "d"), "f");
+		FileUtils.createNewFile(f);
+		File e = new File(new File(t, "d"), "e");
+		FileUtils.mkdir(e);
+		FileUtils.delete(t, FileUtils.EMPTY_DIRECTORIES_ONLY | FileUtils.RECURSIVE
+				| FileUtils.IGNORE_ERRORS);
+		// Should have deleted as much as possible, but not all
+		assertTrue(t.exists());
+		assertTrue(f.exists());
+		assertFalse(e.exists());
+	}
+
+	@Test
+	public void testRenameOverNonExistingFile() throws IOException {
+		File d = new File(trash, "d");
+		FileUtils.mkdirs(d);
+		File f1 = new File(trash, "d/f");
+		File f2 = new File(trash, "d/g");
+		JGitTestUtil.write(f1, "f1");
+		// test
+		FileUtils.rename(f1, f2);
+		assertFalse(f1.exists());
+		assertTrue(f2.exists());
+		assertEquals("f1", JGitTestUtil.read(f2));
+	}
+
+	@Test
+	public void testRenameOverExistingFile() throws IOException {
+		File d = new File(trash, "d");
+		FileUtils.mkdirs(d);
+		File f1 = new File(trash, "d/f");
+		File f2 = new File(trash, "d/g");
+		JGitTestUtil.write(f1, "f1");
+		JGitTestUtil.write(f2, "f2");
+		// test
+		FileUtils.rename(f1, f2);
+		assertFalse(f1.exists());
+		assertTrue(f2.exists());
+		assertEquals("f1", JGitTestUtil.read(f2));
+	}
+
+	@Test
+	public void testRenameOverExistingNonEmptyDirectory() throws IOException {
+		File d = new File(trash, "d");
+		FileUtils.mkdirs(d);
+		File f1 = new File(trash, "d/f");
+		File f2 = new File(trash, "d/g");
+		File d1 = new File(trash, "d/g/h/i");
+		File f3 = new File(trash, "d/g/h/f");
+		FileUtils.mkdirs(d1);
+		JGitTestUtil.write(f1, "f1");
+		JGitTestUtil.write(f3, "f3");
+		// test
+		try {
+			FileUtils.rename(f1, f2);
+			fail("rename to non-empty directory should fail");
+		} catch (IOException e) {
+			assertEquals("f1", JGitTestUtil.read(f1)); // untouched source
+			assertEquals("f3", JGitTestUtil.read(f3)); // untouched
+			// empty directories within f2 may or may not have been deleted
+		}
+	}
+
+	@Test
+	public void testRenameOverExistingEmptyDirectory() throws IOException {
+		File d = new File(trash, "d");
+		FileUtils.mkdirs(d);
+		File f1 = new File(trash, "d/f");
+		File f2 = new File(trash, "d/g");
+		File d1 = new File(trash, "d/g/h/i");
+		FileUtils.mkdirs(d1);
+		JGitTestUtil.write(f1, "f1");
+		// test
+		FileUtils.rename(f1, f2);
+		assertFalse(f1.exists());
+		assertTrue(f2.exists());
+		assertEquals("f1", JGitTestUtil.read(f2));
+	}
+
+	@Test
+	public void testCreateSymlink() throws IOException {
+		FS fs = FS.DETECTED;
+		try {
+			fs.createSymLink(new File(trash, "x"), "y");
+		} catch (IOException e) {
+			if (fs.supportsSymlinks())
+				fail("FS claims to support symlinks but attempt to create symlink failed");
+			return;
+		}
+		assertTrue(fs.supportsSymlinks());
+		String target = fs.readSymLink(new File(trash, "x"));
+		assertEquals("y", target);
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/LockFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawSubStringPatternTest.java
similarity index 55%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/LockFileTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawSubStringPatternTest.java
index a5cf5cc..194fab4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/LockFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawSubStringPatternTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, GitHub Inc.
+ * Copyright (C) 2013, Robin Stocker <robin at nibor.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -40,45 +40,65 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.util;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.errors.LockFailedException;
+import java.io.UnsupportedEncodingException;
+
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Test;
 
-/**
- * Unit tests of {@link LockFile}
- */
-public class LockFileTest extends RepositoryTestCase {
+public class RawSubStringPatternTest extends RepositoryTestCase {
+
+	@Test
+	public void testBoundary() {
+		assertMatchResult("a", "a", 0);
+		assertMatchResult("a", "abcd", 0);
+		assertMatchResult("ab", "abcd", 0);
+		assertMatchResult("abcd", "abcd", 0);
+		assertMatchResult("bc", "abcd", 1);
+		assertMatchResult("bcd", "abcd", 1);
+		assertMatchResult("cd", "abcd", 2);
+		assertMatchResult("d", "abcd", 3);
+		assertMatchResult("abab", "abaabab", 3);
+	}
+
+	@Test
+	public void testNoMatches() {
+		assertMatchResult("a", "", -1);
+		assertMatchResult("a", "b", -1);
+		assertMatchResult("abab", "abaaba", -1);
+		assertMatchResult("ab", "ddda", -1);
+	}
 
 	@Test
-	public void lockFailedExceptionRecovery() throws Exception {
-		Git git = new Git(db);
-		writeTrashFile("file.txt", "content");
-		git.add().addFilepattern("file.txt").call();
-		RevCommit commit1 = git.commit().setMessage("create file").call();
+	public void testCaseInsensitive() {
+		assertMatchResult("a", "A", 0);
+		assertMatchResult("A", "a", 0);
+		assertMatchResult("Ab", "aB", 0);
+		assertMatchResult("aB", "Ab", 0);
+	}
 
-		assertNotNull(commit1);
-		writeTrashFile("file.txt", "content2");
-		git.add().addFilepattern("file.txt").call();
-		assertNotNull(git.commit().setMessage("edit file").call());
+	@Test(expected = IllegalArgumentException.class)
+	public void testEmptyPattern() {
+		assertNotNull(new RawSubStringPattern(""));
+	}
+
+	private static void assertMatchResult(String pattern, String input, int position) {
+		RawSubStringPattern p = new RawSubStringPattern(pattern);
+		assertEquals("Expected match result " + position + " with input "
+				+ input + " for pattern " + pattern,
+				position, p.match(raw(input)));
+	}
 
-		LockFile lf = new LockFile(db.getIndexFile(), db.getFS());
-		assertTrue(lf.lock());
+	private static RawCharSequence raw(String text) {
 		try {
-			git.checkout().setName(commit1.name()).call();
-			fail("JGitInternalException not thrown");
-		} catch (JGitInternalException e) {
-			assertTrue(e.getCause() instanceof LockFailedException);
-			lf.unlock();
-			git.checkout().setName(commit1.name()).call();
+			byte[] bytes = text.getBytes("UTF-8");
+			return new RawCharSequence(bytes, 0, bytes.length);
+		} catch (UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
index 754c9fc..5975d37 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
@@ -64,6 +64,17 @@ public class AutoCRLFInputStreamTest {
 		assertNoCrLf("\r\n\r\r", "\r\n\r\r");
 		assertNoCrLf("\r\n\r\n", "\r\n\r\n");
 		assertNoCrLf("\r\n\r\n\r", "\n\r\n\r");
+		assertNoCrLf("\0\n", "\0\n");
+	}
+
+	@Test
+	public void testBoundary() throws IOException {
+		for (int i = AutoCRLFInputStream.BUFFER_SIZE - 10; i < AutoCRLFInputStream.BUFFER_SIZE + 10; i++) {
+			String s1 = AutoCRLFOutputStreamTest.repeat("a", i);
+			assertNoCrLf(s1, s1);
+			String s2 = AutoCRLFOutputStreamTest.repeat("\0", i);
+			assertNoCrLf(s2, s2);
+		}
 	}
 
 	private void assertNoCrLf(String string, String string2) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
index b370468..6cb3105 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011, Robin Rosenberg
+ * Copyright (C) 2011, 2013 Robin Rosenberg
+ * Copyright (C) 2013 Robin Stocker
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -65,6 +66,25 @@ public class AutoCRLFOutputStreamTest {
 		assertNoCrLf("\r\n\r\r", "\r\n\r\r");
 		assertNoCrLf("\r\n\r\n", "\r\n\r\n");
 		assertNoCrLf("\r\n\r\n\r", "\n\r\n\r");
+		assertNoCrLf("\0\n", "\0\n");
+	}
+
+	@Test
+	public void testBoundary() throws IOException {
+		for (int i = AutoCRLFOutputStream.BUFFER_SIZE - 10; i < AutoCRLFOutputStream.BUFFER_SIZE + 10; i++) {
+			String s1 = repeat("a", i);
+			assertNoCrLf(s1, s1);
+			String s2 = repeat("\0", i);
+			assertNoCrLf(s2, s2);
+		}
+	}
+
+	public static String repeat(String input, int size) {
+		StringBuilder sb = new StringBuilder(input.length() * size);
+		for (int i = 0; i < size; i++)
+			sb.append(input);
+		String s = sb.toString();
+		return s;
 	}
 
 	private void assertNoCrLf(String string, String string2) throws IOException {
@@ -81,8 +101,9 @@ public class AutoCRLFOutputStreamTest {
 			throws IOException {
 		byte[] inbytes = input.getBytes();
 		byte[] expectBytes = expect.getBytes();
-		for (int i = 0; i < 5; ++i) {
-			byte[] buf = new byte[i];
+		for (int i = -4; i < 5; ++i) {
+			int size = Math.abs(i);
+			byte[] buf = new byte[size];
 			InputStream in = new ByteArrayInputStream(inbytes);
 			ByteArrayOutputStream bos = new ByteArrayOutputStream();
 			OutputStream out = new AutoCRLFOutputStream(bos);
@@ -91,6 +112,13 @@ public class AutoCRLFOutputStreamTest {
 				while ((n = in.read(buf)) >= 0) {
 					out.write(buf, 0, n);
 				}
+			} else if (i < 0) {
+				int n;
+				while ((n = in.read(buf)) >= 0) {
+					byte[] b = new byte[n];
+					System.arraycopy(buf, 0, b, 0, n);
+					out.write(b);
+				}
 			} else {
 				int c;
 				while ((c = in.read()) != -1)
@@ -100,7 +128,7 @@ public class AutoCRLFOutputStreamTest {
 			in.close();
 			out.close();
 			byte[] actualBytes = bos.toByteArray();
-			Assert.assertEquals("bufsize=" + i, encode(expectBytes),
+			Assert.assertEquals("bufsize=" + size, encode(expectBytes),
 					encode(actualBytes));
 		}
 	}
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index f31b3a8..548277d 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -3,15 +3,14 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Export-Package: org.eclipse.jgit.awtui;version="2.3.1"
-Import-Package: org.eclipse.jgit.errors;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.lib;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.nls;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revplot;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.revwalk;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.transport;version="[2.3.1,2.4.0)",
- org.eclipse.jgit.util;version="[2.3.1,2.4.0)"
-Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
+Export-Package: org.eclipse.jgit.awtui;version="3.0.0"
+Import-Package: org.eclipse.jgit.errors;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.lib;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.nls;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revplot;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.revwalk;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.transport;version="[3.0.0,3.1.0)",
+ org.eclipse.jgit.util;version="[3.0.0,3.1.0)"
diff --git a/org.eclipse.jgit.ui/plugin.properties b/org.eclipse.jgit.ui/plugin.properties
index 71fbb34..006c8c2 100644
--- a/org.eclipse.jgit.ui/plugin.properties
+++ b/org.eclipse.jgit.ui/plugin.properties
@@ -1,2 +1,2 @@
 plugin_name=JGit AWT User Interface
-provider_name=Eclipse.org
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index ccb19ac..827a0dd 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index fe5c1e4..35a4994 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -2,41 +2,45 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 2.3.1.201302201838-r
+Bundle-Version: 3.0.0.201306101825-r
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.api;version="2.3.1",
- org.eclipse.jgit.api.errors;version="2.3.1",
- org.eclipse.jgit.blame;version="2.3.1",
- org.eclipse.jgit.diff;version="2.3.1",
- org.eclipse.jgit.dircache;version="2.3.1",
- org.eclipse.jgit.errors;version="2.3.1",
- org.eclipse.jgit.events;version="2.3.1",
- org.eclipse.jgit.fnmatch;version="2.3.1",
- org.eclipse.jgit.ignore;version="2.3.1",
- org.eclipse.jgit.internal;version="2.3.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.lib;version="2.3.1",
- org.eclipse.jgit.merge;version="2.3.1",
- org.eclipse.jgit.nls;version="2.3.1",
- org.eclipse.jgit.notes;version="2.3.1",
- org.eclipse.jgit.patch;version="2.3.1",
- org.eclipse.jgit.revplot;version="2.3.1",
- org.eclipse.jgit.revwalk;version="2.3.1",
- org.eclipse.jgit.revwalk.filter;version="2.3.1",
- org.eclipse.jgit.storage.dfs;version="2.3.1",
- org.eclipse.jgit.storage.file;version="2.3.1",
- org.eclipse.jgit.storage.pack;version="2.3.1",
- org.eclipse.jgit.submodule;version="2.3.1",
- org.eclipse.jgit.transport;version="2.3.1",
- org.eclipse.jgit.transport.resolver;version="2.3.1",
- org.eclipse.jgit.treewalk;version="2.3.1",
- org.eclipse.jgit.treewalk.filter;version="2.3.1",
- org.eclipse.jgit.util;version="2.3.1",
- org.eclipse.jgit.util.io;version="2.3.1"
+Export-Package: org.eclipse.jgit.api;version="3.0.0",
+ org.eclipse.jgit.api.errors;version="3.0.0",
+ org.eclipse.jgit.blame;version="3.0.0",
+ org.eclipse.jgit.diff;version="3.0.0",
+ org.eclipse.jgit.dircache;version="3.0.0",
+ org.eclipse.jgit.errors;version="3.0.0",
+ org.eclipse.jgit.events;version="3.0.0",
+ org.eclipse.jgit.fnmatch;version="3.0.0",
+ org.eclipse.jgit.ignore;version="3.0.0",
+ org.eclipse.jgit.internal;version="3.0.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.storage.dfs;version="3.0.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.storage.file;version="3.0.0";x-friends:="org.eclipse.jgit.test,
+ 	org.eclipse.jgit.junit,org.eclipse.jgit.junit.http,org.eclipse.jgit.http.server",
+ org.eclipse.jgit.internal.storage.pack;version="3.0.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.lib;version="3.0.0",
+ org.eclipse.jgit.merge;version="3.0.0",
+ org.eclipse.jgit.nls;version="3.0.0",
+ org.eclipse.jgit.notes;version="3.0.0",
+ org.eclipse.jgit.patch;version="3.0.0",
+ org.eclipse.jgit.revplot;version="3.0.0",
+ org.eclipse.jgit.revwalk;version="3.0.0",
+ org.eclipse.jgit.revwalk.filter;version="3.0.0",
+ org.eclipse.jgit.storage.file;version="3.0.0",
+ org.eclipse.jgit.storage.pack;version="3.0.0",
+ org.eclipse.jgit.submodule;version="3.0.0",
+ org.eclipse.jgit.transport;version="3.0.0",
+ org.eclipse.jgit.transport.resolver;version="3.0.0",
+ org.eclipse.jgit.treewalk;version="3.0.0",
+ org.eclipse.jgit.treewalk.filter;version="3.0.0",
+ org.eclipse.jgit.util;version="3.0.0",
+ org.eclipse.jgit.util.io;version="3.0.0"
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
-Import-Package: javax.crypto,
+Import-Package: javaewah;version="0.5.6",
+ javax.crypto,
  javax.net.ssl,
  org.xml.sax,
  org.xml.sax.helpers
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index bf6d7d7..8e8c7c0 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,6 +3,6 @@ Bundle-ManifestVersion: 2
 Bundle-Name: org.eclipse.jgit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.source;singleton:=true
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 2.3.1.201302201838-r
-Eclipse-SourceBundle: org.eclipse.jgit;version="2.3.1";roots="."
+Bundle-Version: 3.0.0.201306101825-r
+Eclipse-SourceBundle: org.eclipse.jgit;version="3.0.0";roots="."
 
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index ecc09be..fd05d41 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>2.3.1.201302201838-r</version>
+    <version>3.0.0.201306101825-r</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
@@ -73,6 +73,11 @@
       <groupId>com.jcraft</groupId>
       <artifactId>jsch</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>com.googlecode.javaewah</groupId>
+      <artifactId>JavaEWAH</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index b3ef62a..33489eb 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -25,10 +25,13 @@ badSectionEntry=Bad section entry: {0}
 bareRepositoryNoWorkdirAndIndex=Bare Repository has neither a working tree, nor an index
 base64InputNotProperlyPadded=Base64 input not properly padded.
 baseLengthIncorrect=base length incorrect
+bitmapMissingObject=Bitmap at {0} is missing {1}.
+bitmapsMustBePrepared=Bitmaps must be prepared before they may be written.
 blameNotCommittedYet=Not Committed Yet
 blobNotFound=Blob not found: {0}
 blobNotFoundForPath=Blob not found: {0} for path: {1}
 branchNameInvalid=Branch name {0} is not allowed
+buildingBitmaps=Building bitmaps
 cachedPacksPreventsIndexCreation=Using cached packs prevents index creation
 cachedPacksPreventsListingObjects=Using cached packs prevents listing objects
 cannotBeCombined=Cannot be combined.
@@ -121,6 +124,7 @@ couldNotCheckOutBecauseOfConflicts=Could not check out because of conflicts
 couldNotDeleteLockFileShouldNotHappen=Could not delete lock file. Should not happen
 couldNotDeleteTemporaryIndexFileShouldNotHappen=Could not delete temporary index file. Should not happen
 couldNotGetAdvertisedRef=Could not get advertised Ref for branch {0}
+couldNotGetRepoStatistics=Could not get repository statistics
 couldNotLockHEAD=Could not lock HEAD
 couldNotReadIndexInOneGo=Could not read index in one go, only {0} out of {1} read
 couldNotReadObjectWhileParsingCommit=Could not read an object while parsing commit {0}
@@ -176,6 +180,7 @@ errorInvalidProtocolWantedOldNewRef=error: invalid protocol: wanted 'old new ref
 errorListing=Error listing {0}
 errorOccurredDuringUnpackingOnTheRemoteEnd=error occurred during unpacking on the remote end: {0}
 errorReadingInfoRefs=error reading info/refs
+errorSymlinksNotSupported=Symlinks are not supported with this OS/JRE
 exceptionCaughtDuringExecutionOfAddCommand=Exception caught during execution of add command
 exceptionCaughtDuringExecutionOfCherryPickCommand=Exception caught during execution of cherry-pick command. {0}
 exceptionCaughtDuringExecutionOfCommitCommand=Exception caught during execution of commit command
@@ -196,6 +201,7 @@ expectedBooleanStringValue=Expected boolean string value
 expectedCharacterEncodingGuesses=Expected {0} character encoding guesses
 expectedEOFReceived=expected EOF; received ''{0}'' instead
 expectedGot=expected ''{0}'', got ''{1}''
+expectedLessThanGot=expected less than ''{0}'', got ''{1}''
 expectedPktLineWithService=expected pkt-line with '# service=-', got ''{0}''
 expectedReceivedContentType=expected Content-Type {0}; received Content-Type {1}
 expectedReportForRefNotReceived={0}: expected report for ref {1} not received
@@ -265,6 +271,7 @@ invalidTagOption=Invalid tag option: {0}
 invalidTimeout=Invalid timeout: {0}
 invalidURL=Invalid URL {0}
 invalidWildcards=Invalid wildcards {0}
+invalidRefSpec=Invalid refspec {0}
 invalidWindowSize=Invalid window size
 isAStaticFlagAndHasNorevWalkInstance={0} is a static flag and has no RevWalk instance
 JRELacksMD5Implementation=JRE lacks MD5 implementation
@@ -288,6 +295,9 @@ mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs
 mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a default strategy
 mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
 mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
+mergeRecursiveReturnedNoCommit=Merge returned no commit:\n Depth {0}\n Head one {1}\n Head two {2}
+mergeRecursiveTooManyMergeBasesFor = "More than {0} merge bases for:\n a {1}\n b {2} found:\n  count {3}"
+messageAndTaggerNotAllowedInUnannotatedTags = Unannotated tags cannot have a message or tagger
 minutesAgo={0} minutes ago
 missingAccesskey=Missing accesskey.
 missingConfigurationForKey=No value for key {0} found in configuration
@@ -313,6 +323,7 @@ noApplyInDelete=No apply in delete
 noClosingBracket=No closing {0} found for {1} at index {2}.
 noHEADExistsAndNoExplicitStartingRevisionWasSpecified=No HEAD exists and no explicit starting revision was specified
 noHMACsupport=No {0} support: {1}
+noMergeBase=No merge base could be determined. Reason={0}. {1}
 noMergeHeadSpecified=No merge head specified
 noSuchRef=no such ref
 notABoolean=Not a boolean: {0}
@@ -333,6 +344,7 @@ objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream
 objectAtPathDoesNotHaveId=Object at path "{0}" does not have an id assigned. All object ids must be assigned prior to writing a tree.
 objectIsCorrupt=Object {0} is corrupt: {1}
 objectIsNotA=Object {0} is not a {1}.
+objectNotFound=Object {0} not found.
 objectNotFoundIn=Object {0} not found in {1}.
 obtainingCommitsForCherryPick=Obtaining commits that need to be cherry-picked
 offsetWrittenDeltaBaseForObjectNotFoundInAPack=Offset-written delta base for object not found in a pack
@@ -377,7 +389,7 @@ pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport
 pushNotPermitted=push not permitted
 rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
 readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
-readTimedOut=Read timed out
+readTimedOut=Read timed out after {0} ms
 receivePackObjectTooLarge1=Object too large, rejecting the pack. Max object size limit is {0} bytes.
 receivePackObjectTooLarge2=Object too large ({0} bytes), rejecting the pack. Max object size limit is {1} bytes.
 receivingObjects=Receiving objects
@@ -421,6 +433,7 @@ rewinding=Rewinding to commit {0}
 searchForReuse=Finding sources
 searchForSizes=Getting sizes
 secondsAgo={0} seconds ago
+selectingCommits=Selecting commits
 sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm.
 serviceNotEnabledNoName=Service not enabled
 serviceNotPermitted={0} not permitted
@@ -532,7 +545,7 @@ weeksAgo={0} weeks ago
 windowSizeMustBeLesserThanLimit=Window size must be < limit
 windowSizeMustBePowerOf2=Window size must be power of 2
 writerAlreadyInitialized=Writer already initialized
-writeTimedOut=Write timed out
+writeTimedOut=Write timed out after {0} ms
 writingNotPermitted=Writing not permitted
 writingNotSupported=Writing {0} not supported.
 writingObjects=Writing objects
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/storage/dfs/DfsText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/storage/dfs/DfsText.properties
similarity index 100%
rename from org.eclipse.jgit/resources/org/eclipse/jgit/storage/dfs/DfsText.properties
rename to org.eclipse.jgit/resources/org/eclipse/jgit/internal/storage/dfs/DfsText.properties
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index dca7197..d5d9559 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -150,7 +150,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
 				String cherryPickName = srcCommit.getId().abbreviate(7).name()
 						+ " " + srcCommit.getShortMessage(); //$NON-NLS-1$
 
-				ResolveMerger merger = (ResolveMerger) MergeStrategy.RESOLVE
+				ResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE
 						.newMerger(repo);
 				merger.setWorkingTreeIterator(new FileTreeIterator(repo));
 				merger.setBase(srcParent.getTree());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 22bda61..645d3e7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2011, 2013 Chris Aniszczyk <caniszczyk at gmail.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -154,12 +154,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
 		RemoteConfig config = new RemoteConfig(clonedRepo.getConfig(), remote);
 		config.addURI(u);
 
-		final String dst = bare ? Constants.R_HEADS : Constants.R_REMOTES
-				+ config.getName();
+		final String dst = (bare ? Constants.R_HEADS : Constants.R_REMOTES
+				+ config.getName() + "/") + "*"; //$NON-NLS-1$//$NON-NLS-2$
 		RefSpec refSpec = new RefSpec();
 		refSpec = refSpec.setForceUpdate(true);
-		refSpec = refSpec.setSourceDestination(
-				Constants.R_HEADS + "*", dst + "/*"); //$NON-NLS-1$ //$NON-NLS-2$
+		refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$
 
 		config.addFetchRefSpec(refSpec);
 		config.update(clonedRepo.getConfig());
@@ -182,7 +181,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
 	private List<RefSpec> calculateRefSpecs(final String dst) {
 		RefSpec wcrs = new RefSpec();
 		wcrs = wcrs.setForceUpdate(true);
-		wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst + "/*"); //$NON-NLS-1$ //$NON-NLS-2$
+		wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$
 		List<RefSpec> specs = new ArrayList<RefSpec>();
 		if (cloneAllBranches)
 			specs.add(wcrs);
@@ -199,12 +198,19 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException, GitAPIException {
 
-		Ref head = result.getAdvertisedRef(branch);
+		Ref head = null;
 		if (branch.equals(Constants.HEAD)) {
 			Ref foundBranch = findBranchToCheckout(result);
 			if (foundBranch != null)
 				head = foundBranch;
 		}
+		if (head == null) {
+			head = result.getAdvertisedRef(branch);
+			if (head == null)
+				head = result.getAdvertisedRef(Constants.R_HEADS + branch);
+			if (head == null)
+				head = result.getAdvertisedRef(Constants.R_TAGS + branch);
+		}
 
 		if (head == null || head.getObjectId() == null)
 			return; // throw exception?
@@ -363,7 +369,9 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
 
 	/**
 	 * @param branch
-	 *            the initial branch to check out when cloning the repository
+	 *            the initial branch to check out when cloning the repository.
+	 *            Can be specified as ref name (<code>refs/heads/master</code>),
+	 *            branch name (<code>master</code>) or tag name (<code>v1.2.3</code>).
 	 * @return this instance
 	 */
 	public CloneCommand setBranch(String branch) {
@@ -410,7 +418,8 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
 	/**
 	 * @param branchesToClone
 	 *            collection of branches to clone. Ignored when allSelected is
-	 *            true.
+	 *            true. Must be specified as full ref names (e.g.
+	 *            <code>refs/heads/master</code>).
 	 * @return {@code this}
 	 */
 	public CloneCommand setBranchesToClone(Collection<String> branchesToClone) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 056b47d..162105b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -77,6 +77,8 @@ import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
@@ -150,13 +152,16 @@ public class CommitCommand extends GitCommand<RevCommit> {
 		checkCallable();
 		Collections.sort(only);
 
-		RepositoryState state = repo.getRepositoryState();
-		if (!state.canCommit())
-			throw new WrongRepositoryStateException(MessageFormat.format(
-					JGitText.get().cannotCommitOnARepoWithState, state.name()));
-		processOptions(state);
+		RevWalk rw = new RevWalk(repo);
 
 		try {
+			RepositoryState state = repo.getRepositoryState();
+			if (!state.canCommit())
+				throw new WrongRepositoryStateException(MessageFormat.format(
+						JGitText.get().cannotCommitOnARepoWithState,
+						state.name()));
+			processOptions(state, rw);
+
 			if (all && !repo.isBare() && repo.getWorkTree() != null) {
 				Git git = new Git(repo);
 				try {
@@ -182,11 +187,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
 
 			if (headId != null)
 				if (amend) {
-					RevCommit previousCommit = new RevWalk(repo)
-							.parseCommit(headId);
-					RevCommit[] p = previousCommit.getParents();
-					for (int i = 0; i < p.length; i++)
-						parents.add(0, p[i].getId());
+					RevCommit previousCommit = rw.parseCommit(headId);
+					for (RevCommit p : previousCommit.getParents())
+						parents.add(p.getId());
 					if (author == null)
 						author = previousCommit.getAuthorIdent();
 				} else {
@@ -197,7 +200,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
 			DirCache index = repo.lockDirCache();
 			try {
 				if (!only.isEmpty())
-					index = createTemporaryIndex(headId, index);
+					index = createTemporaryIndex(headId, index, rw);
 
 				ObjectInserter odi = repo.newObjectInserter();
 				try {
@@ -220,55 +223,51 @@ public class CommitCommand extends GitCommand<RevCommit> {
 					ObjectId commitId = odi.insert(commit);
 					odi.flush();
 
-					RevWalk revWalk = new RevWalk(repo);
-					try {
-						RevCommit revCommit = revWalk.parseCommit(commitId);
-						RefUpdate ru = repo.updateRef(Constants.HEAD);
-						ru.setNewObjectId(commitId);
-						if (reflogComment != null) {
-							ru.setRefLogMessage(reflogComment, false);
-						} else {
-							String prefix = amend ? "commit (amend): " //$NON-NLS-1$
-									: "commit: ";
-							ru.setRefLogMessage(
-									prefix + revCommit.getShortMessage(), false);
-						}
-						if (headId != null)
-							ru.setExpectedOldObjectId(headId);
-						else
-							ru.setExpectedOldObjectId(ObjectId.zeroId());
-						Result rc = ru.forceUpdate();
-						switch (rc) {
-						case NEW:
-						case FORCED:
-						case FAST_FORWARD: {
-							setCallable(false);
-							if (state == RepositoryState.MERGING_RESOLVED) {
-								// Commit was successful. Now delete the files
-								// used for merge commits
-								repo.writeMergeCommitMsg(null);
-								repo.writeMergeHeads(null);
-							} else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
-								repo.writeMergeCommitMsg(null);
-								repo.writeCherryPickHead(null);
-							} else if (state == RepositoryState.REVERTING_RESOLVED) {
-								repo.writeMergeCommitMsg(null);
-								repo.writeRevertHead(null);
-							}
-							return revCommit;
-						}
-						case REJECTED:
-						case LOCK_FAILURE:
-							throw new ConcurrentRefUpdateException(JGitText
-									.get().couldNotLockHEAD, ru.getRef(), rc);
-						default:
-							throw new JGitInternalException(MessageFormat
-									.format(JGitText.get().updatingRefFailed,
-											Constants.HEAD,
-											commitId.toString(), rc));
+					RevCommit revCommit = rw.parseCommit(commitId);
+					RefUpdate ru = repo.updateRef(Constants.HEAD);
+					ru.setNewObjectId(commitId);
+					if (reflogComment != null) {
+						ru.setRefLogMessage(reflogComment, false);
+					} else {
+						String prefix = amend ? "commit (amend): " //$NON-NLS-1$
+								: parents.size() == 0 ? "commit (initial): "
+										: "commit: ";
+						ru.setRefLogMessage(
+								prefix + revCommit.getShortMessage(), false);
+					}
+					if (headId != null)
+						ru.setExpectedOldObjectId(headId);
+					else
+						ru.setExpectedOldObjectId(ObjectId.zeroId());
+					Result rc = ru.forceUpdate();
+					switch (rc) {
+					case NEW:
+					case FORCED:
+					case FAST_FORWARD: {
+						setCallable(false);
+						if (state == RepositoryState.MERGING_RESOLVED) {
+							// Commit was successful. Now delete the files
+							// used for merge commits
+							repo.writeMergeCommitMsg(null);
+							repo.writeMergeHeads(null);
+						} else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
+							repo.writeMergeCommitMsg(null);
+							repo.writeCherryPickHead(null);
+						} else if (state == RepositoryState.REVERTING_RESOLVED) {
+							repo.writeMergeCommitMsg(null);
+							repo.writeRevertHead(null);
 						}
-					} finally {
-						revWalk.release();
+						return revCommit;
+					}
+					case REJECTED:
+					case LOCK_FAILURE:
+						throw new ConcurrentRefUpdateException(
+								JGitText.get().couldNotLockHEAD, ru.getRef(),
+								rc);
+					default:
+						throw new JGitInternalException(MessageFormat.format(
+								JGitText.get().updatingRefFailed,
+								Constants.HEAD, commitId.toString(), rc));
 					}
 				} finally {
 					odi.release();
@@ -281,6 +280,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
 		} catch (IOException e) {
 			throw new JGitInternalException(
 					JGitText.get().exceptionCaughtDuringExecutionOfCommitCommand, e);
+		} finally {
+			rw.dispose();
 		}
 	}
 
@@ -297,7 +298,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
 					+ changeId.getName() + "\n"); //$NON-NLS-1$
 	}
 
-	private DirCache createTemporaryIndex(ObjectId headId, DirCache index)
+	private DirCache createTemporaryIndex(ObjectId headId, DirCache index,
+			RevWalk rw)
 			throws IOException {
 		ObjectInserter inserter = null;
 
@@ -317,7 +319,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
 		int fIdx = treeWalk.addTree(new FileTreeIterator(repo));
 		int hIdx = -1;
 		if (headId != null)
-			hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
+			hIdx = treeWalk.addTree(rw.parseTree(headId));
 		treeWalk.setRecursive(true);
 
 		String lastAddedFile = null;
@@ -473,11 +475,14 @@ public class CommitCommand extends GitCommand<RevCommit> {
 	 *
 	 * @param state
 	 *            the state of the repository we are working on
+	 * @param rw
+	 *            the RevWalk to use
 	 *
 	 * @throws NoMessageException
 	 *             if the commit message has not been specified
 	 */
-	private void processOptions(RepositoryState state) throws NoMessageException {
+	private void processOptions(RepositoryState state, RevWalk rw)
+			throws NoMessageException {
 		if (committer == null)
 			committer = new PersonIdent(repo);
 		if (author == null && !amend)
@@ -487,6 +492,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
 		if (state == RepositoryState.MERGING_RESOLVED) {
 			try {
 				parents = repo.readMergeHeads();
+				if (parents != null)
+					for (int i = 0; i < parents.size(); i++) {
+						RevObject ro = rw.parseAny(parents.get(i));
+						if (ro instanceof RevTag)
+							parents.set(i, rw.peel(ro));
+					}
 			} catch (IOException e) {
 				throw new JGitInternalException(MessageFormat.format(
 						JGitText.get().exceptionOccurredDuringReadingOfGIT_DIR,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index 9dd6633..77b84d3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -51,11 +51,11 @@ import java.util.Properties;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.GC;
+import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.FileRepository;
-import org.eclipse.jgit.storage.file.GC;
-import org.eclipse.jgit.storage.file.GC.RepoStatistics;
 import org.eclipse.jgit.util.GitDateParser;
 
 /**
@@ -129,6 +129,24 @@ public class GarbageCollectCommand extends GitCommand<Properties> {
 		}
 	}
 
+	/**
+	 * Computes and returns the repository statistics.
+	 *
+	 * @return the repository statistics
+	 * @throws GitAPIException
+	 *             thrown if the repository statistics cannot be computed
+	 * @since 3.0
+	 */
+	public Properties getStatistics() throws GitAPIException {
+		try {
+			GC gc = new GC((FileRepository) repo);
+			return toProperties(gc.getStatistics());
+		} catch (IOException e) {
+			throw new JGitInternalException(
+					JGitText.get().couldNotGetRepoStatistics, e);
+		}
+	}
+
 	@SuppressWarnings("boxing")
 	private static Properties toProperties(RepoStatistics stats) {
 		Properties p = new Properties();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 77f951e..4b8378a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -633,6 +633,16 @@ public class Git {
 	}
 
 	/**
+	 * Returns a command object to find human-readable names of revisions.
+	 *
+	 * @return a {@link NameRevCommand}.
+	 * @since 3.0
+	 */
+	public NameRevCommand nameRev() {
+		return new NameRevCommand(repo);
+	}
+
+	/**
 	 * @return the git repository this class is interacting with
 	 */
 	public Repository getRepository() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index 6d4b347..cad2790 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -239,10 +239,25 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> {
 	 */
 	public LogCommand all() throws IOException {
 		for (Ref ref : getRepository().getAllRefs().values()) {
+			if(!ref.isPeeled())
+				ref = getRepository().peel(ref);
+
 			ObjectId objectId = ref.getPeeledObjectId();
 			if (objectId == null)
 				objectId = ref.getObjectId();
-			add(objectId);
+			RevCommit commit = null;
+			try {
+				commit = walk.parseCommit(objectId);
+			} catch (MissingObjectException e) {
+				// ignore: the ref points to an object that does not exist;
+				// it should be ignored as traversal starting point.
+			} catch (IncorrectObjectTypeException e) {
+				// ignore: the ref points to an object that is not a commit
+				// (e.g. a tree or a blob);
+				// it should be ignored as traversal starting point.
+			}
+			if (commit != null)
+				add(commit);
 		}
 		return this;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index 6eba35e..509203e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -95,7 +95,7 @@ import org.eclipse.jgit.util.StringUtils;
  */
 public class MergeCommand extends GitCommand<MergeResult> {
 
-	private MergeStrategy mergeStrategy = MergeStrategy.RESOLVE;
+	private MergeStrategy mergeStrategy = MergeStrategy.RECURSIVE;
 
 	private List<Ref> commits = new LinkedList<Ref>();
 
@@ -195,6 +195,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
 		}
 	}
 
+	private boolean commit = true;
+
 	/**
 	 * @param repo
 	 */
@@ -234,6 +236,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
 			refLogMessage.append(ref.getName());
 
 			// handle annotated tags
+			ref = repo.peel(ref);
 			ObjectId objectId = ref.getPeeledObjectId();
 			if (objectId == null)
 				objectId = ref.getObjectId();
@@ -326,7 +329,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
 				if (merger instanceof ResolveMerger) {
 					ResolveMerger resolveMerger = (ResolveMerger) merger;
 					resolveMerger.setCommitNames(new String[] {
-							"BASE", "HEAD", ref.getName() }); //$NON-NLS-1$
+							"BASE", "HEAD", ref.getName() }); //$NON-NLS-1$ //$NON-NLS-2$
 					resolveMerger.setWorkingTreeIterator(new FileTreeIterator(repo));
 					noProblems = merger.merge(headCommit, srcCommit);
 					lowLevelResults = resolveMerger
@@ -349,18 +352,26 @@ public class MergeCommand extends GitCommand<MergeResult> {
 					dco.checkout();
 
 					String msg = null;
-					RevCommit newHead = null;
+					ObjectId newHeadId = null;
 					MergeStatus mergeStatus = null;
-					if (!squash) {
-						newHead = new Git(getRepository()).commit()
-							.setReflogComment(refLogMessage.toString()).call();
+					if (!commit && squash) {
+						mergeStatus = MergeStatus.MERGED_SQUASHED_NOT_COMMITTED;
+					}
+					if (!commit && !squash) {
+						mergeStatus = MergeStatus.MERGED_NOT_COMMITTED;
+					}
+					if (commit && !squash) {
+						newHeadId = new Git(getRepository()).commit()
+								.setReflogComment(refLogMessage.toString())
+								.call().getId();
 						mergeStatus = MergeStatus.MERGED;
-					} else {
+					}
+					if (commit && squash) {
 						msg = JGitText.get().squashCommitNotUpdatingHEAD;
-						newHead = headCommit;
+						newHeadId = headCommit.getId();
 						mergeStatus = MergeStatus.MERGED_SQUASHED;
 					}
-					return new MergeResult(newHead.getId(), null,
+					return new MergeResult(newHeadId, null,
 							new ObjectId[] { headCommit.getId(),
 									srcCommit.getId() }, mergeStatus,
 							mergeStrategy, null, msg);
@@ -520,4 +531,23 @@ public class MergeCommand extends GitCommand<MergeResult> {
 		this.fastForwardMode = fastForwardMode;
 		return this;
 	}
+
+	/**
+	 * Controls whether the merge command should automatically commit after a
+	 * successful merge
+	 *
+	 * @param commit
+	 *            <code>true</code> if this command should commit (this is the
+	 *            default behavior). <code>false</code> if this command should
+	 *            not commit. In case the merge was successful but this flag was
+	 *            set to <code>false</code> a {@link MergeResult} with type
+	 *            {@link MergeResult} with status
+	 *            {@link MergeStatus#MERGED_NOT_COMMITTED} is returned
+	 * @return {@code this}
+	 * @since 3.0
+	 */
+	public MergeCommand setCommit(boolean commit) {
+		this.commit = commit;
+		return this;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index 8227216..c34ba6b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -141,6 +141,20 @@ public class MergeResult {
 				return true;
 			}
 		},
+		/**
+		 * @since 3.0
+		 */
+		MERGED_SQUASHED_NOT_COMMITTED {
+			@Override
+			public String toString() {
+				return "Merged-squashed-not-committed";
+			}
+
+			@Override
+			public boolean isSuccessful() {
+				return true;
+			}
+		},
 		/** */
 		CONFLICTING {
 			@Override
@@ -167,6 +181,19 @@ public class MergeResult {
 				return false;
 			}
 		},
+		/**
+		 * @since 3.0
+		 **/
+		MERGED_NOT_COMMITTED {
+			public String toString() {
+				return "MergedNotCommited";
+			}
+
+			@Override
+			public boolean isSuccessful() {
+				return true;
+			}
+		},
 		/** */
 		NOT_SUPPORTED {
 			@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
new file mode 100644
index 0000000..95a1f35
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2013, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.FIFORevQueue;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/**
+ * Command to find human-readable names of revisions.
+ *
+ * @see <a
+ *      href="http://www.kernel.org/pub/software/scm/git/docs/git-name-rev.html"
+ *      >Git documentation about name-rev</a>
+ * @since 3.0
+ */
+public class NameRevCommand extends GitCommand<Map<ObjectId, String>> {
+	/** Amount of slop to allow walking past the earliest requested commit. */
+	private static final int COMMIT_TIME_SLOP = 60 * 60 * 24;
+
+	/** Cost of traversing a merge commit compared to a linear history. */
+	private static final int MERGE_COST = 65535;
+
+	private static class NameRevCommit extends RevCommit {
+		private String tip;
+		private int distance;
+		private long cost;
+
+		private NameRevCommit(AnyObjectId id) {
+			super(id);
+		}
+
+		private StringBuilder format() {
+			StringBuilder sb = new StringBuilder(tip);
+			if (distance > 0)
+				sb.append('~').append(distance);
+			return sb;
+		}
+
+		@Override
+		public String toString() {
+			StringBuilder sb = new StringBuilder(getClass().getSimpleName())
+				.append('[');
+			if (tip != null)
+				sb.append(format());
+			else
+				sb.append((Object) null);
+			sb.append(',').append(cost).append(']').append(' ')
+				.append(super.toString()).toString();
+			return sb.toString();
+		}
+	}
+
+	private final RevWalk walk;
+	private final List<String> prefixes;
+	private final List<Ref> refs;
+	private final List<ObjectId> revs;
+	private int mergeCost;
+
+	/**
+	 * Create a new name-rev command.
+	 *
+	 * @param repo
+	 */
+	protected NameRevCommand(Repository repo) {
+		super(repo);
+		mergeCost = MERGE_COST;
+		prefixes = new ArrayList<String>(2);
+		refs = new ArrayList<Ref>();
+		revs = new ArrayList<ObjectId>(2);
+		walk = new RevWalk(repo) {
+			@Override
+			public NameRevCommit createCommit(AnyObjectId id) {
+				return new NameRevCommit(id);
+			}
+		};
+	}
+
+	@Override
+	public Map<ObjectId, String> call() throws GitAPIException {
+		try {
+			Map<ObjectId, String> nonCommits = new HashMap<ObjectId, String>();
+			FIFORevQueue pending = new FIFORevQueue();
+			for (Ref ref : refs)
+				addRef(ref, nonCommits, pending);
+			addPrefixes(nonCommits, pending);
+			int cutoff = minCommitTime() - COMMIT_TIME_SLOP;
+
+			while (true) {
+				NameRevCommit c = (NameRevCommit) pending.next();
+				if (c == null)
+					break;
+				if (c.getCommitTime() < cutoff)
+					continue;
+				for (int i = 0; i < c.getParentCount(); i++) {
+					NameRevCommit p = (NameRevCommit) walk.parseCommit(c.getParent(i));
+					long cost = c.cost + (i > 0 ? mergeCost : 1);
+					if (p.tip == null || compare(c.tip, cost, p.tip, p.cost) < 0) {
+						if (i > 0) {
+							p.tip = c.format().append('^').append(i + 1).toString();
+							p.distance = 0;
+						} else {
+							p.tip = c.tip;
+							p.distance = c.distance + 1;
+						}
+						p.cost = cost;
+						pending.add(p);
+					}
+				}
+			}
+
+			Map<ObjectId, String> result =
+				new LinkedHashMap<ObjectId, String>(revs.size());
+			for (ObjectId id : revs) {
+				RevObject o = walk.parseAny(id);
+				if (o instanceof NameRevCommit) {
+					NameRevCommit c = (NameRevCommit) o;
+					if (c.tip != null)
+						result.put(id, simplify(c.format().toString()));
+				} else {
+					String name = nonCommits.get(id);
+					if (name != null)
+						result.put(id, simplify(name));
+				}
+			}
+
+			setCallable(false);
+			walk.release();
+			return result;
+		} catch (IOException e) {
+			walk.reset();
+			throw new JGitInternalException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Add an object to search for.
+	 *
+	 * @param id
+	 *            object ID to add.
+	 * @return {@code this}
+	 * @throws MissingObjectException
+	 *             the object supplied is not available from the object
+	 *             database.
+	 * @throws JGitInternalException
+	 *             a low-level exception of JGit has occurred. The original
+	 *             exception can be retrieved by calling
+	 *             {@link Exception#getCause()}.
+	 */
+	public NameRevCommand add(ObjectId id) throws MissingObjectException,
+			JGitInternalException {
+		checkCallable();
+		try {
+			walk.parseAny(id);
+		} catch (MissingObjectException e) {
+			throw e;
+		} catch (IOException e) {
+			throw new JGitInternalException(e.getMessage(), e);
+		}
+		revs.add(id.copy());
+		return this;
+	}
+
+	/**
+	 * Add multiple objects to search for.
+	 *
+	 * @param ids
+	 *            object IDs to add.
+	 * @return {@code this}
+	 * @throws MissingObjectException
+	 *             the object supplied is not available from the object
+	 *             database.
+	 * @throws JGitInternalException
+	 *             a low-level exception of JGit has occurred. The original
+	 *             exception can be retrieved by calling
+	 *             {@link Exception#getCause()}.
+	 */
+	public NameRevCommand add(Iterable<ObjectId> ids)
+			throws MissingObjectException, JGitInternalException {
+		for (ObjectId id : ids)
+			add(id);
+		return this;
+	}
+
+	/**
+	 * Add a ref prefix to the set that results must match.
+	 * <p>
+	 * If an object matches multiple refs equally well, the first matching ref
+	 * added with {@link #addRef(Ref)} is preferred, or else the first matching
+	 * prefix added by {@link #addPrefix(String)}.
+	 *
+	 * @param prefix
+	 *            prefix to add; see {@link RefDatabase#getRefs(String)}
+	 * @return {@code this}
+	 */
+	public NameRevCommand addPrefix(String prefix) {
+		checkCallable();
+		prefixes.add(prefix);
+		return this;
+	}
+
+	/**
+	 * Add all annotated tags under {@code refs/tags/} to the set that all results
+	 * must match.
+	 * <p>
+	 * Calls {@link #addRef(Ref)}; see that method for a note on matching
+	 * priority.
+	 *
+	 * @return {@code this}
+	 * @throws JGitInternalException
+	 *             a low-level exception of JGit has occurred. The original
+	 *             exception can be retrieved by calling
+	 *             {@link Exception#getCause()}.
+	 */
+	public NameRevCommand addAnnotatedTags() {
+		checkCallable();
+		try {
+			for (Ref ref : repo.getRefDatabase().getRefs(Constants.R_TAGS).values()) {
+				ObjectId id = ref.getObjectId();
+				if (id != null && (walk.parseAny(id) instanceof RevTag))
+					addRef(ref);
+			}
+		} catch (IOException e) {
+			throw new JGitInternalException(e.getMessage(), e);
+		}
+		return this;
+	}
+
+	/**
+	 * Add a ref to the set that all results must match.
+	 * <p>
+	 * If an object matches multiple refs equally well, the first matching ref
+	 * added with {@link #addRef(Ref)} is preferred, or else the first matching
+	 * prefix added by {@link #addPrefix(String)}.
+	 *
+	 * @param ref
+	 *            ref to add.
+	 * @return {@code this}
+	 */
+	public NameRevCommand addRef(Ref ref) {
+		checkCallable();
+		refs.add(ref);
+		return this;
+	}
+
+	NameRevCommand setMergeCost(int cost) {
+		mergeCost = cost;
+		return this;
+	}
+
+	private void addPrefixes(Map<ObjectId, String> nonCommits,
+			FIFORevQueue pending) throws IOException {
+		if (!prefixes.isEmpty()) {
+			for (String prefix : prefixes)
+				addPrefix(prefix, nonCommits, pending);
+		} else if (refs.isEmpty())
+			addPrefix(Constants.R_REFS, nonCommits, pending);
+	}
+
+	private void addPrefix(String prefix, Map<ObjectId, String> nonCommits,
+			FIFORevQueue pending) throws IOException {
+		for (Ref ref : repo.getRefDatabase().getRefs(prefix).values())
+			addRef(ref, nonCommits, pending);
+	}
+
+	private void addRef(Ref ref, Map<ObjectId, String> nonCommits,
+			FIFORevQueue pending) throws IOException {
+		if (ref.getObjectId() == null)
+			return;
+		RevObject o = walk.parseAny(ref.getObjectId());
+		while (o instanceof RevTag) {
+			RevTag t = (RevTag) o;
+			nonCommits.put(o, ref.getName());
+			o = t.getObject();
+			walk.parseHeaders(o);
+		}
+		if (o instanceof NameRevCommit) {
+			NameRevCommit c = (NameRevCommit) o;
+			if (c.tip == null)
+				c.tip = ref.getName();
+			pending.add(c);
+		} else if (!nonCommits.containsKey(o))
+			nonCommits.put(o, ref.getName());
+	}
+
+	private int minCommitTime() throws IOException {
+		int min = Integer.MAX_VALUE;
+		for (ObjectId id : revs) {
+			RevObject o = walk.parseAny(id);
+			while (o instanceof RevTag) {
+				o = ((RevTag) o).getObject();
+				walk.parseHeaders(o);
+			}
+			if (o instanceof RevCommit) {
+				RevCommit c = (RevCommit) o;
+				if (c.getCommitTime() < min)
+					min = c.getCommitTime();
+			}
+		}
+		return min;
+	}
+
+	private long compare(String leftTip, long leftCost, String rightTip, long rightCost) {
+		long c = leftCost - rightCost;
+		if (c != 0 || prefixes.isEmpty())
+			return c;
+		int li = -1;
+		int ri = -1;
+		for (int i = 0; i < prefixes.size(); i++) {
+			String prefix = prefixes.get(i);
+			if (li < 0 && leftTip.startsWith(prefix))
+				li = i;
+			if (ri < 0 && rightTip.startsWith(prefix))
+				ri = i;
+		}
+		// Don't tiebreak if prefixes are the same, in order to prefer first-parent
+		// paths.
+		return li - ri;
+	}
+
+	private static String simplify(String refName) {
+		if (refName.startsWith(Constants.R_HEADS))
+			return refName.substring(Constants.R_HEADS.length());
+		if (refName.startsWith(Constants.R_TAGS))
+			return refName.substring(Constants.R_TAGS.length());
+		if (refName.startsWith(Constants.R_REFS))
+			return refName.substring(Constants.R_REFS.length());
+		return refName;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
index 1a4058e..c719f0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
@@ -43,6 +43,7 @@
 package org.eclipse.jgit.api;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -92,6 +93,8 @@ public class PushCommand extends
 
 	private boolean thin = Transport.DEFAULT_PUSH_THIN;
 
+	private OutputStream out;
+
 	/**
 	 * @param repo
 	 */
@@ -150,7 +153,7 @@ public class PushCommand extends
 						.findRemoteRefUpdatesFor(refSpecs);
 
 				try {
-					PushResult result = transport.push(monitor, toPush);
+					PushResult result = transport.push(monitor, toPush, out);
 					pushResults.add(result);
 
 				} catch (TransportException e) {
@@ -404,4 +407,16 @@ public class PushCommand extends
 		this.force = force;
 		return this;
 	}
+
+	/**
+	 * Sets the output stream to write sideband messages to
+	 *
+	 * @param out
+	 * @return {@code this}
+	 * @since 3.0
+	 */
+	public PushCommand setOutputStream(OutputStream out) {
+		this.out = out;
+		return this;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 5158c85..911a4e6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler at sap.com>
+ * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -108,11 +108,16 @@ import org.eclipse.jgit.util.RawParseUtils;
  */
 public class RebaseCommand extends GitCommand<RebaseResult> {
 	/**
-	 * The name of the "rebase-merge" folder
+	 * The name of the "rebase-merge" folder for interactive rebases.
 	 */
 	public static final String REBASE_MERGE = "rebase-merge"; //$NON-NLS-1$
 
 	/**
+	 * The name of the "rebase-apply" folder for non-interactive rebases.
+	 */
+	private static final String REBASE_APPLY = "rebase-apply"; //$NON-NLS-1$
+
+	/**
 	 * The name of the "stopped-sha" file
 	 */
 	public static final String STOPPED_SHA = "stopped-sha"; //$NON-NLS-1$
@@ -177,7 +182,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 
 	private final RevWalk walk;
 
-	private final File rebaseDir;
+	private final RebaseState rebaseState;
 
 	private InteractiveHandler interactiveHandler;
 
@@ -187,7 +192,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 	protected RebaseCommand(Repository repo) {
 		super(repo);
 		walk = new RevWalk(repo);
-		rebaseDir = new File(repo.getDirectory(), REBASE_MERGE);
+		rebaseState = new RebaseState(repo.getDirectory());
 	}
 
 	/**
@@ -219,9 +224,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			case SKIP:
 				// fall through
 			case CONTINUE:
-				String upstreamCommitId = readFile(rebaseDir, ONTO);
+				String upstreamCommitId = rebaseState.readFile(ONTO);
 				try {
-					upstreamCommitName = readFile(rebaseDir, ONTO_NAME);
+					upstreamCommitName = rebaseState.readFile(ONTO_NAME);
 				} catch (FileNotFoundException e) {
 					// Fall back to commit ID if file doesn't exist (e.g. rebase
 					// was started by C Git)
@@ -242,7 +247,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			if (operation == Operation.CONTINUE) {
 				newHead = continueRebase();
 
-				File amendFile = new File(rebaseDir, AMEND);
+				File amendFile = rebaseState.getFile(AMEND);
 				boolean amendExists = amendFile.exists();
 				if (amendExists) {
 					FileUtils.delete(amendFile);
@@ -265,9 +270,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			List<Step> steps = loadSteps();
 			if (isInteractive()) {
 				interactiveHandler.prepareSteps(steps);
-				BufferedWriter fw = new BufferedWriter(
-						new OutputStreamWriter(new FileOutputStream(new File(
-								rebaseDir, GIT_REBASE_TODO)),
+				BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(
+						new FileOutputStream(
+								rebaseState.getFile(GIT_REBASE_TODO)),
 								Constants.CHARACTER_ENCODING));
 				fw.newLine();
 				try {
@@ -339,7 +344,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 								.setAmend(true).call();
 						continue;
 					case EDIT:
-						createFile(rebaseDir, AMEND, commitToPick.name());
+						rebaseState.createFile(AMEND, commitToPick.name());
 						return stop(commitToPick);
 					}
 				} finally {
@@ -347,9 +352,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 				}
 			}
 			if (newHead != null) {
-				String headName = readFile(rebaseDir, HEAD_NAME);
+				String headName = rebaseState.readFile(HEAD_NAME);
 				updateHead(headName, newHead);
-				FileUtils.delete(rebaseDir, FileUtils.RECURSIVE);
+				FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
 				if (lastStepWasForward)
 					return RebaseResult.FAST_FORWARD_RESULT;
 				return RebaseResult.OK_RESULT;
@@ -458,7 +463,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 
 		if (needsCommit) {
 			CommitCommand commit = new Git(repo).commit();
-			commit.setMessage(readFile(rebaseDir, MESSAGE));
+			commit.setMessage(rebaseState.readFile(MESSAGE));
 			commit.setAuthor(parseAuthor());
 			return commit.call();
 		}
@@ -466,7 +471,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 	}
 
 	private PersonIdent parseAuthor() throws IOException {
-		File authorScriptFile = new File(rebaseDir, AUTHOR_SCRIPT);
+		File authorScriptFile = rebaseState.getFile(AUTHOR_SCRIPT);
 		byte[] raw;
 		try {
 			raw = IO.readFully(authorScriptFile);
@@ -479,15 +484,17 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 	private RebaseResult stop(RevCommit commitToPick) throws IOException {
 		PersonIdent author = commitToPick.getAuthorIdent();
 		String authorScript = toAuthorScript(author);
-		createFile(rebaseDir, AUTHOR_SCRIPT, authorScript);
-		createFile(rebaseDir, MESSAGE, commitToPick.getFullMessage());
+		rebaseState.createFile(AUTHOR_SCRIPT, authorScript);
+		rebaseState.createFile(MESSAGE, commitToPick.getFullMessage());
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
 		DiffFormatter df = new DiffFormatter(bos);
 		df.setRepository(repo);
 		df.format(commitToPick.getParent(0), commitToPick);
-		createFile(rebaseDir, PATCH, new String(bos.toByteArray(),
+		rebaseState.createFile(PATCH, new String(bos.toByteArray(),
 				Constants.CHARACTER_ENCODING));
-		createFile(rebaseDir, STOPPED_SHA, repo.newObjectReader().abbreviate(
+		rebaseState.createFile(STOPPED_SHA,
+				repo.newObjectReader()
+				.abbreviate(
 				commitToPick).name());
 		// Remove cherry pick state file created by CherryPickCommand, it's not
 		// needed for rebase
@@ -531,8 +538,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			return;
 		List<String> todoLines = new ArrayList<String>();
 		List<String> poppedLines = new ArrayList<String>();
-		File todoFile = new File(rebaseDir, GIT_REBASE_TODO);
-		File doneFile = new File(rebaseDir, DONE);
+		File todoFile = rebaseState.getFile(GIT_REBASE_TODO);
+		File doneFile = rebaseState.getFile(DONE);
 		BufferedReader br = new BufferedReader(new InputStreamReader(
 				new FileInputStream(todoFile), Constants.CHARACTER_ENCODING));
 		try {
@@ -640,26 +647,22 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 		List<RevCommit> cherryPickList = new ArrayList<RevCommit>();
 		for (RevCommit commit : commitsToUse) {
 			if (commit.getParentCount() != 1)
-				throw new JGitInternalException(
-						MessageFormat.format(
-								JGitText.get().canOnlyCherryPickCommitsWithOneParent,
-								commit.name(),
-								Integer.valueOf(commit.getParentCount())));
+				continue;
 			cherryPickList.add(commit);
 		}
 
 		Collections.reverse(cherryPickList);
 		// create the folder for the meta information
-		FileUtils.mkdir(rebaseDir);
+		FileUtils.mkdir(rebaseState.getDir());
 
 		repo.writeOrigHead(headId);
-		createFile(rebaseDir, REBASE_HEAD, headId.name());
-		createFile(rebaseDir, HEAD_NAME, headName);
-		createFile(rebaseDir, ONTO, upstreamCommit.name());
-		createFile(rebaseDir, ONTO_NAME, upstreamCommitName);
-		createFile(rebaseDir, INTERACTIVE, ""); //$NON-NLS-1$
+		rebaseState.createFile(REBASE_HEAD, headId.name());
+		rebaseState.createFile(HEAD_NAME, headName);
+		rebaseState.createFile(ONTO, upstreamCommit.name());
+		rebaseState.createFile(ONTO_NAME, upstreamCommitName);
+		rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
 		BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(
-				new FileOutputStream(new File(rebaseDir, GIT_REBASE_TODO)),
+				new FileOutputStream(rebaseState.getFile(GIT_REBASE_TODO)),
 				Constants.CHARACTER_ENCODING));
 		fw.write("# Created by EGit: rebasing " + upstreamCommit.name()
 				+ " onto " + headId.name());
@@ -691,7 +694,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			checkoutOk = checkoutCommit(upstreamCommit);
 		} finally {
 			if (!checkoutOk)
-				FileUtils.delete(rebaseDir, FileUtils.RECURSIVE);
+				FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
 		}
 		monitor.endTask();
 
@@ -803,18 +806,6 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			}
 	}
 
-	private void createFile(File parentDir, String name, String content)
-			throws IOException {
-		File file = new File(parentDir, name);
-		FileOutputStream fos = new FileOutputStream(file);
-		try {
-			fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
-			fos.write('\n');
-		} finally {
-			fos.close();
-		}
-	}
-
 	private RebaseResult abort(RebaseResult result) throws IOException {
 		try {
 			ObjectId origHead = repo.readOrigHead();
@@ -844,7 +835,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			monitor.endTask();
 		}
 		try {
-			String headName = readFile(rebaseDir, HEAD_NAME);
+			String headName = rebaseState.readFile(HEAD_NAME);
 			if (headName.startsWith(Constants.R_REFS)) {
 				monitor.beginTask(MessageFormat.format(
 						JGitText.get().resettingHead, headName),
@@ -864,7 +855,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 				}
 			}
 			// cleanup the files
-			FileUtils.delete(rebaseDir, FileUtils.RECURSIVE);
+			FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
 			repo.writeCherryPickHead(null);
 			return result;
 
@@ -873,15 +864,6 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 		}
 	}
 
-	private String readFile(File directory, String fileName) throws IOException {
-		byte[] content = IO.readFully(new File(directory, fileName));
-		// strip off the last LF
-		int end = content.length;
-		while (0 < end && content[end - 1] == '\n')
-			end--;
-		return RawParseUtils.decode(content, 0, end);
-	}
-
 	private boolean checkoutCommit(RevCommit commit) throws IOException,
 			CheckoutConflictException {
 		try {
@@ -915,7 +897,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 	}
 
 	List<Step> loadSteps() throws IOException {
-		byte[] buf = IO.readFully(new File(rebaseDir, GIT_REBASE_TODO));
+		byte[] buf = IO.readFully(rebaseState.getFile(GIT_REBASE_TODO));
 		int ptr = 0;
 		int tokenBegin = 0;
 		ArrayList<Step> r = new ArrayList<Step>();
@@ -1229,4 +1211,60 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 			return new PersonIdent(name, email, when, tz);
 		return null;
 	}
+
+	private static class RebaseState {
+
+		private final File repoDirectory;
+		private File dir;
+
+		public RebaseState(File repoDirectory) {
+			this.repoDirectory = repoDirectory;
+		}
+
+		public File getDir() {
+			if (dir == null) {
+				File rebaseApply = new File(repoDirectory, REBASE_APPLY);
+				if (rebaseApply.exists()) {
+					dir = rebaseApply;
+				} else {
+					File rebaseMerge = new File(repoDirectory, REBASE_MERGE);
+					dir = rebaseMerge;
+				}
+			}
+			return dir;
+		}
+
+		public String readFile(String name) throws IOException {
+			return readFile(getDir(), name);
+		}
+
+		public void createFile(String name, String content) throws IOException {
+			createFile(getDir(), name, content);
+		}
+
+		public File getFile(String name) {
+			return new File(getDir(), name);
+		}
+
+		private static String readFile(File directory, String fileName)
+				throws IOException {
+			byte[] content = IO.readFully(new File(directory, fileName));
+			// strip off the last LF
+			int end = RawParseUtils.prevLF(content, content.length);
+			return RawParseUtils.decode(content, 0, end + 1);
+		}
+
+		private static void createFile(File parentDir, String name,
+				String content)
+				throws IOException {
+			File file = new File(parentDir, name);
+			FileOutputStream fos = new FileOutputStream(file);
+			try {
+				fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
+				fos.write('\n');
+			} finally {
+				fos.close();
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
index ef344b5..8cd78ae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
@@ -50,9 +50,9 @@ import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidRefNameException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.eclipse.jgit.storage.file.ReflogReader;
 
 /**
  * The reflog command
@@ -96,7 +96,7 @@ public class ReflogCommand extends GitCommand<Collection<ReflogEntry>> {
 		checkCallable();
 
 		try {
-			ReflogReader reader = new ReflogReader(repo, ref);
+			ReflogReader reader = repo.getReflogReader(ref);
 			return reader.getReverseEntries();
 		} catch (IOException e) {
 			throw new InvalidRefNameException(MessageFormat.format(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 16522b7..c392209 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -160,11 +160,12 @@ public class RevertCommand extends GitCommand<RevCommit> {
 				String revertName = srcCommit.getId().abbreviate(7).name()
 						+ " " + srcCommit.getShortMessage(); //$NON-NLS-1$
 
-				ResolveMerger merger = (ResolveMerger) MergeStrategy.RESOLVE
+				ResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE
 						.newMerger(repo);
 				merger.setWorkingTreeIterator(new FileTreeIterator(repo));
 				merger.setBase(srcCommit.getTree());
-				merger.setCommitNames(new String[] { "BASE", ourName, revertName }); //$NON-NLS-1$ //$NON-NLS-2$
+				merger.setCommitNames(new String[] {
+						"BASE", ourName, revertName }); //$NON-NLS-1$
 
 				String shortMessage = "Revert \"" + srcCommit.getShortMessage() //$NON-NLS-1$
 						+ "\""; //$NON-NLS-1$
@@ -193,14 +194,15 @@ public class RevertCommand extends GitCommand<RevCommit> {
 								merger.getBaseCommit(0, 1),
 								new ObjectId[] { headCommit.getId(),
 										srcParent.getId() },
-								MergeStatus.FAILED, MergeStrategy.RESOLVE,
+								MergeStatus.FAILED, MergeStrategy.RECURSIVE,
 								merger.getMergeResults(), failingPaths, null);
 					else
 						failingResult = new MergeResult(null,
 								merger.getBaseCommit(0, 1),
 								new ObjectId[] { headCommit.getId(),
 										srcParent.getId() },
-								MergeStatus.CONFLICTING, MergeStrategy.RESOLVE,
+								MergeStatus.CONFLICTING,
+								MergeStrategy.RECURSIVE,
 								merger.getMergeResults(), failingPaths, null);
 					if (!merger.failed() && !unmergedPaths.isEmpty()) {
 						String message = new MergeMessageFormatter()
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index f515609..b1fde1d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -168,7 +168,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
 					.getParent(1));
 			ObjectId stashHeadCommit = stashCommit.getParent(0);
 
-			ResolveMerger merger = (ResolveMerger) MergeStrategy.RESOLVE
+			ResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE
 					.newMerger(repo);
 			merger.setCommitNames(new String[] { "stashed HEAD", "HEAD",
 					"stash" });
@@ -181,7 +181,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
 				dco.setFailOnConflict(true);
 				dco.checkout(); // Ignoring failed deletes....
 				if (applyIndex) {
-					ResolveMerger ixMerger = (ResolveMerger) MergeStrategy.RESOLVE
+					ResolveMerger ixMerger = (ResolveMerger) MergeStrategy.RECURSIVE
 							.newMerger(repo, true);
 					ixMerger.setCommitNames(new String[] { "stashed HEAD",
 							"HEAD", "stashed index" });
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
index 375dee0..fc21b91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -241,6 +241,7 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
 				MutableObjectId id = new MutableObjectId();
 				List<PathEdit> wtEdits = new ArrayList<PathEdit>();
 				List<String> wtDeletes = new ArrayList<String>();
+				boolean hasChanges = false;
 				do {
 					AbstractTreeIterator headIter = treeWalk.getTree(0,
 							AbstractTreeIterator.class);
@@ -248,13 +249,18 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
 							DirCacheIterator.class);
 					WorkingTreeIterator wtIter = treeWalk.getTree(2,
 							WorkingTreeIterator.class);
-					if (headIter != null && indexIter != null && wtIter != null) {
-						if (!indexIter.getDirCacheEntry().isMerged())
-							throw new UnmergedPathsException(
-									new UnmergedPathException(
-											indexIter.getDirCacheEntry()));
-						if (wtIter.idEqual(indexIter)
-								|| wtIter.idEqual(headIter))
+					if (indexIter != null
+							&& !indexIter.getDirCacheEntry().isMerged())
+						throw new UnmergedPathsException(
+								new UnmergedPathException(
+										indexIter.getDirCacheEntry()));
+					if (wtIter != null) {
+						if (indexIter == null && headIter == null)
+							continue;
+						hasChanges = true;
+						if (indexIter != null && wtIter.idEqual(indexIter))
+							continue;
+						if (headIter != null && wtIter.idEqual(headIter))
 							continue;
 						treeWalk.getObjectId(id, 0);
 						final DirCacheEntry entry = new DirCacheEntry(
@@ -271,17 +277,19 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
 							in.close();
 						}
 						wtEdits.add(new PathEdit(entry) {
-
 							public void apply(DirCacheEntry ent) {
 								ent.copyMetaData(entry);
 							}
 						});
-					} else if (indexIter == null)
-						wtDeletes.add(treeWalk.getPathString());
-					else if (wtIter == null && headIter != null)
+					}
+					hasChanges = true;
+					if (wtIter == null && headIter != null)
 						wtDeletes.add(treeWalk.getPathString());
 				} while (treeWalk.next());
 
+				if (!hasChanges)
+					return null;
+
 				String branch = Repository.shortenRefName(head.getTarget()
 						.getName());
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index dde736b..e4431c3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -54,14 +54,14 @@ import org.eclipse.jgit.api.errors.InvalidRefNameException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.ReflogWriter;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.eclipse.jgit.storage.file.ReflogReader;
-import org.eclipse.jgit.storage.file.ReflogWriter;
 import org.eclipse.jgit.util.FileUtils;
 
 /**
@@ -181,9 +181,9 @@ public class StashDropCommand extends GitCommand<ObjectId> {
 			return null;
 		}
 
-		ReflogReader reader = new ReflogReader(repo, R_STASH);
 		List<ReflogEntry> entries;
 		try {
+			ReflogReader reader = repo.getReflogReader(R_STASH);
 			entries = reader.getReverseEntries();
 		} catch (IOException e) {
 			throw new JGitInternalException(JGitText.get().stashDropFailed, e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
index 6977905..bbbb7ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
@@ -54,10 +54,10 @@ import org.eclipse.jgit.api.errors.InvalidRefNameException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.ReflogEntry;
 
 /**
  * Command class to list the stashed commits in a repository.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
index cb2ae6b..e840c2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Christian Halstrick <christian.halstrick at sap.com>
+ * Copyright (C) 2011, 2013 Christian Halstrick <christian.halstrick at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,9 +43,11 @@
 package org.eclipse.jgit.api;
 
 import java.util.Collections;
+import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.jgit.lib.IndexDiff;
+import org.eclipse.jgit.lib.IndexDiff.StageState;
 
 /**
  * A class telling where the working-tree, the index and the current HEAD differ
@@ -153,6 +155,14 @@ public class Status {
 	}
 
 	/**
+	 * @return a map from conflicting path to its {@link StageState}.
+	 * @since 3.0
+	 */
+	public Map<String, StageState> getConflictingStageState() {
+		return Collections.unmodifiableMap(diff.getConflictingStageStates());
+	}
+
+	/**
 	 * @return set of files and folders that are ignored and not in the index.
 	 */
 	public Set<String> getIgnoredNotInIndex() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 1784acd..8570baa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk at gmail.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -66,9 +66,24 @@ import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
 
 /**
- * A class used to execute a {@code Tag} command. It has setters for all
- * supported options and arguments of this command and a {@link #call()} method
- * to finally execute the command.
+ * Create/update an annotated tag object or a simple unannotated tag
+ * <p>
+ * Examples (<code>git</code> is a {@link Git} instance):
+ * <p>
+ * Create a new tag for the current commit:
+ *
+ * <pre>
+ * git.tag().setName("v1.0").setMessage("First stable release").call();
+ * </pre>
+ * <p>
+ *
+ * <p>
+ * Create a new unannotated tag for the current commit:
+ *
+ * <pre>
+ * git.tag().setName("v1.0").setAnnotated(false).call();
+ * </pre>
+ * <p>
  *
  * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-tag.html"
  *      >Git documentation about Tag</a>
@@ -86,6 +101,8 @@ public class TagCommand extends GitCommand<Ref> {
 
 	private boolean forceUpdate;
 
+	private boolean annotated = true;
+
 	/**
 	 * @param repo
 	 */
@@ -111,13 +128,8 @@ public class TagCommand extends GitCommand<Ref> {
 		RepositoryState state = repo.getRepositoryState();
 		processOptions(state);
 
+		RevWalk revWalk = new RevWalk(repo);
 		try {
-			// create the tag object
-			TagBuilder newTag = new TagBuilder();
-			newTag.setTag(name);
-			newTag.setMessage(message);
-			newTag.setTagger(tagger);
-
 			// if no id is set, we should attempt to use HEAD
 			if (id == null) {
 				ObjectId objectId = repo.resolve(Constants.HEAD + "^{commit}"); //$NON-NLS-1$
@@ -125,47 +137,33 @@ public class TagCommand extends GitCommand<Ref> {
 					throw new NoHeadException(
 							JGitText.get().tagOnRepoWithoutHEADCurrentlyNotSupported);
 
-				newTag.setObjectId(objectId, Constants.OBJ_COMMIT);
-			} else {
-				newTag.setObjectId(id);
+				id = revWalk.parseCommit(objectId);
+			}
+
+			if (!annotated) {
+				if (message != null || tagger != null)
+					throw new JGitInternalException(
+							JGitText.get().messageAndTaggerNotAllowedInUnannotatedTags);
+				return updateTagRef(id, revWalk, name,
+						"SimpleTag[" + name + " : " + id //$NON-NLS-1$ //$NON-NLS-2$
+								+ "]"); //$NON-NLS-1$
 			}
 
+			// create the tag object
+			TagBuilder newTag = new TagBuilder();
+			newTag.setTag(name);
+			newTag.setMessage(message);
+			newTag.setTagger(tagger);
+			newTag.setObjectId(id);
+
 			// write the tag object
 			ObjectInserter inserter = repo.newObjectInserter();
 			try {
 				ObjectId tagId = inserter.insert(newTag);
 				inserter.flush();
 
-				RevWalk revWalk = new RevWalk(repo);
-				try {
-					String refName = Constants.R_TAGS + newTag.getTag();
-					RefUpdate tagRef = repo.updateRef(refName);
-					tagRef.setNewObjectId(tagId);
-					tagRef.setForceUpdate(forceUpdate);
-					tagRef.setRefLogMessage("tagged " + name, false); //$NON-NLS-1$
-					Result updateResult = tagRef.update(revWalk);
-					switch (updateResult) {
-					case NEW:
-					case FORCED:
-						return repo.getRef(refName);
-					case LOCK_FAILURE:
-						throw new ConcurrentRefUpdateException(
-								JGitText.get().couldNotLockHEAD,
-								tagRef.getRef(), updateResult);
-					case REJECTED:
-						throw new RefAlreadyExistsException(
-								MessageFormat.format(
-										JGitText.get().tagAlreadyExists,
-										newTag.toString()));
-					default:
-						throw new JGitInternalException(MessageFormat.format(
-								JGitText.get().updatingRefFailed, refName,
-								newTag.toString(), updateResult));
-					}
-
-				} finally {
-					revWalk.release();
-				}
+				String tag = newTag.getTag();
+				return updateTagRef(tagId, revWalk, tag, newTag.toString());
 
 			} finally {
 				inserter.release();
@@ -175,6 +173,35 @@ public class TagCommand extends GitCommand<Ref> {
 			throw new JGitInternalException(
 					JGitText.get().exceptionCaughtDuringExecutionOfTagCommand,
 					e);
+		} finally {
+			revWalk.release();
+		}
+	}
+
+	private Ref updateTagRef(ObjectId tagId, RevWalk revWalk,
+			String tagName, String newTagToString) throws IOException,
+			ConcurrentRefUpdateException, RefAlreadyExistsException {
+		String refName = Constants.R_TAGS + tagName;
+		RefUpdate tagRef = repo.updateRef(refName);
+		tagRef.setNewObjectId(tagId);
+		tagRef.setForceUpdate(forceUpdate);
+		tagRef.setRefLogMessage("tagged " + name, false); //$NON-NLS-1$
+		Result updateResult = tagRef.update(revWalk);
+		switch (updateResult) {
+		case NEW:
+		case FORCED:
+			return repo.getRef(refName);
+		case LOCK_FAILURE:
+			throw new ConcurrentRefUpdateException(
+					JGitText.get().couldNotLockHEAD, tagRef.getRef(),
+					updateResult);
+		case REJECTED:
+			throw new RefAlreadyExistsException(MessageFormat.format(
+					JGitText.get().tagAlreadyExists, newTagToString));
+		default:
+			throw new JGitInternalException(MessageFormat.format(
+					JGitText.get().updatingRefFailed, refName, newTagToString,
+					updateResult));
 		}
 	}
 
@@ -192,7 +219,7 @@ public class TagCommand extends GitCommand<Ref> {
 	 */
 	private void processOptions(RepositoryState state)
 			throws InvalidTagNameException {
-		if (tagger == null)
+		if (tagger == null && annotated)
 			tagger = new PersonIdent(repo);
 		if (name == null || !Repository.isValidRefName(Constants.R_TAGS + name))
 			throw new InvalidTagNameException(MessageFormat.format(JGitText
@@ -314,4 +341,22 @@ public class TagCommand extends GitCommand<Ref> {
 		return this;
 	}
 
+	/**
+	 * @param annotated
+	 * @return {@code this}
+	 * @since 3.0
+	 */
+	public TagCommand setAnnotated(boolean annotated) {
+		this.annotated = annotated;
+		return this;
+	}
+
+	/**
+	 * @return true if this command will create an annotated tag (default is
+	 *         true)
+	 * @since 3.0
+	 */
+	public boolean isAnnotated() {
+		return annotated;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
index 1387400..b1cbb91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
@@ -48,6 +48,7 @@ import java.text.MessageFormat;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Config.SectionParser;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.util.StringUtils;
 
 /** Keeps track of diff related configuration options. */
@@ -78,10 +79,12 @@ public class DiffConfig {
 	private final int renameLimit;
 
 	private DiffConfig(final Config rc) {
-		noPrefix = rc.getBoolean("diff", "noprefix", false); //$NON-NLS-1$ //$NON-NLS-2$
-		renameDetectionType = parseRenameDetectionType(rc.getString("diff", //$NON-NLS-1$
-				null, "renames")); //$NON-NLS-1$
-		renameLimit = rc.getInt("diff", "renamelimit", 200); //$NON-NLS-1$ //$NON-NLS-2$
+		noPrefix = rc.getBoolean(ConfigConstants.CONFIG_DIFF_SECTION,
+				ConfigConstants.CONFIG_KEY_NOPREFIX, false);
+		renameDetectionType = parseRenameDetectionType(rc.getString(
+				ConfigConstants.CONFIG_DIFF_SECTION, null, ConfigConstants.CONFIG_KEY_RENAMES));
+		renameLimit = rc.getInt(ConfigConstants.CONFIG_DIFF_SECTION,
+				ConfigConstants.CONFIG_KEY_RENAMELIMIT, 200);
 	}
 
 	/** @return true if the prefix "a/" and "b/" should be suppressed. */
@@ -108,16 +111,21 @@ public class DiffConfig {
 			final String renameString) {
 		if (renameString == null)
 			return RenameDetectionType.FALSE;
-		else if (StringUtils.equalsIgnoreCase("copy", renameString) //$NON-NLS-1$
-				|| StringUtils.equalsIgnoreCase("copies", renameString)) //$NON-NLS-1$
+		else if (StringUtils.equalsIgnoreCase(
+				ConfigConstants.CONFIG_RENAMELIMIT_COPY, renameString)
+				|| StringUtils
+						.equalsIgnoreCase(
+								ConfigConstants.CONFIG_RENAMELIMIT_COPIES,
+								renameString))
 			return RenameDetectionType.COPY;
 		else {
 			final Boolean renameBoolean = StringUtils
 					.toBooleanOrNull(renameString);
 			if (renameBoolean == null)
 				throw new IllegalArgumentException(MessageFormat.format(
-						JGitText.get().enumValueNotSupported2, "diff", //$NON-NLS-1$
-						"renames", renameString)); //$NON-NLS-1$
+						JGitText.get().enumValueNotSupported2,
+						ConfigConstants.CONFIG_DIFF_SECTION,
+						ConfigConstants.CONFIG_KEY_RENAMES, renameString));
 			else if (renameBoolean.booleanValue())
 				return RenameDetectionType.TRUE;
 			else
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
index a3d4e09..06b2aec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
@@ -308,6 +308,8 @@ public class DiffEntry {
 		r.changeType = changeType;
 		r.score = score;
 
+		r.treeFilterMarks = src.treeFilterMarks | dst.treeFilterMarks;
+
 		return r;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index fe0db33..f660d6b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -119,6 +119,8 @@ public class DiffFormatter {
 
 	private ObjectReader reader;
 
+	private DiffConfig diffCfg;
+
 	private int context = 3;
 
 	private int abbreviationLength = 7;
@@ -173,6 +175,7 @@ public class DiffFormatter {
 
 		db = repository;
 		reader = db.newObjectReader();
+		diffCfg = db.getConfig().get(DiffConfig.KEY);
 
 		ContentSource cs = ContentSource.create(reader);
 		source = new ContentSource.Pair(cs, cs);
@@ -534,7 +537,7 @@ public class DiffFormatter {
 		String oldPath = ((FollowFilter) pathFilter).getPath();
 		for (DiffEntry ent : files) {
 			if (isRename(ent) && ent.getNewPath().equals(oldPath)) {
-				pathFilter = FollowFilter.create(ent.getOldPath());
+				pathFilter = FollowFilter.create(ent.getOldPath(), diffCfg);
 				return Collections.singletonList(ent);
 			}
 		}
@@ -909,6 +912,11 @@ public class DiffFormatter {
 			editList = new EditList();
 			type = PatchType.UNIFIED;
 
+		} else if (ent.getOldId() == null || ent.getNewId() == null) {
+			// Content not changed (e.g. only mode, pure rename)
+			editList = new EditList();
+			type = PatchType.UNIFIED;
+
 		} else {
 			assertHaveRepository();
 
@@ -1103,7 +1111,7 @@ public class DiffFormatter {
 			o.write('\n');
 		}
 
-		if (!ent.getOldId().equals(ent.getNewId())) {
+		if (ent.getOldId() != null && !ent.getOldId().equals(ent.getNewId())) {
 			formatIndexLine(o, ent);
 		}
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index f229a04..b819ad0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -111,7 +111,7 @@ public class RenameDetector {
 
 	private boolean done;
 
-	private final Repository repo;
+	private final ObjectReader objectReader;
 
 	/** Similarity score required to pair an add/delete as a rename. */
 	private int renameScore = 60;
@@ -136,11 +136,21 @@ public class RenameDetector {
 	 *            the repository to use for rename detection
 	 */
 	public RenameDetector(Repository repo) {
-		this.repo = repo;
+		this(repo.newObjectReader(), repo.getConfig().get(DiffConfig.KEY));
+	}
 
-		DiffConfig cfg = repo.getConfig().get(DiffConfig.KEY);
+	/**
+	 * Create a new rename detector with a specified reader and diff config.
+	 *
+	 * @param reader
+	 *            reader to obtain objects from the repository with.
+	 * @param cfg
+	 *            diff config specifying rename detection options.
+	 * @since 3.0
+	 */
+	public RenameDetector(ObjectReader reader, DiffConfig cfg) {
+		objectReader = reader.newReader();
 		renameLimit = cfg.getRenameLimit();
-
 		reset();
 	}
 
@@ -310,11 +320,10 @@ public class RenameDetector {
 	 */
 	public List<DiffEntry> compute(ProgressMonitor pm) throws IOException {
 		if (!done) {
-			ObjectReader reader = repo.newObjectReader();
 			try {
-				return compute(reader, pm);
+				return compute(objectReader, pm);
 			} finally {
-				reader.release();
+				objectReader.release();
 			}
 		}
 		return Collections.unmodifiableList(entries);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 353c237..275dfed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -68,12 +68,12 @@ import org.eclipse.jgit.errors.UnmergedPathException;
 import org.eclipse.jgit.events.IndexChangedEvent;
 import org.eclipse.jgit.events.IndexChangedListener;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileSnapshot;
+import org.eclipse.jgit.internal.storage.file.LockFile;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.FileSnapshot;
-import org.eclipse.jgit.storage.file.LockFile;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 292206e..6fb56ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -1126,15 +1126,12 @@ public class DirCacheCheckout {
 					fs.setExecute(tmpFile, false);
 			}
 		}
-		if (!tmpFile.renameTo(f)) {
-			// tried to rename which failed. Let' delete the target file and try
-			// again
-			FileUtils.delete(f);
-			if (!tmpFile.renameTo(f)) {
-				throw new IOException(MessageFormat.format(
-						JGitText.get().couldNotWriteFile, tmpFile.getPath(),
-						f.getPath()));
-			}
+		try {
+			FileUtils.rename(tmpFile, f);
+		} catch (IOException e) {
+			throw new IOException(MessageFormat.format(
+					JGitText.get().couldNotWriteFile, tmpFile.getPath(),
+					f.getPath()));
 		}
 		entry.setLastModified(f.lastModified());
 		if (opt.getAutoCRLF() != AutoCRLF.FALSE)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java
similarity index 50%
copy from org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java
index cda2b59..0c419d7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/NoMergeBaseException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2013, Christian Halstrick <christian.halstrick at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,103 +41,86 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.util.io;
+package org.eclipse.jgit.errors;
 
-import java.io.FilterInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
 import java.text.MessageFormat;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.merge.RecursiveMerger;
 
-/** InputStream with a configurable timeout. */
-public class TimeoutInputStream extends FilterInputStream {
-	private final InterruptTimer myTimer;
-
-	private int timeout;
-
-	/**
-	 * Wrap an input stream with a timeout on all read operations.
-	 *
-	 * @param src
-	 *            base input stream (to read from). The stream must be
-	 *            interruptible (most socket streams are).
-	 * @param timer
-	 *            timer to manage the timeouts during reads.
-	 */
-	public TimeoutInputStream(final InputStream src,
-			final InterruptTimer timer) {
-		super(src);
-		myTimer = timer;
-	}
+/**
+ * Exception thrown if a merge fails because no merge base could be determined.
+ *
+ * @since 3.0
+ */
+public class NoMergeBaseException extends IOException {
+	private static final long serialVersionUID = 1L;
 
-	/** @return number of milliseconds before aborting a read. */
-	public int getTimeout() {
-		return timeout;
-	}
+	private MergeBaseFailureReason reason;
 
 	/**
-	 * @param millis
-	 *            number of milliseconds before aborting a read. Must be > 0.
+	 * An enum listing the different reason why no merge base could be
+	 * determined.
 	 */
-	public void setTimeout(final int millis) {
-		if (millis < 0)
-			throw new IllegalArgumentException(MessageFormat.format(
-					JGitText.get().invalidTimeout, Integer.valueOf(millis)));
-		timeout = millis;
-	}
-
-	@Override
-	public int read() throws IOException {
-		try {
-			beginRead();
-			return super.read();
-		} catch (InterruptedIOException e) {
-			throw readTimedOut();
-		} finally {
-			endRead();
-		}
-	}
+	public static enum MergeBaseFailureReason {
+		/**
+		 * Multiple merge bases have been found (e.g. the commits to be merged
+		 * have multiple common predecessors) but the merge strategy doesn't
+		 * support this (e.g. ResolveMerge)
+		 */
+		MULTIPLE_MERGE_BASES_NOT_SUPPORTED,
 
-	@Override
-	public int read(byte[] buf) throws IOException {
-		return read(buf, 0, buf.length);
-	}
+		/**
+		 * The number of merge bases exceeds {@link RecursiveMerger#MAX_BASES}
+		 */
+		TOO_MANY_MERGE_BASES,
 
-	@Override
-	public int read(byte[] buf, int off, int cnt) throws IOException {
-		try {
-			beginRead();
-			return super.read(buf, off, cnt);
-		} catch (InterruptedIOException e) {
-			throw readTimedOut();
-		} finally {
-			endRead();
-		}
+		/**
+		 * In order to find a single merge base it may required to merge
+		 * together multiple common predecessors. If during these merges
+		 * conflicts occur the merge fails with this reason
+		 */
+		CONFLICTS_DURING_MERGE_BASE_CALCULATION
 	}
 
-	@Override
-	public long skip(long cnt) throws IOException {
-		try {
-			beginRead();
-			return super.skip(cnt);
-		} catch (InterruptedIOException e) {
-			throw readTimedOut();
-		} finally {
-			endRead();
-		}
-	}
 
-	private void beginRead() {
-		myTimer.begin(timeout);
+	/**
+	 * Construct a NoMergeBase exception
+	 *
+	 * @param reason
+	 *            the reason why no merge base could be found
+	 * @param message
+	 *            a text describing the problem
+	 */
+	public NoMergeBaseException(MergeBaseFailureReason reason, String message) {
+		super(MessageFormat.format(JGitText.get().noMergeBase,
+				reason.toString(), message));
+		this.reason = reason;
 	}
 
-	private void endRead() {
-		myTimer.end();
+	/**
+	 * Construct a NoMergeBase exception
+	 *
+	 * @param reason
+	 *            the reason why no merge base could be found
+	 * @param message
+	 *            a text describing the problem
+	 * @param why
+	 *            an exception causing this error
+	 */
+	public NoMergeBaseException(MergeBaseFailureReason reason, String message,
+			Throwable why) {
+		super(MessageFormat.format(JGitText.get().noMergeBase,
+				reason.toString(), message));
+		this.reason = reason;
+		initCause(why);
 	}
 
-	private static InterruptedIOException readTimedOut() {
-		return new InterruptedIOException(JGitText.get().readTimedOut);
+	/**
+	 * @return the reason why no merge base could be found
+	 */
+	public MergeBaseFailureReason getReason() {
+		return reason;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
index e9e3f4d..0bd035b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/StoredObjectRepresentationNotAvailableException.java
@@ -43,7 +43,7 @@
 
 package org.eclipse.jgit.errors;
 
-import org.eclipse.jgit.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
 
 /** A previously selected representation is no longer available. */
 public class StoredObjectRepresentationNotAvailableException extends Exception {
@@ -54,6 +54,7 @@ public class StoredObjectRepresentationNotAvailableException extends Exception {
 	 *
 	 * @param otp
 	 *            the object whose current representation is no longer present.
+	 * @since 3.0
 	 */
 	public StoredObjectRepresentationNotAvailableException(ObjectToPack otp) {
 		// Do nothing.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index d032bbc..d75ed4e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov at sap.com>
+ * Copyright (C) 2012, Research In Motion Limited
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -86,10 +87,13 @@ public class JGitText extends TranslationBundle {
 	/***/ public String bareRepositoryNoWorkdirAndIndex;
 	/***/ public String base64InputNotProperlyPadded;
 	/***/ public String baseLengthIncorrect;
+	/***/ public String bitmapMissingObject;
+	/***/ public String bitmapsMustBePrepared;
 	/***/ public String blameNotCommittedYet;
 	/***/ public String blobNotFound;
 	/***/ public String blobNotFoundForPath;
 	/***/ public String branchNameInvalid;
+	/***/ public String buildingBitmaps;
 	/***/ public String cachedPacksPreventsIndexCreation;
 	/***/ public String cachedPacksPreventsListingObjects;
 	/***/ public String cannotBeCombined;
@@ -182,6 +186,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String couldNotDeleteLockFileShouldNotHappen;
 	/***/ public String couldNotDeleteTemporaryIndexFileShouldNotHappen;
 	/***/ public String couldNotGetAdvertisedRef;
+	/***/ public String couldNotGetRepoStatistics;
 	/***/ public String couldNotLockHEAD;
 	/***/ public String couldNotReadIndexInOneGo;
 	/***/ public String couldNotReadObjectWhileParsingCommit;
@@ -237,6 +242,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String errorListing;
 	/***/ public String errorOccurredDuringUnpackingOnTheRemoteEnd;
 	/***/ public String errorReadingInfoRefs;
+	/***/ public String errorSymlinksNotSupported;
 	/***/ public String exceptionCaughtDuringExecutionOfAddCommand;
 	/***/ public String exceptionCaughtDuringExecutionOfCherryPickCommand;
 	/***/ public String exceptionCaughtDuringExecutionOfCommitCommand;
@@ -257,6 +263,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String expectedCharacterEncodingGuesses;
 	/***/ public String expectedEOFReceived;
 	/***/ public String expectedGot;
+	/***/ public String expectedLessThanGot;
 	/***/ public String expectedPktLineWithService;
 	/***/ public String expectedReceivedContentType;
 	/***/ public String expectedReportForRefNotReceived;
@@ -326,6 +333,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String invalidTimeout;
 	/***/ public String invalidURL;
 	/***/ public String invalidWildcards;
+	/***/ public String invalidRefSpec;
 	/***/ public String invalidWindowSize;
 	/***/ public String isAStaticFlagAndHasNorevWalkInstance;
 	/***/ public String JRELacksMD5Implementation;
@@ -349,6 +357,9 @@ public class JGitText extends TranslationBundle {
 	/***/ public String mergeStrategyAlreadyExistsAsDefault;
 	/***/ public String mergeStrategyDoesNotSupportHeads;
 	/***/ public String mergeUsingStrategyResultedInDescription;
+	/***/ public String mergeRecursiveReturnedNoCommit;
+	/***/ public String mergeRecursiveTooManyMergeBasesFor;
+	/***/ public String messageAndTaggerNotAllowedInUnannotatedTags;
 	/***/ public String minutesAgo;
 	/***/ public String missingAccesskey;
 	/***/ public String missingConfigurationForKey;
@@ -374,6 +385,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String noClosingBracket;
 	/***/ public String noHEADExistsAndNoExplicitStartingRevisionWasSpecified;
 	/***/ public String noHMACsupport;
+	/***/ public String noMergeBase;
 	/***/ public String noMergeHeadSpecified;
 	/***/ public String noSuchRef;
 	/***/ public String notABoolean;
@@ -394,6 +406,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String objectAtPathDoesNotHaveId;
 	/***/ public String objectIsCorrupt;
 	/***/ public String objectIsNotA;
+	/***/ public String objectNotFound;
 	/***/ public String objectNotFoundIn;
 	/***/ public String obtainingCommitsForCherryPick;
 	/***/ public String offsetWrittenDeltaBaseForObjectNotFoundInAPack;
@@ -482,6 +495,7 @@ public class JGitText extends TranslationBundle {
 	/***/ public String searchForReuse;
 	/***/ public String searchForSizes;
 	/***/ public String secondsAgo;
+	/***/ public String selectingCommits;
 	/***/ public String sequenceTooLargeForDiffAlgorithm;
 	/***/ public String serviceNotEnabledNoName;
 	/***/ public String serviceNotPermitted;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/BeforeDfsPackIndexLoadedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedEvent.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/BeforeDfsPackIndexLoadedEvent.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedEvent.java
index 07d7743..75ccb07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/BeforeDfsPackIndexLoadedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedEvent.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import org.eclipse.jgit.events.RepositoryEvent;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/BeforeDfsPackIndexLoadedListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/BeforeDfsPackIndexLoadedListener.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java
index ab9dd42..047c86f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/BeforeDfsPackIndexLoadedListener.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import org.eclipse.jgit.events.RepositoryListener;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DeltaBaseCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
index 7b313da..53c05f0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.lang.ref.SoftReference;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlock.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlock.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
index cec098e..1e447b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 import java.security.MessageDigest;
@@ -51,7 +51,7 @@ import java.util.zip.CRC32;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
-import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
 
 /** A cached slice of a {@link DfsPackFile}. */
 final class DfsBlock {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
similarity index 92%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlockCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index f5a1b86..a8d797d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -42,16 +42,13 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReferenceArray;
 import java.util.concurrent.locks.ReentrantLock;
@@ -116,8 +113,6 @@ public final class DfsBlockCache {
 		cache = nc;
 
 		if (oc != null) {
-			if (oc.readAheadService != null)
-				oc.readAheadService.shutdown();
 			for (DfsPackFile pack : oc.getPackFiles())
 				pack.key.cachedSize.set(0);
 		}
@@ -153,12 +148,6 @@ public final class DfsBlockCache {
 	/** As {@link #blockSize} is a power of 2, bits to shift for a / blockSize. */
 	private final int blockSizeShift;
 
-	/** Number of bytes to read-ahead from current read position. */
-	private final int readAheadLimit;
-
-	/** Thread pool to handle optimistic read-ahead. */
-	private final ThreadPoolExecutor readAheadService;
-
 	/** Cache of pack files, indexed by description. */
 	private final Map<DfsPackDescription, DfsPackFile> packCache;
 
@@ -209,9 +198,6 @@ public final class DfsBlockCache {
 		clockHand = new Ref<Object>(new DfsPackKey(), -1, 0, null);
 		clockHand.next = clockHand;
 
-		readAheadLimit = cfg.getReadAheadLimit();
-		readAheadService = cfg.getReadAheadService();
-
 		packCache = new ConcurrentHashMap<DfsPackDescription, DfsPackFile>(
 				16, 0.75f, 1);
 		packFiles = Collections.unmodifiableCollection(packCache.values());
@@ -498,32 +484,6 @@ public final class DfsBlockCache {
 		return val;
 	}
 
-	boolean readAhead(ReadableChannel rc, DfsPackKey key, int size, long pos,
-			long len, DfsReader ctx) {
-		if (!ctx.wantReadAhead() || readAheadLimit <= 0 || readAheadService == null)
-			return false;
-
-		int cap = readAheadLimit / size;
-		long readAheadEnd = pos + readAheadLimit;
-		List<ReadAheadTask.BlockFuture> blocks = new ArrayList<ReadAheadTask.BlockFuture>(cap);
-		while (pos < readAheadEnd && pos < len) {
-			long end = Math.min(pos + size, len);
-			if (!contains(key, pos))
-				blocks.add(new ReadAheadTask.BlockFuture(key, pos, end));
-			pos = end;
-		}
-		if (blocks.isEmpty())
-			return false;
-
-		ReadAheadTask task = new ReadAheadTask(this, rc, blocks);
-		ReadAheadTask.TaskFuture t = new ReadAheadTask.TaskFuture(task);
-		for (ReadAheadTask.BlockFuture b : blocks)
-			b.setTask(t);
-		readAheadService.execute(t);
-		ctx.startedReadAhead(blocks);
-		return true;
-	}
-
 	private <T> T scan(HashEntry n, DfsPackKey pack, long position) {
 		Ref<T> r = scanRef(n, pack, position);
 		return r != null ? r.get() : null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
similarity index 60%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlockCacheConfig.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index 45994aa..ca1451a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -41,21 +41,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_READ_AHEAD_LIMIT;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_READ_AHEAD_THREADS;
-
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import org.eclipse.jgit.lib.Config;
 
@@ -71,10 +62,6 @@ public class DfsBlockCacheConfig {
 
 	private int blockSize;
 
-	private int readAheadLimit;
-
-	private ThreadPoolExecutor readAheadService;
-
 	/** Create a default configuration. */
 	public DfsBlockCacheConfig() {
 		setBlockLimit(32 * MB);
@@ -118,40 +105,6 @@ public class DfsBlockCacheConfig {
 		return this;
 	}
 
-	/** @return number of bytes to read ahead sequentially by. */
-	public int getReadAheadLimit() {
-		return readAheadLimit;
-	}
-
-	/**
-	 * @param newSize
-	 *            new read-ahead limit, in bytes.
-	 * @return {@code this}
-	 */
-	public DfsBlockCacheConfig setReadAheadLimit(final int newSize) {
-		readAheadLimit = Math.max(0, newSize);
-		return this;
-	}
-
-	/** @return service to perform read-ahead of sequential blocks. */
-	public ThreadPoolExecutor getReadAheadService() {
-		return readAheadService;
-	}
-
-	/**
-	 * @param svc
-	 *            service to perform read-ahead of sequential blocks with. If
-	 *            not null the {@link RejectedExecutionHandler} must be managed
-	 *            by the JGit DFS library and not the application.
-	 * @return {@code this}.
-	 */
-	public DfsBlockCacheConfig setReadAheadService(ThreadPoolExecutor svc) {
-		if (svc != null)
-			svc.setRejectedExecutionHandler(ReadAheadRejectedExecutionHandler.INSTANCE);
-		readAheadService = svc;
-		return this;
-	}
-
 	/**
 	 * Update properties by setting fields from the configuration.
 	 * <p>
@@ -174,39 +127,6 @@ public class DfsBlockCacheConfig {
 				CONFIG_DFS_SECTION,
 				CONFIG_KEY_BLOCK_SIZE,
 				getBlockSize()));
-
-		setReadAheadLimit(rc.getInt(
-				CONFIG_CORE_SECTION,
-				CONFIG_DFS_SECTION,
-				CONFIG_KEY_READ_AHEAD_LIMIT,
-				getReadAheadLimit()));
-
-		int readAheadThreads = rc.getInt(
-				CONFIG_CORE_SECTION,
-				CONFIG_DFS_SECTION,
-				CONFIG_KEY_READ_AHEAD_THREADS,
-				0);
-
-		if (0 < getReadAheadLimit() && 0 < readAheadThreads) {
-			setReadAheadService(new ThreadPoolExecutor(
-					1, // Minimum number of threads kept alive.
-					readAheadThreads, // Maximum threads active.
-					60, TimeUnit.SECONDS, // Idle threads wait this long before ending.
-					new ArrayBlockingQueue<Runnable>(1), // Do not queue deeply.
-					new ThreadFactory() {
-						private final String name = "JGit-DFS-ReadAhead"; //$NON-NLS-1$
-						private final AtomicInteger cnt = new AtomicInteger();
-						private final ThreadGroup group = new ThreadGroup(name);
-
-						public Thread newThread(Runnable body) {
-							int id = cnt.incrementAndGet();
-							Thread thread = new Thread(group, body, name + "-" + id); //$NON-NLS-1$
-							thread.setDaemon(true);
-							thread.setContextClassLoader(getClass().getClassLoader());
-							return thread;
-						}
-					}, ReadAheadRejectedExecutionHandler.INSTANCE));
-		}
 		return this;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
similarity index 87%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsCachedPack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
index e0ecc80..3da5184 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
@@ -41,16 +41,14 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
-import java.util.Set;
 
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 
 /** A DfsPackFile available for reuse as-is. */
 public class DfsCachedPack extends CachedPack {
@@ -66,11 +64,6 @@ public class DfsCachedPack extends CachedPack {
 	}
 
 	@Override
-	public Set<ObjectId> getTips() {
-		return getPackDescription().getTips();
-	}
-
-	@Override
 	public long getObjectCount() throws IOException {
 		return getPackDescription().getObjectCount();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsConfig.java
similarity index 97%
copy from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsConfig.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsConfig.java
index fa91b7c..696595c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsConfig.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
similarity index 73%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index 76fb521..b5ea6b5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -41,22 +41,26 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import static org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource.GC;
-import static org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
-import static org.eclipse.jgit.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -65,10 +69,7 @@ import org.eclipse.jgit.lib.ObjectIdOwnerMap;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource;
-import org.eclipse.jgit.storage.file.PackIndex;
 import org.eclipse.jgit.storage.pack.PackConfig;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.util.io.CountingOutputStream;
 
 /** Repack and garbage collect a repository. */
@@ -89,6 +90,8 @@ public class DfsGarbageCollector {
 
 	private PackConfig packConfig;
 
+	private long coalesceGarbageLimit = 50 << 20;
+
 	private Map<String, Ref> refsBefore;
 
 	private List<DfsPackFile> packsBefore;
@@ -97,12 +100,6 @@ public class DfsGarbageCollector {
 
 	private Set<ObjectId> nonHeads;
 
-	/** Sum of object counts in {@link #packsBefore}. */
-	private long objectsBefore;
-
-	/** Sum of object counts iN {@link #newPackDesc}. */
-	private long objectsPacked;
-
 	private Set<ObjectId> tagTargets;
 
 	/**
@@ -138,6 +135,38 @@ public class DfsGarbageCollector {
 		return this;
 	}
 
+	/** @return garbage packs smaller than this size will be repacked. */
+	public long getCoalesceGarbageLimit() {
+		return coalesceGarbageLimit;
+	}
+
+	/**
+	 * Set the byte size limit for garbage packs to be repacked.
+	 * <p>
+	 * Any UNREACHABLE_GARBAGE pack smaller than this limit will be repacked at
+	 * the end of the run. This allows the garbage collector to coalesce
+	 * unreachable objects into a single file.
+	 * <p>
+	 * If an UNREACHABLE_GARBAGE pack is already larger than this limit it will
+	 * be left alone by the garbage collector. This avoids unnecessary disk IO
+	 * reading and copying the objects.
+	 * <p>
+	 * If limit is set to 0 the UNREACHABLE_GARBAGE coalesce is disabled.<br>
+	 * If limit is set to {@link Long#MAX_VALUE}, everything is coalesced.
+	 * <p>
+	 * Keeping unreachable garbage prevents race conditions with repository
+	 * changes that may suddenly need an object whose only copy was stored in
+	 * the UNREACHABLE_GARBAGE pack.
+	 *
+	 * @param limit
+	 *            size in bytes.
+	 * @return {@code this}
+	 */
+	public DfsGarbageCollector setCoalesceGarbageLimit(long limit) {
+		coalesceGarbageLimit = limit;
+		return this;
+	}
+
 	/**
 	 * Create a single new pack file containing all of the live objects.
 	 * <p>
@@ -166,7 +195,7 @@ public class DfsGarbageCollector {
 			objdb.clearCache();
 
 			refsBefore = repo.getAllRefs();
-			packsBefore = Arrays.asList(objdb.getPacks());
+			packsBefore = packsToRebuild();
 			if (packsBefore.isEmpty())
 				return true;
 
@@ -202,6 +231,19 @@ public class DfsGarbageCollector {
 		}
 	}
 
+	private List<DfsPackFile> packsToRebuild() throws IOException {
+		DfsPackFile[] packs = objdb.getPacks();
+		List<DfsPackFile> out = new ArrayList<DfsPackFile>(packs.length);
+		for (DfsPackFile p : packs) {
+			DfsPackDescription d = p.getPackDescription();
+			if (d.getPackSource() != UNREACHABLE_GARBAGE)
+				out.add(p);
+			else if (d.getFileSize(PackExt.PACK) < coalesceGarbageLimit)
+				out.add(p);
+		}
+		return out;
+	}
+
 	/** @return all of the source packs that fed into this compaction. */
 	public List<DfsPackDescription> getSourcePacks() {
 		return toPrune();
@@ -231,16 +273,17 @@ public class DfsGarbageCollector {
 
 		PackWriter pw = newPackWriter();
 		try {
+			pw.setTagTargets(tagTargets);
 			pw.preparePack(pm, allHeads, Collections.<ObjectId> emptySet());
 			if (0 < pw.getObjectCount())
-				writePack(GC, pw, pm).setTips(allHeads);
+				writePack(GC, pw, pm);
 		} finally {
 			pw.release();
 		}
 	}
 
 	private void packRest(ProgressMonitor pm) throws IOException {
-		if (nonHeads.isEmpty() || objectsPacked == getObjectsBefore())
+		if (nonHeads.isEmpty())
 			return;
 
 		PackWriter pw = newPackWriter();
@@ -256,16 +299,21 @@ public class DfsGarbageCollector {
 	}
 
 	private void packGarbage(ProgressMonitor pm) throws IOException {
-		if (objectsPacked == getObjectsBefore())
-			return;
-
 		// TODO(sop) This is ugly. The garbage pack needs to be deleted.
-		PackWriter pw = newPackWriter();
+		PackConfig cfg = new PackConfig(packConfig);
+		cfg.setReuseDeltas(true);
+		cfg.setReuseObjects(true);
+		cfg.setDeltaCompress(false);
+		cfg.setBuildBitmaps(false);
+
+		PackWriter pw = new PackWriter(cfg, ctx);
+		pw.setDeltaBaseAsOffset(true);
+		pw.setReuseDeltaCommits(true);
 		try {
 			RevWalk pool = new RevWalk(ctx);
+			pm.beginTask("Finding garbage", objectsBefore());
 			for (DfsPackFile oldPack : packsBefore) {
 				PackIndex oldIdx = oldPack.getPackIndex(ctx);
-				pm.beginTask("Finding garbage", (int) oldIdx.getObjectCount());
 				for (PackIndex.MutableEntry ent : oldIdx) {
 					pm.update(1);
 					ObjectId id = ent.toObjectId();
@@ -275,8 +323,8 @@ public class DfsGarbageCollector {
 					int type = oldPack.getObjectType(ctx, ent.getOffset());
 					pw.addObject(pool.lookupAny(id, type));
 				}
-				pm.endTask();
 			}
+			pm.endTask();
 			if (0 < pw.getObjectCount())
 				writePack(UNREACHABLE_GARBAGE, pw, pm);
 		} finally {
@@ -295,19 +343,17 @@ public class DfsGarbageCollector {
 		return ref.getName().startsWith(Constants.R_HEADS);
 	}
 
-	private long getObjectsBefore() {
-		if (objectsBefore == 0) {
-			for (DfsPackFile p : packsBefore)
-				objectsBefore += p.getPackDescription().getObjectCount();
-		}
-		return objectsBefore;
+	private int objectsBefore() {
+		int cnt = 0;
+		for (DfsPackFile p : packsBefore)
+			cnt += p.getPackDescription().getObjectCount();
+		return cnt;
 	}
 
 	private PackWriter newPackWriter() {
 		PackWriter pw = new PackWriter(packConfig, ctx);
 		pw.setDeltaBaseAsOffset(true);
 		pw.setReuseDeltaCommits(false);
-		pw.setTagTargets(tagTargets);
 		return pw;
 	}
 
@@ -320,6 +366,7 @@ public class DfsGarbageCollector {
 		out = objdb.writeFile(pack, PACK);
 		try {
 			pw.writePack(pm, pm, out);
+			pack.addFileExt(PACK);
 		} finally {
 			out.close();
 		}
@@ -328,11 +375,25 @@ public class DfsGarbageCollector {
 		try {
 			CountingOutputStream cnt = new CountingOutputStream(out);
 			pw.writeIndex(cnt);
+			pack.addFileExt(INDEX);
 			pack.setFileSize(INDEX, cnt.getCount());
+			pack.setIndexVersion(pw.getIndexVersion());
 		} finally {
 			out.close();
 		}
 
+		if (pw.prepareBitmapIndex(pm)) {
+			out = objdb.writeFile(pack, BITMAP_INDEX);
+			try {
+				CountingOutputStream cnt = new CountingOutputStream(out);
+				pw.writeBitmapIndex(cnt);
+				pack.addFileExt(BITMAP_INDEX);
+				pack.setFileSize(BITMAP_INDEX, cnt.getCount());
+			} finally {
+				out.close();
+			}
+		}
+
 		final ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> packedObjs = pw
 				.getObjectSet();
 		newPackObj.add(new PackWriter.ObjectIdSet() {
@@ -343,10 +404,6 @@ public class DfsGarbageCollector {
 
 		PackWriter.Statistics stats = pw.getStatistics();
 		pack.setPackStats(stats);
-		pack.setFileSize(PACK, stats.getTotalBytes());
-		pack.setObjectCount(stats.getTotalObjects());
-		pack.setDeltaCount(stats.getTotalDeltas());
-		objectsPacked += stats.getTotalObjects();
 		newPackStats.add(stats);
 
 		DfsBlockCache.getInstance().getOrCreate(pack, null);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsInserter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index 90bd444..76554c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -41,10 +41,10 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
-import static org.eclipse.jgit.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -57,12 +57,12 @@ import java.util.zip.CRC32;
 import java.util.zip.Deflater;
 import java.util.zip.DeflaterOutputStream;
 
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
 import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.storage.file.PackIndex;
-import org.eclipse.jgit.storage.file.PackIndexWriter;
 import org.eclipse.jgit.transport.PackedObjectInfo;
 import org.eclipse.jgit.util.BlockList;
 import org.eclipse.jgit.util.IO;
@@ -153,6 +153,7 @@ public class DfsInserter extends ObjectInserter {
 			throw new IOException();
 
 		byte[] packHash = packOut.writePackFooter();
+		packDsc.addFileExt(PACK);
 		packDsc.setFileSize(PACK, packOut.getCount());
 		packOut.close();
 		packOut = null;
@@ -240,6 +241,7 @@ public class DfsInserter extends ObjectInserter {
 
 	PackIndex writePackIndex(DfsPackDescription pack, byte[] packHash,
 			List<PackedObjectInfo> list) throws IOException {
+		pack.setIndexVersion(INDEX_VERSION);
 		pack.setObjectCount(list.size());
 
 		// If there are less than 58,000 objects, the entire index fits in under
@@ -260,6 +262,7 @@ public class DfsInserter extends ObjectInserter {
 				buf.writeTo(cnt, null);
 			else
 				index(cnt, packHash, list);
+			pack.addFileExt(INDEX);
 			pack.setFileSize(INDEX, cnt.getCount());
 		} finally {
 			os.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjDatabase.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index 3163955..b92f784 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -53,10 +53,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.eclipse.jgit.internal.storage.pack.PackExt;
 import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.storage.pack.PackExt;
 
 /** Manages objects stored in {@link DfsPackFile} on a storage system. */
 public abstract class DfsObjDatabase extends ObjectDatabase {
@@ -65,7 +65,7 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
 	/** Sources for a pack file. */
 	public static enum PackSource {
 		/** The pack is created by ObjectInserter due to local activity. */
-		INSERT,
+		INSERT(0),
 
 		/**
 		 * The pack is created by PackParser due to a network event.
@@ -76,7 +76,7 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
 		 * storage layout preferred by this version. Received packs are likely
 		 * to be either compacted or garbage collected in the future.
 		 */
-		RECEIVE,
+		RECEIVE(0),
 
 		/**
 		 * Pack was created by Git garbage collection by this implementation.
@@ -87,7 +87,7 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
 		 *
 		 * @see DfsGarbageCollector
 		 */
-		GC,
+		GC(1),
 
 		/**
 		 * The pack was created by compacting multiple packs together.
@@ -98,7 +98,7 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
 		 *
 		 * @see DfsPackCompactor
 		 */
-		COMPACT,
+		COMPACT(1),
 
 		/**
 		 * Pack was created by Git garbage collection.
@@ -107,7 +107,13 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
 		 * last GC pass. It is retained in a new pack until it is safe to prune
 		 * these objects from the repository.
 		 */
-		UNREACHABLE_GARBAGE;
+		UNREACHABLE_GARBAGE(2);
+
+		final int category;
+
+		PackSource(int category) {
+			this.category = category;
+		}
 	}
 
 	private final AtomicReference<PackList> packList;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
similarity index 73%
copy from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
index dfef797..7edae88 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
@@ -41,33 +41,41 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import java.io.IOException;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
 
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.RefRename;
-import org.eclipse.jgit.lib.RefUpdate.Result;
 
-final class DfsRefRename extends RefRename {
-	DfsRefRename(DfsRefUpdate src, DfsRefUpdate dst) {
-		super(src, dst);
+class DfsObjectRepresentation extends StoredObjectRepresentation {
+	final DfsPackFile pack;
+	int format;
+	long offset;
+	long length;
+	ObjectId baseId;
+
+	DfsObjectRepresentation(DfsPackFile pack) {
+		this.pack = pack;
+	}
+
+	@Override
+	public int getFormat() {
+		return format;
 	}
 
 	@Override
-	protected Result doRename() throws IOException {
-		// TODO Correctly handle renaming foo/bar to foo.
-		// TODO Batch these together into one log update.
+	public int getWeight() {
+		return (int) Math.min(length, Integer.MAX_VALUE);
+	}
 
-		destination.setExpectedOldObjectId(ObjectId.zeroId());
-		destination.setNewObjectId(source.getRef().getObjectId());
-		switch (destination.update()) {
-		case NEW:
-			source.delete();
-			return Result.RENAMED;
+	@Override
+	public ObjectId getDeltaBase() {
+		return baseId;
+	}
 
-		default:
-			return destination.getResult();
-		}
+	@Override
+	public boolean wasDeltaAttempted() {
+		return pack.getPackDescription().getPackSource() == GC;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectToPack.java
similarity index 84%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjectToPack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectToPack.java
index c5243d9..ff270bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjectToPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectToPack.java
@@ -41,28 +41,35 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.lib.AnyObjectId;
 
 /** {@link ObjectToPack} for {@link DfsObjDatabase}. */
 class DfsObjectToPack extends ObjectToPack {
+	private static final int FLAG_FOUND = 1 << 0;
+
 	/** Pack to reuse compressed data from, otherwise null. */
 	DfsPackFile pack;
 
-	/** Position of the pack in the reader's pack list. */
-	int packIndex;
-
 	/** Offset of the object's header in {@link #pack}. */
 	long offset;
 
 	/** Length of the data section of the object. */
 	long length;
 
-	DfsObjectToPack(RevObject obj) {
-		super(obj);
+	DfsObjectToPack(AnyObjectId src, final int type) {
+		super(src, type);
+	}
+
+	final boolean isFound() {
+		return isExtendedFlag(FLAG_FOUND);
+	}
+
+	final void setFound() {
+		setExtendedFlag(FLAG_FOUND);
 	}
 
 	@Override
@@ -75,7 +82,6 @@ class DfsObjectToPack extends ObjectToPack {
 	public void select(StoredObjectRepresentation ref) {
 		DfsObjectRepresentation ptr = (DfsObjectRepresentation) ref;
 		this.pack = ptr.pack;
-		this.packIndex = ptr.packIndex;
 		this.offset = ptr.offset;
 		this.length = ptr.length;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsOutputStream.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsOutputStream.java
index 2070d80..ef3173e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsOutputStream.java
@@ -41,13 +41,13 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 
-import org.eclipse.jgit.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
 
 /**
  * Output stream to create a file on the DFS.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
similarity index 71%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackCompactor.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index c17c863..ea56392 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -41,11 +41,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import static org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
-import static org.eclipse.jgit.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -55,6 +56,9 @@ import java.util.List;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
@@ -62,9 +66,7 @@ import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.PackIndex;
 import org.eclipse.jgit.storage.pack.PackConfig;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.util.BlockList;
 import org.eclipse.jgit.util.io.CountingOutputStream;
 
@@ -88,12 +90,18 @@ public class DfsPackCompactor {
 
 	private final List<DfsPackFile> srcPacks;
 
+	private final List<PackWriter.ObjectIdSet> exclude;
+
 	private final List<DfsPackDescription> newPacks;
 
 	private final List<PackWriter.Statistics> newStats;
 
 	private int autoAddSize;
 
+	private RevWalk rw;
+	private RevFlag added;
+	private RevFlag isBase;
+
 	/**
 	 * Initialize a pack compactor.
 	 *
@@ -104,6 +112,7 @@ public class DfsPackCompactor {
 		repo = repository;
 		autoAddSize = 5 * 1024 * 1024; // 5 MiB
 		srcPacks = new ArrayList<DfsPackFile>();
+		exclude = new ArrayList<PackWriter.ObjectIdSet>(4);
 		newPacks = new ArrayList<DfsPackDescription>(1);
 		newStats = new ArrayList<PackWriter.Statistics>(1);
 	}
@@ -141,11 +150,49 @@ public class DfsPackCompactor {
 			DfsPackDescription d = pack.getPackDescription();
 			if (d.getFileSize(PACK) < autoAddSize)
 				add(pack);
+			else
+				exclude(pack);
 		}
 		return this;
 	}
 
 	/**
+	 * Exclude objects from the compacted pack.
+	 *
+	 * @param set
+	 *            objects to not include.
+	 * @return {@code this}.
+	 */
+	public DfsPackCompactor exclude(PackWriter.ObjectIdSet set) {
+		exclude.add(set);
+		return this;
+	}
+
+	/**
+	 * Exclude objects from the compacted pack.
+	 *
+	 * @param pack
+	 *            objects to not include.
+	 * @return {@code this}.
+	 * @throws IOException
+	 *             pack index cannot be loaded.
+	 */
+	public DfsPackCompactor exclude(DfsPackFile pack) throws IOException {
+		final PackIndex idx;
+		DfsReader ctx = (DfsReader) repo.newObjectReader();
+		try {
+			idx = pack.getPackIndex(ctx);
+		} finally {
+			ctx.release();
+		}
+		return exclude(new PackWriter.ObjectIdSet() {
+			public boolean contains(AnyObjectId id) {
+				return idx.hasObject(id);
+			}
+		});
+	}
+
+	/**
 	 * Compact the pack files together.
 	 *
 	 * @param pm
@@ -200,6 +247,7 @@ public class DfsPackCompactor {
 					pw.release();
 			}
 		} finally {
+			rw = null;
 			ctx.release();
 		}
 	}
@@ -239,70 +287,95 @@ public class DfsPackCompactor {
 			}
 		});
 
-		RevWalk rw = new RevWalk(ctx);
-		RevFlag added = rw.newFlag("ADDED"); //$NON-NLS-1$
+		rw = new RevWalk(ctx);
+		added = rw.newFlag("ADDED"); //$NON-NLS-1$
+		isBase = rw.newFlag("IS_BASE"); //$NON-NLS-1$
+		List<RevObject> baseObjects = new BlockList<RevObject>();
 
 		pm.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN);
 		for (DfsPackFile src : srcPacks) {
-			List<ObjectIdWithOffset> want = new BlockList<ObjectIdWithOffset>();
-			for (PackIndex.MutableEntry ent : src.getPackIndex(ctx)) {
-				ObjectId id = ent.toObjectId();
-				RevObject obj = rw.lookupOrNull(id);
-				if (obj == null || !obj.has(added))
-					want.add(new ObjectIdWithOffset(id, ent.getOffset()));
-			}
+			List<ObjectIdWithOffset> want = toInclude(src, ctx);
+			if (want.isEmpty())
+				continue;
 
-			// Sort objects by the order they appear in the pack file, for
-			// two benefits. Scanning object type information is faster when
-			// the pack is traversed in order, and this allows the PackWriter
-			// to be given the new objects in a relatively sane newest-first
-			// ordering without additional logic, like unpacking commits and
-			// walking a commit queue.
-			Collections.sort(want, new Comparator<ObjectIdWithOffset>() {
-				public int compare(ObjectIdWithOffset a, ObjectIdWithOffset b) {
-					return Long.signum(a.offset - b.offset);
-				}
-			});
-
-			// Only pack each object at most once into the output file. The
-			// PackWriter will later select a representation to reuse, which
-			// may be the version in this pack, or may be from another pack if
-			// the object was copied here to complete a thin pack and is larger
-			// than a delta from another pack. This is actually somewhat common
-			// if an object is modified frequently, such as the top level tree.
+			PackReverseIndex rev = src.getReverseIdx(ctx);
+			DfsObjectRepresentation rep = new DfsObjectRepresentation(src);
 			for (ObjectIdWithOffset id : want) {
 				int type = src.getObjectType(ctx, id.offset);
 				RevObject obj = rw.lookupAny(id, type);
-				if (!obj.has(added)) {
-					pm.update(1);
-					pw.addObject(obj);
-					obj.add(added);
+				if (obj.has(added))
+					continue;
+
+				pm.update(1);
+				pw.addObject(obj);
+				obj.add(added);
+
+				src.representation(rep, id.offset, ctx, rev);
+				if (rep.getFormat() != PACK_DELTA)
+					continue;
+
+				RevObject base = rw.lookupAny(rep.getDeltaBase(), type);
+				if (!base.has(added) && !base.has(isBase)) {
+					baseObjects.add(base);
+					base.add(isBase);
 				}
 			}
 		}
+		for (RevObject obj : baseObjects) {
+			if (!obj.has(added)) {
+				pm.update(1);
+				pw.addObject(obj);
+				obj.add(added);
+			}
+		}
 		pm.endTask();
 	}
 
-	private void writePack(DfsObjDatabase objdb, DfsPackDescription pack,
+	private List<ObjectIdWithOffset> toInclude(DfsPackFile src, DfsReader ctx)
+			throws IOException {
+		PackIndex srcIdx = src.getPackIndex(ctx);
+		List<ObjectIdWithOffset> want = new BlockList<ObjectIdWithOffset>(
+				(int) srcIdx.getObjectCount());
+		SCAN: for (PackIndex.MutableEntry ent : srcIdx) {
+			ObjectId id = ent.toObjectId();
+			RevObject obj = rw.lookupOrNull(id);
+			if (obj != null && (obj.has(added) || obj.has(isBase)))
+				continue;
+			for (PackWriter.ObjectIdSet e : exclude)
+				if (e.contains(id))
+					continue SCAN;
+			want.add(new ObjectIdWithOffset(id, ent.getOffset()));
+		}
+		Collections.sort(want, new Comparator<ObjectIdWithOffset>() {
+			public int compare(ObjectIdWithOffset a, ObjectIdWithOffset b) {
+				return Long.signum(a.offset - b.offset);
+			}
+		});
+		return want;
+	}
+
+	private static void writePack(DfsObjDatabase objdb,
+			DfsPackDescription pack,
 			PackWriter pw, ProgressMonitor pm) throws IOException {
 		DfsOutputStream out = objdb.writeFile(pack, PACK);
 		try {
-			CountingOutputStream cnt = new CountingOutputStream(out);
-			pw.writePack(pm, pm, cnt);
-			pack.setObjectCount(pw.getObjectCount());
-			pack.setFileSize(PACK, cnt.getCount());
+			pw.writePack(pm, pm, out);
+			pack.addFileExt(PACK);
 		} finally {
 			out.close();
 		}
 	}
 
-	private void writeIndex(DfsObjDatabase objdb, DfsPackDescription pack,
+	private static void writeIndex(DfsObjDatabase objdb,
+			DfsPackDescription pack,
 			PackWriter pw) throws IOException {
 		DfsOutputStream out = objdb.writeFile(pack, INDEX);
 		try {
 			CountingOutputStream cnt = new CountingOutputStream(out);
 			pw.writeIndex(cnt);
+			pack.addFileExt(INDEX);
 			pack.setFileSize(INDEX, cnt.getCount());
+			pack.setIndexVersion(pw.getIndexVersion());
 		} finally {
 			out.close();
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
similarity index 83%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
index a793d3c..fba5157 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
@@ -41,16 +41,16 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource;
-import org.eclipse.jgit.storage.pack.PackExt;
-import org.eclipse.jgit.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 
 /**
  * Description of a DFS stored pack/index file.
@@ -69,16 +69,18 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 
 	private long lastModified;
 
-	private Map<PackExt, Long> sizeMap;
+	private final Map<PackExt, Long> sizeMap;
 
 	private long objectCount;
 
 	private long deltaCount;
 
-	private Set<ObjectId> tips;
-
 	private PackWriter.Statistics stats;
 
+	private int extensions;
+
+	private int indexVersion;
+
 	/**
 	 * Initialize a description by pack name and repository.
 	 * <p>
@@ -98,7 +100,7 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 		this.repoDesc = repoDesc;
 		int dot = name.lastIndexOf('.');
 		this.packName = (dot < 0) ? name : name.substring(0, dot);
-		this.sizeMap = new HashMap<PackExt, Long>(5);
+		this.sizeMap = new HashMap<PackExt, Long>(PackExt.values().length * 2);
 	}
 
 	/** @return description of the repository. */
@@ -107,10 +109,29 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 	}
 
 	/**
+	 * Adds the pack file extension to the known list.
+	 *
+	 * @param ext
+	 *            the file extension
+	 */
+	public void addFileExt(PackExt ext) {
+		extensions |= ext.getBit();
+	}
+
+	/**
+	 * @param ext
+	 *            the file extension
+	 * @return whether the pack file extensions is known to exist.
+	 */
+	public boolean hasFileExt(PackExt ext) {
+		return (extensions & ext.getBit()) != 0;
+	}
+
+	/**
 	 * @param ext
 	 *            the file extension
 	 * @return name of the file.
-	 * */
+	 */
 	public String getFileName(PackExt ext) {
 		return packName + '.' + ext.getExtension();
 	}
@@ -198,21 +219,6 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 		return this;
 	}
 
-	/** @return the tips that created this pack, if known. */
-	public Set<ObjectId> getTips() {
-		return tips;
-	}
-
-	/**
-	 * @param tips
-	 *            the tips of the pack, null if it has no known tips.
-	 * @return {@code this}
-	 */
-	public DfsPackDescription setTips(Set<ObjectId> tips) {
-		this.tips = tips;
-		return this;
-	}
-
 	/**
 	 * @return statistics from PackWriter, if the pack was built with it.
 	 *         Generally this is only available for packs created by
@@ -225,6 +231,9 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 
 	DfsPackDescription setPackStats(PackWriter.Statistics stats) {
 		this.stats = stats;
+		setFileSize(PACK, stats.getTotalBytes());
+		setObjectCount(stats.getTotalObjects());
+		setDeltaCount(stats.getTotalDeltas());
 		return this;
 	}
 
@@ -238,6 +247,21 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 		return this;
 	}
 
+	/** @return the version of the index file written. */
+	public int getIndexVersion() {
+		return indexVersion;
+	}
+
+	/**
+	 * @param version
+	 *            the version of the index file written.
+	 * @return {@code this}
+	 */
+	public DfsPackDescription setIndexVersion(int version) {
+		indexVersion = version;
+		return this;
+	}
+
 	@Override
 	public int hashCode() {
 		return packName.hashCode();
@@ -265,6 +289,15 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
 	 *            the other pack.
 	 */
 	public int compareTo(DfsPackDescription b) {
+		// Cluster by PackSource, pushing UNREACHABLE_GARBAGE to the end.
+		PackSource as = getPackSource();
+		PackSource bs = b.getPackSource();
+		if (as != null && bs != null) {
+			int cmp = as.category - bs.category;
+			if (cmp != 0)
+				return cmp;
+		}
+
 		// Newer packs should sort first.
 		int cmp = Long.signum(b.getLastModified() - getLastModified());
 		if (cmp != 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
similarity index 90%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index ca58f8d..1c588d2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -43,10 +43,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
-import static org.eclipse.jgit.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.BufferedInputStream;
 import java.io.EOFException;
@@ -65,17 +67,19 @@ import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.PackInvalidException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.PackIndex;
-import org.eclipse.jgit.storage.file.PackReverseIndex;
-import org.eclipse.jgit.storage.pack.BinaryDelta;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.LongList;
 
@@ -99,6 +103,9 @@ public final class DfsPackFile {
 	/** Offset used to cache {@link #reverseIndex}. See {@link #POS_INDEX}. */
 	private static final long POS_REVERSE_INDEX = -2;
 
+	/** Offset used to cache {@link #bitmapIndex}. See {@link #POS_INDEX}. */
+	private static final long POS_BITMAP_INDEX = -3;
+
 	/** Cache that owns this pack file and its data. */
 	private final DfsBlockCache cache;
 
@@ -141,6 +148,9 @@ public final class DfsPackFile {
 	/** Reverse version of {@link #index} mapping position to {@link ObjectId}. */
 	private volatile DfsBlockCache.Ref<PackReverseIndex> reverseIndex;
 
+	/** Index of compressed bitmap mapping entire object graph. */
+	private volatile DfsBlockCache.Ref<PackBitmapIndex> bitmapIndex;
+
 	/**
 	 * Objects we have tried to read, and discovered to be corrupt.
 	 * <p>
@@ -267,7 +277,71 @@ public final class DfsPackFile {
 		}
 	}
 
-	private PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
+	final boolean isGarbage() {
+		return packDesc.getPackSource() == UNREACHABLE_GARBAGE;
+	}
+
+	PackBitmapIndex getBitmapIndex(DfsReader ctx) throws IOException {
+		if (invalid || isGarbage())
+			return null;
+		DfsBlockCache.Ref<PackBitmapIndex> idxref = bitmapIndex;
+		if (idxref != null) {
+			PackBitmapIndex idx = idxref.get();
+			if (idx != null)
+				return idx;
+		}
+
+		if (!packDesc.hasFileExt(PackExt.BITMAP_INDEX))
+			return null;
+
+		synchronized (initLock) {
+			idxref = bitmapIndex;
+			if (idxref != null) {
+				PackBitmapIndex idx = idxref.get();
+				if (idx != null)
+					return idx;
+			}
+
+			long size;
+			PackBitmapIndex idx;
+			try {
+				ReadableChannel rc = ctx.db.openFile(packDesc, BITMAP_INDEX);
+				try {
+					InputStream in = Channels.newInputStream(rc);
+					int wantSize = 8192;
+					int bs = rc.blockSize();
+					if (0 < bs && bs < wantSize)
+						bs = (wantSize / bs) * bs;
+					else if (bs <= 0)
+						bs = wantSize;
+					in = new BufferedInputStream(in, bs);
+					idx = PackBitmapIndex.read(
+							in, idx(ctx), getReverseIdx(ctx));
+				} finally {
+					size = rc.position();
+					rc.close();
+				}
+			} catch (EOFException e) {
+				IOException e2 = new IOException(MessageFormat.format(
+						DfsText.get().shortReadOfIndex,
+						packDesc.getFileName(BITMAP_INDEX)));
+				e2.initCause(e);
+				throw e2;
+			} catch (IOException e) {
+				IOException e2 = new IOException(MessageFormat.format(
+						DfsText.get().cannotReadIndex,
+						packDesc.getFileName(BITMAP_INDEX)));
+				e2.initCause(e);
+				throw e2;
+			}
+
+			bitmapIndex = cache.put(key, POS_BITMAP_INDEX,
+					(int) Math.min(size, Integer.MAX_VALUE), idx);
+			return idx;
+		}
+	}
+
+	PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
 		DfsBlockCache.Ref<PackReverseIndex> revref = reverseIndex;
 		if (revref != null) {
 			PackReverseIndex revidx = revref.get();
@@ -357,22 +431,6 @@ public final class DfsPackFile {
 		return idx(ctx).getObjectCount();
 	}
 
-	/**
-	 * Search for object id with the specified start offset in associated pack
-	 * (reverse) index.
-	 *
-	 * @param ctx
-	 *            current reader for the calling thread.
-	 * @param offset
-	 *            start offset of object to find
-	 * @return object id for this offset, or null if no object was found
-	 * @throws IOException
-	 *             the index file cannot be loaded into memory.
-	 */
-	ObjectId findObjectForOffset(DfsReader ctx, long offset) throws IOException {
-		return getReverseIdx(ctx).findObject(offset);
-	}
-
 	private byte[] decompress(long position, int sz, DfsReader ctx)
 			throws IOException, DataFormatException {
 		byte[] dstbuf;
@@ -622,7 +680,6 @@ public final class DfsPackFile {
 		if (invalid)
 			throw new PackInvalidException(getPackName());
 
-		boolean close = true;
 		ReadableChannel rc = ctx.db.openFile(packDesc, PACK);
 		try {
 			// If the block alignment is not yet known, discover it. Prefer the
@@ -681,12 +738,9 @@ public final class DfsPackFile {
 			}
 
 			DfsBlock v = new DfsBlock(key, pos, buf);
-			if (v.end < len)
-				close = !cache.readAhead(rc, key, size, v.end, len, ctx);
 			return v;
 		} finally {
-			if (close)
-				rc.close();
+			rc.close();
 		}
 	}
 
@@ -979,9 +1033,10 @@ public final class DfsPackFile {
 		}
 	}
 
-	void representation(DfsReader ctx, DfsObjectRepresentation r)
+	void representation(DfsObjectRepresentation r, final long pos,
+			DfsReader ctx, PackReverseIndex rev)
 			throws IOException {
-		final long pos = r.offset;
+		r.offset = pos;
 		final byte[] ib = ctx.tempId;
 		readFully(pos, ib, 0, 20, ctx);
 		int c = ib[0] & 0xff;
@@ -990,13 +1045,14 @@ public final class DfsPackFile {
 		while ((c & 0x80) != 0)
 			c = ib[p++] & 0xff;
 
-		long len = (getReverseIdx(ctx).findNextOffset(pos, length - 20) - pos);
+		long len = rev.findNextOffset(pos, length - 20) - pos;
 		switch (typeCode) {
 		case Constants.OBJ_COMMIT:
 		case Constants.OBJ_TREE:
 		case Constants.OBJ_BLOB:
 		case Constants.OBJ_TAG:
 			r.format = StoredObjectRepresentation.PACK_WHOLE;
+			r.baseId = null;
 			r.length = len - p;
 			return;
 
@@ -1009,21 +1065,17 @@ public final class DfsPackFile {
 				ofs <<= 7;
 				ofs += (c & 127);
 			}
-			ofs = pos - ofs;
 			r.format = StoredObjectRepresentation.PACK_DELTA;
-			r.baseId = findObjectForOffset(ctx, ofs);
+			r.baseId = rev.findObject(pos - ofs);
 			r.length = len - p;
 			return;
 		}
 
 		case Constants.OBJ_REF_DELTA: {
-			len -= p;
-			len -= Constants.OBJECT_ID_LENGTH;
 			readFully(pos + p, ib, 0, 20, ctx);
-			ObjectId id = ObjectId.fromRaw(ib);
 			r.format = StoredObjectRepresentation.PACK_DELTA;
-			r.baseId = id;
-			r.length = len;
+			r.baseId = ObjectId.fromRaw(ib);
+			r.length = len - p - 20;
 			return;
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackKey.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackKey.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackKey.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackKey.java
index 880ac99..98a2a94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackKey.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackKey.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.util.concurrent.atomic.AtomicLong;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackParser.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java
index e31de53..6430ea9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackParser.java
@@ -41,9 +41,9 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -55,11 +55,11 @@ import java.util.List;
 import java.util.zip.CRC32;
 import java.util.zip.Deflater;
 
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackLock;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.storage.file.PackIndex;
-import org.eclipse.jgit.storage.file.PackLock;
 import org.eclipse.jgit.transport.PackParser;
 import org.eclipse.jgit.transport.PackedObjectInfo;
 
@@ -148,6 +148,7 @@ public class DfsPackParser extends PackParser {
 			out = null;
 			currBuf = null;
 			readBlock = null;
+			packDsc.addFileExt(PACK);
 			packDsc.setFileSize(PACK, packEnd);
 
 			writePackIndex();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPacksChangedEvent.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedEvent.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPacksChangedEvent.java
index 1694222..14d67c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedEvent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPacksChangedEvent.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import org.eclipse.jgit.events.RepositoryEvent;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPacksChangedListener.java
similarity index 97%
copy from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedListener.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPacksChangedListener.java
index 0ac43b5..324626c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedListener.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPacksChangedListener.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import org.eclipse.jgit.events.RepositoryListener;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
similarity index 71%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index 393fa3f..26fc035 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -42,15 +42,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
-import static org.eclipse.jgit.storage.pack.PackExt.PACK;
 
 import java.io.IOException;
-import java.io.InterruptedIOException;
 import java.security.MessageDigest;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -60,10 +57,8 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
@@ -71,25 +66,27 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
+import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
 import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.InflaterCache;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.util.BlockList;
 
 /**
@@ -113,9 +110,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 
 	private DfsPackFile last;
 
-	private boolean wantReadAhead;
-
-	private List<ReadAheadTask.BlockFuture> pendingReadAhead;
+	private boolean avoidUnreachable;
 
 	DfsReader(DfsObjDatabase db) {
 		this.db = db;
@@ -141,12 +136,41 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 	}
 
 	@Override
+	public void setAvoidUnreachableObjects(boolean avoid) {
+		avoidUnreachable = avoid;
+	}
+
+	@Override
+	public BitmapIndex getBitmapIndex() throws IOException {
+		for (DfsPackFile pack : db.getPacks()) {
+			PackBitmapIndex bitmapIndex = pack.getBitmapIndex(this);
+			if (bitmapIndex != null)
+				return new BitmapIndexImpl(bitmapIndex);
+		}
+		return null;
+	}
+
+	public Collection<CachedPack> getCachedPacksAndUpdate(
+		BitmapBuilder needBitmap) throws IOException {
+		for (DfsPackFile pack : db.getPacks()) {
+			PackBitmapIndex bitmapIndex = pack.getBitmapIndex(this);
+			if (needBitmap.removeAllOrNone(bitmapIndex))
+				return Collections.<CachedPack> singletonList(
+						new DfsCachedPack(pack));
+		}
+		return Collections.emptyList();
+	}
+
+	@Override
 	public Collection<ObjectId> resolve(AbbreviatedObjectId id)
 			throws IOException {
 		if (id.isComplete())
 			return Collections.singleton(id.toObjectId());
+		boolean noGarbage = avoidUnreachable;
 		HashSet<ObjectId> matches = new HashSet<ObjectId>(4);
 		for (DfsPackFile pack : db.getPacks()) {
+			if (noGarbage && pack.isGarbage())
+				continue;
 			pack.resolve(this, matches, id, 256);
 			if (256 <= matches.size())
 				break;
@@ -158,8 +182,9 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 	public boolean has(AnyObjectId objectId) throws IOException {
 		if (last != null && last.hasObject(this, objectId))
 			return true;
+		boolean noGarbage = avoidUnreachable;
 		for (DfsPackFile pack : db.getPacks()) {
-			if (last == pack)
+			if (pack == last || (noGarbage && pack.isGarbage()))
 				continue;
 			if (pack.hasObject(this, objectId)) {
 				last = pack;
@@ -179,8 +204,9 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 				return ldr;
 		}
 
+		boolean noGarbage = avoidUnreachable;
 		for (DfsPackFile pack : db.getPacks()) {
-			if (pack == last)
+			if (pack == last || (noGarbage && pack.isGarbage()))
 				continue;
 			ObjectLoader ldr = pack.get(this, objectId);
 			if (ldr != null) {
@@ -241,6 +267,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 
 		int lastIdx = 0;
 		DfsPackFile lastPack = packList[lastIdx];
+		boolean noGarbage = avoidUnreachable;
 
 		OBJECT_SCAN: for (T t : objectIds) {
 			try {
@@ -257,6 +284,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 				if (i == lastIdx)
 					continue;
 				DfsPackFile pack = packList[i];
+				if (noGarbage && pack.isGarbage())
+					continue;
 				try {
 					long p = pack.findOffset(this, t);
 					if (0 < p) {
@@ -281,8 +310,6 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 	@Override
 	public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
 			Iterable<T> objectIds, final boolean reportMissing) {
-		wantReadAhead = true;
-
 		Iterable<FoundObject<T>> order;
 		IOException error = null;
 		try {
@@ -304,7 +331,6 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 				} else if (findAllError != null) {
 					throw findAllError;
 				} else {
-					cancelReadAhead();
 					return false;
 				}
 			}
@@ -324,12 +350,11 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 			}
 
 			public boolean cancel(boolean mayInterruptIfRunning) {
-				cancelReadAhead();
 				return true;
 			}
 
 			public void release() {
-				cancelReadAhead();
+				// Nothing to clean up.
 			}
 		};
 	}
@@ -337,8 +362,6 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 	@Override
 	public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
 			Iterable<T> objectIds, final boolean reportMissing) {
-		wantReadAhead = true;
-
 		Iterable<FoundObject<T>> order;
 		IOException error = null;
 		try {
@@ -365,7 +388,6 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 				} else if (findAllError != null) {
 					throw findAllError;
 				} else {
-					cancelReadAhead();
 					return false;
 				}
 			}
@@ -383,32 +405,16 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 			}
 
 			public boolean cancel(boolean mayInterruptIfRunning) {
-				cancelReadAhead();
 				return true;
 			}
 
 			public void release() {
-				cancelReadAhead();
+				// Nothing to clean up.
 			}
 		};
 	}
 
 	@Override
-	public void walkAdviceBeginCommits(RevWalk walk, Collection<RevCommit> roots) {
-		wantReadAhead = true;
-	}
-
-	@Override
-	public void walkAdviceBeginTrees(ObjectWalk ow, RevCommit min, RevCommit max) {
-		wantReadAhead = true;
-	}
-
-	@Override
-	public void walkAdviceEnd() {
-		cancelReadAhead();
-	}
-
-	@Override
 	public long getObjectSize(AnyObjectId objectId, int typeHint)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
@@ -433,73 +439,50 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 		throw new MissingObjectException(objectId.copy(), typeHint);
 	}
 
-	public DfsObjectToPack newObjectToPack(RevObject obj) {
-		return new DfsObjectToPack(obj);
+	public DfsObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
+		return new DfsObjectToPack(objectId, type);
 	}
 
-	private static final Comparator<DfsObjectRepresentation> REPRESENTATION_SORT = new Comparator<DfsObjectRepresentation>() {
-		public int compare(DfsObjectRepresentation a, DfsObjectRepresentation b) {
-			int cmp = a.packIndex - b.packIndex;
-			if (cmp == 0)
-				cmp = Long.signum(a.offset - b.offset);
-			return cmp;
+	private static final Comparator<DfsObjectToPack> OFFSET_SORT = new Comparator<DfsObjectToPack>() {
+		public int compare(DfsObjectToPack a, DfsObjectToPack b) {
+			return Long.signum(a.getOffset() - b.getOffset());
 		}
 	};
 
 	public void selectObjectRepresentation(PackWriter packer,
 			ProgressMonitor monitor, Iterable<ObjectToPack> objects)
 			throws IOException, MissingObjectException {
-		DfsPackFile[] packList = db.getPacks();
-		if (packList.length == 0) {
-			Iterator<ObjectToPack> itr = objects.iterator();
-			if (itr.hasNext())
-				throw new MissingObjectException(itr.next(), "unknown");
-			return;
-		}
-
-		int objectCount = 0;
-		int updated = 0;
-		int posted = 0;
-		List<DfsObjectRepresentation> all = new BlockList<DfsObjectRepresentation>();
-		for (ObjectToPack otp : objects) {
-			boolean found = false;
-			for (int packIndex = 0; packIndex < packList.length; packIndex++) {
-				DfsPackFile pack = packList[packIndex];
-				long p = pack.findOffset(this, otp);
-				if (0 < p) {
-					DfsObjectRepresentation r = new DfsObjectRepresentation(otp);
-					r.pack = pack;
-					r.packIndex = packIndex;
-					r.offset = p;
-					all.add(r);
-					found = true;
+		for (DfsPackFile pack : db.getPacks()) {
+			List<DfsObjectToPack> tmp = findAllFromPack(pack, objects);
+			if (tmp.isEmpty())
+				continue;
+			Collections.sort(tmp, OFFSET_SORT);
+			PackReverseIndex rev = pack.getReverseIdx(this);
+			DfsObjectRepresentation rep = new DfsObjectRepresentation(pack);
+			for (DfsObjectToPack otp : tmp) {
+				pack.representation(rep, otp.getOffset(), this, rev);
+				otp.setOffset(0);
+				packer.select(otp, rep);
+				if (!otp.isFound()) {
+					otp.setFound();
+					monitor.update(1);
 				}
 			}
-			if (!found)
-				throw new MissingObjectException(otp, otp.getType());
-			if ((++updated & 1) == 1) {
-				monitor.update(1); // Update by 50%, the other 50% is below.
-				posted++;
-			}
-			objectCount++;
 		}
-		Collections.sort(all, REPRESENTATION_SORT);
+	}
 
-		try {
-			wantReadAhead = true;
-			for (DfsObjectRepresentation r : all) {
-				r.pack.representation(this, r);
-				packer.select(r.object, r);
-				if ((++updated & 1) == 1 && posted < objectCount) {
-					monitor.update(1);
-					posted++;
-				}
+	private List<DfsObjectToPack> findAllFromPack(DfsPackFile pack,
+			Iterable<ObjectToPack> objects) throws IOException {
+		List<DfsObjectToPack> tmp = new BlockList<DfsObjectToPack>();
+		PackIndex idx = pack.getPackIndex(this);
+		for (ObjectToPack otp : objects) {
+			long p = idx.findOffset(otp);
+			if (0 < p) {
+				otp.setOffset(p);
+				tmp.add((DfsObjectToPack) otp);
 			}
-		} finally {
-			cancelReadAhead();
 		}
-		if (posted < objectCount)
-			monitor.update(objectCount - posted);
+		return tmp;
 	}
 
 	public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
@@ -509,74 +492,15 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 		src.pack.copyAsIs(out, src, validate, this);
 	}
 
-	private static final Comparator<ObjectToPack> WRITE_SORT = new Comparator<ObjectToPack>() {
-		public int compare(ObjectToPack o1, ObjectToPack o2) {
-			DfsObjectToPack a = (DfsObjectToPack) o1;
-			DfsObjectToPack b = (DfsObjectToPack) o2;
-			int cmp = a.packIndex - b.packIndex;
-			if (cmp == 0)
-				cmp = Long.signum(a.offset - b.offset);
-			return cmp;
-		}
-	};
-
 	public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
 			throws IOException {
-		if (list.isEmpty())
-			return;
-
-		// Sorting objects by order in the current packs is usually
-		// worthwhile. Most packs are going to be OFS_DELTA style,
-		// where the base must appear before the deltas. If both base
-		// and delta are to be reused, this ensures the base writes in
-		// the output first without the recursive write-base-first logic
-		// used by PackWriter to ensure OFS_DELTA can be used.
-		//
-		// Sorting by pack also ensures newer objects go first, which
-		// typically matches the desired order.
-		//
-		// Only do this sorting for OBJ_TREE and OBJ_BLOB. Commits
-		// are very likely to already be sorted in a good order in the
-		// incoming list, and if they aren't, JGit's PackWriter has fixed
-		// the order to be more optimal for readers, so honor that.
-		switch (list.get(0).getType()) {
-		case OBJ_TREE:
-		case OBJ_BLOB:
-			Collections.sort(list, WRITE_SORT);
-		}
-
-		try {
-			wantReadAhead = true;
-			for (ObjectToPack otp : list)
-				out.writeObject(otp);
-		} finally {
-			cancelReadAhead();
-		}
-	}
-
-	public Collection<CachedPack> getCachedPacks() throws IOException {
-		DfsPackFile[] packList = db.getPacks();
-		List<CachedPack> cached = new ArrayList<CachedPack>(packList.length);
-		for (DfsPackFile pack : packList) {
-			DfsPackDescription desc = pack.getPackDescription();
-			if (canBeCachedPack(desc))
-				cached.add(new DfsCachedPack(pack));
-		}
-		return cached;
-	}
-
-	private static boolean canBeCachedPack(DfsPackDescription desc) {
-		return desc.getTips() != null && !desc.getTips().isEmpty();
+		for (ObjectToPack otp : list)
+			out.writeObject(otp);
 	}
 
 	public void copyPackAsIs(PackOutputStream out, CachedPack pack,
 			boolean validate) throws IOException {
-		try {
-			wantReadAhead = true;
-			((DfsCachedPack) pack).copyAsIs(out, validate, this);
-		} finally {
-			cancelReadAhead();
-		}
+		((DfsCachedPack) pack).copyAsIs(out, validate, this);
 	}
 
 	/**
@@ -735,60 +659,14 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 			// be cleaned up by the GC during the get for the next window.
 			// So we always clear it, even though we are just going to set
 			// it again.
-			//
 			block = null;
-
-			if (pendingReadAhead != null)
-				waitForBlock(pack.key, position);
 			block = pack.getOrLoadBlock(position, this);
 		}
 	}
 
-	boolean wantReadAhead() {
-		return wantReadAhead;
-	}
-
-	void startedReadAhead(List<ReadAheadTask.BlockFuture> blocks) {
-		if (pendingReadAhead == null)
-			pendingReadAhead = new LinkedList<ReadAheadTask.BlockFuture>();
-		pendingReadAhead.addAll(blocks);
-	}
-
-	private void cancelReadAhead() {
-		if (pendingReadAhead != null) {
-			for (ReadAheadTask.BlockFuture f : pendingReadAhead)
-				f.cancel(true);
-			pendingReadAhead = null;
-		}
-		wantReadAhead = false;
-	}
-
-	private void waitForBlock(DfsPackKey key, long position)
-			throws InterruptedIOException {
-		Iterator<ReadAheadTask.BlockFuture> itr = pendingReadAhead.iterator();
-		while (itr.hasNext()) {
-			ReadAheadTask.BlockFuture f = itr.next();
-			if (f.contains(key, position)) {
-				try {
-					f.get();
-				} catch (InterruptedException e) {
-					throw new InterruptedIOException();
-				} catch (ExecutionException e) {
-					// Exceptions should never be thrown by get(). Ignore
-					// this and let the normal load paths identify any error.
-				}
-				itr.remove();
-				if (pendingReadAhead.isEmpty())
-					pendingReadAhead = null;
-				break;
-			}
-		}
-	}
-
 	/** Release the current window cursor. */
 	@Override
 	public void release() {
-		cancelReadAhead();
 		last = null;
 		block = null;
 		baseCache = null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReaderOptions.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
index 2cb83e1..2a62547 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReaderOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
@@ -51,7 +51,7 @@ import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_TRESHO
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.storage.pack.PackConfig;
 
-/** Options controlling how objects are read from a DHT stored repository. */
+/** Options controlling how objects are read from a DFS stored repository. */
 public class DfsReaderOptions {
 	/** 1024 (number of bytes in one kibibyte/kilobyte) */
 	public static final int KiB = 1024;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefDatabase.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
index 383431b..6784b49 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefRename.java
similarity index 98%
copy from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefRename.java
index dfef797..a4cb791 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefRename.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefUpdate.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefUpdate.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefUpdate.java
index 148329d..d872f97 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefUpdate.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepository.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
index ce33f6c..122f6d3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 import java.text.MessageFormat;
@@ -49,9 +49,9 @@ import java.text.MessageFormat;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.storage.file.ReflogReader;
 
 /** A Git repository on a DFS. */
 public abstract class DfsRepository extends Repository {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepositoryBuilder.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java
index 5e526b4..e0c0d0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepositoryDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryDescription.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepositoryDescription.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryDescription.java
index d0816ff..8afad0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRepositoryDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryDescription.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 /** A description of a Git repository on a DFS. */
 public class DfsRepositoryDescription {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsText.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java
index 76afa5d..dedcab0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsText.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import org.eclipse.jgit.nls.NLS;
 import org.eclipse.jgit.nls.TranslationBundle;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/InMemoryRepository.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index 780669d..56efdd6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -1,4 +1,4 @@
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileNotFoundException;
@@ -13,9 +13,9 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.eclipse.jgit.internal.storage.pack.PackExt;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
-import org.eclipse.jgit.storage.pack.PackExt;
 import org.eclipse.jgit.util.RefList;
 
 /**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/LargePackedWholeObject.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
index 46e7d58..4b050c5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/LargePackedWholeObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
@@ -109,7 +109,7 @@ final class LargePackedWholeObject extends ObjectLoader {
 			// again and open the stream from that location instead.
 			//
 			try {
-				ObjectId obj = pack.findObjectForOffset(ctx, objectOffset);
+				ObjectId obj = pack.getReverseIdx(ctx).findObject(objectOffset);
 				return ctx.open(obj, type).openStream();
 			} finally {
 				ctx.release();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/PackInputStream.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java
index b15b2a9..805d243 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadableChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadableChannel.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
index 3680931..5ec7079 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadableChannel.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
 
 import java.io.IOException;
 import java.nio.channels.ReadableByteChannel;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/package-info.java
similarity index 56%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/package-info.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/package-info.java
index 32f3d32..0df5586 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/package-info.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/package-info.java
@@ -1,4 +1,4 @@
 /**
  * Distributed file system based repository storage.
  */
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.internal.storage.dfs;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
new file mode 100644
index 0000000..b6214ba
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import javaewah.EWAHCompressedBitmap;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+
+/**
+ * Base implementation of the PackBitmapIndex.
+ */
+abstract class BasePackBitmapIndex extends PackBitmapIndex {
+	private final ObjectIdOwnerMap<StoredBitmap> bitmaps;
+
+	BasePackBitmapIndex(ObjectIdOwnerMap<StoredBitmap> bitmaps) {
+		this.bitmaps = bitmaps;
+	}
+
+	public EWAHCompressedBitmap getBitmap(AnyObjectId objectId) {
+		StoredBitmap sb = bitmaps.get(objectId);
+		return sb != null ? sb.getBitmap() : null;
+	}
+
+	ObjectIdOwnerMap<StoredBitmap> getBitmaps() {
+		return bitmaps;
+	}
+
+	/**
+	 * Data representation of the bitmap entry restored from a pack index. The
+	 * commit of the bitmap is the map key.
+	 */
+	static final class StoredBitmap extends ObjectIdOwnerMap.Entry {
+		private volatile Object bitmapContainer;
+		private final int flags;
+
+		StoredBitmap(AnyObjectId objectId, EWAHCompressedBitmap bitmap,
+				StoredBitmap xorBitmap, int flags) {
+			super(objectId);
+			this.bitmapContainer = xorBitmap == null
+					? bitmap
+					: new XorCompressedBitmap(bitmap, xorBitmap);
+			this.flags = flags;
+		}
+
+		/**
+		 * Computes and returns the full bitmap.
+		 *
+		 * @return the full bitmap
+		 */
+		EWAHCompressedBitmap getBitmap() {
+			// Fast path to immediately return the expanded result.
+			Object r = bitmapContainer;
+			if (r instanceof EWAHCompressedBitmap)
+				return (EWAHCompressedBitmap) r;
+
+			// Expand the bitmap and cache the result.
+			XorCompressedBitmap xb = (XorCompressedBitmap) r;
+			EWAHCompressedBitmap out = xb.bitmap;
+			for (;;) {
+				r = xb.xorBitmap.bitmapContainer;
+				if (r instanceof EWAHCompressedBitmap) {
+					out = out.xor((EWAHCompressedBitmap) r);
+					bitmapContainer = out;
+					return out;
+				}
+				xb = (XorCompressedBitmap) r;
+				out = out.xor(xb.bitmap);
+			}
+		}
+
+		/** @return the flags associated with the bitmap */
+		int getFlags() {
+			return flags;
+		}
+	}
+
+	private static final class XorCompressedBitmap {
+		final EWAHCompressedBitmap bitmap;
+		final StoredBitmap xorBitmap;
+
+		XorCompressedBitmap(EWAHCompressedBitmap b, StoredBitmap xb) {
+			bitmap = b;
+			xorBitmap = xb;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitSet.java
similarity index 53%
copy from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitSet.java
index 7671cd6..d0ab32f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2012, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,70 +41,81 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
-import java.io.IOException;
-import java.io.OutputStream;
+import java.util.Arrays;
 
-import org.eclipse.jgit.transport.PackedObjectInfo;
-import org.eclipse.jgit.util.NB;
+import javaewah.EWAHCompressedBitmap;
 
 /**
- * Creates the version 2 pack table of contents files.
- *
- * @see PackIndexWriter
- * @see PackIndexV2
+ * A random access BitSet to supports efficient conversions to
+ * EWAHCompressedBitmap.
  */
-class PackIndexWriterV2 extends PackIndexWriter {
-	private static final int MAX_OFFSET_32 = 0x7fffffff;
-	private static final int IS_OFFSET_64 = 0x80000000;
+final class BitSet {
 
-	PackIndexWriterV2(final OutputStream dst) {
-		super(dst);
-	}
+	private long[] words;
 
-	@Override
-	protected void writeImpl() throws IOException {
-		writeTOC(2);
-		writeFanOutTable();
-		writeObjectNames();
-		writeCRCs();
-		writeOffset32();
-		writeOffset64();
-		writeChecksumFooter();
+	BitSet(int initialCapacity) {
+		words = new long[block(initialCapacity) + 1];
 	}
 
-	private void writeObjectNames() throws IOException {
-		for (final PackedObjectInfo oe : entries)
-			oe.copyRawTo(out);
+	final void clear() {
+		Arrays.fill(words, 0);
 	}
 
-	private void writeCRCs() throws IOException {
-		for (final PackedObjectInfo oe : entries) {
-			NB.encodeInt32(tmp, 0, oe.getCRC());
-			out.write(tmp, 0, 4);
+	final void set(int position) {
+		int block = block(position);
+		if (block >= words.length) {
+			long[] buf = new long[2 * block(position)];
+			System.arraycopy(words, 0, buf, 0, words.length);
+			words = buf;
 		}
+		words[block] |= mask(position);
 	}
 
-	private void writeOffset32() throws IOException {
-		int o64 = 0;
-		for (final PackedObjectInfo oe : entries) {
-			final long o = oe.getOffset();
-			if (o <= MAX_OFFSET_32)
-				NB.encodeInt32(tmp, 0, (int) o);
-			else
-				NB.encodeInt32(tmp, 0, IS_OFFSET_64 | o64++);
-			out.write(tmp, 0, 4);
-		}
+	final void clear(int position) {
+		int block = block(position);
+		if (block < words.length)
+			words[block] &= ~mask(position);
 	}
 
-	private void writeOffset64() throws IOException {
-		for (final PackedObjectInfo oe : entries) {
-			final long o = oe.getOffset();
-			if (MAX_OFFSET_32 < o) {
-				NB.encodeInt64(tmp, 0, o);
-				out.write(tmp, 0, 8);
+	final boolean get(int position) {
+		int block = block(position);
+		return block < words.length && (words[block] & mask(position)) != 0;
+	}
+
+	final EWAHCompressedBitmap toEWAHCompressedBitmap() {
+		EWAHCompressedBitmap compressed = new EWAHCompressedBitmap(
+				words.length);
+		int runningEmptyWords = 0;
+		long lastNonEmptyWord = 0;
+		for (long word : words) {
+			if (word == 0) {
+				runningEmptyWords++;
+				continue;
 			}
+
+			if (lastNonEmptyWord != 0)
+				compressed.add(lastNonEmptyWord);
+
+			if (runningEmptyWords > 0) {
+				compressed.addStreamOfEmptyWords(false, runningEmptyWords);
+				runningEmptyWords = 0;
+			}
+
+			lastNonEmptyWord = word;
 		}
+		int bitsThatMatter = 64 - Long.numberOfLeadingZeros(lastNonEmptyWord);
+		if (bitsThatMatter > 0)
+			compressed.add(lastNonEmptyWord, bitsThatMatter);
+		return compressed;
+	}
+
+	private static final int block(int position) {
+		return position >> 6;
+	}
+
+	private static final long mask(int position) {
+		return 1L << position;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
new file mode 100644
index 0000000..6ada2ec
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javaewah.EWAHCompressedBitmap;
+import javaewah.IntIterator;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapObject;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.util.BlockList;
+
+/** A compressed bitmap representation of the entire object graph. */
+public class BitmapIndexImpl implements BitmapIndex {
+	private static final int EXTRA_BITS = 10 * 1024;
+
+	private final PackBitmapIndex packIndex;
+
+	private final MutableBitmapIndex mutableIndex;
+
+	private final int indexObjectCount;
+
+	/**
+	 * Creates a BitmapIndex that is back by Compressed bitmaps.
+	 *
+	 * @param packIndex
+	 *            the bitmap index for the pack.
+	 */
+	public BitmapIndexImpl(PackBitmapIndex packIndex) {
+		this.packIndex = packIndex;
+		mutableIndex = new MutableBitmapIndex();
+		indexObjectCount = packIndex.getObjectCount();
+	}
+
+	PackBitmapIndex getPackBitmapIndex() {
+		return packIndex;
+	}
+
+	public CompressedBitmap getBitmap(AnyObjectId objectId) {
+		EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId);
+		if (compressed == null)
+			return null;
+		return new CompressedBitmap(compressed);
+	}
+
+	public CompressedBitmapBuilder newBitmapBuilder() {
+		return new CompressedBitmapBuilder();
+	}
+
+	private int findPosition(AnyObjectId objectId) {
+		int position = packIndex.findPosition(objectId);
+		if (position < 0) {
+			position = mutableIndex.findPosition(objectId);
+			if (position >= 0)
+				position += indexObjectCount;
+		}
+		return position;
+	}
+
+	private int addObject(AnyObjectId objectId, int type) {
+		int position = findPosition(objectId);
+		if (position < 0) {
+			position = mutableIndex.addObject(objectId, type);
+			position += indexObjectCount;
+		}
+		return position;
+	}
+
+	private static final class ComboBitset {
+		private InflatingBitSet inflatingBitmap;
+
+		private BitSet toAdd;
+
+		private BitSet toRemove;
+
+		private ComboBitset() {
+			this(new EWAHCompressedBitmap());
+		}
+
+		private ComboBitset(EWAHCompressedBitmap bitmap) {
+			this.inflatingBitmap = new InflatingBitSet(bitmap);
+		}
+
+		EWAHCompressedBitmap combine() {
+			EWAHCompressedBitmap toAddCompressed = null;
+			if (toAdd != null) {
+				toAddCompressed = toAdd.toEWAHCompressedBitmap();
+				toAdd = null;
+			}
+
+			EWAHCompressedBitmap toRemoveCompressed = null;
+			if (toRemove != null) {
+				toRemoveCompressed = toRemove.toEWAHCompressedBitmap();
+				toRemove = null;
+			}
+
+			if (toAddCompressed != null)
+				or(toAddCompressed);
+			if (toRemoveCompressed != null)
+				andNot(toRemoveCompressed);
+			return inflatingBitmap.getBitmap();
+		}
+
+		void or(EWAHCompressedBitmap inbits) {
+			if (toRemove != null)
+				combine();
+			inflatingBitmap = inflatingBitmap.or(inbits);
+		}
+
+		void andNot(EWAHCompressedBitmap inbits) {
+			if (toAdd != null || toRemove != null)
+				combine();
+			inflatingBitmap = inflatingBitmap.andNot(inbits);
+		}
+
+		void xor(EWAHCompressedBitmap inbits) {
+			if (toAdd != null || toRemove != null)
+				combine();
+			inflatingBitmap = inflatingBitmap.xor(inbits);
+		}
+
+		boolean contains(int position) {
+			if (toRemove != null && toRemove.get(position))
+				return false;
+			if (toAdd != null && toAdd.get(position))
+				return true;
+			return inflatingBitmap.contains(position);
+		}
+
+		void remove(int position) {
+			if (toAdd != null)
+				toAdd.clear(position);
+
+			if (inflatingBitmap.maybeContains(position)) {
+				if (toRemove == null)
+					toRemove = new BitSet(position + EXTRA_BITS);
+				toRemove.set(position);
+			}
+		}
+
+		void set(int position) {
+			if (toRemove != null)
+				toRemove.clear(position);
+
+			if (toAdd == null)
+				toAdd = new BitSet(position + EXTRA_BITS);
+			toAdd.set(position);
+		}
+	}
+
+	private final class CompressedBitmapBuilder implements BitmapBuilder {
+		private ComboBitset bitset = new ComboBitset();
+
+		public boolean add(AnyObjectId objectId, int type) {
+			int position = addObject(objectId, type);
+			if (bitset.contains(position))
+				return false;
+
+			Bitmap entry = getBitmap(objectId);
+			if (entry != null) {
+				or(entry);
+				return false;
+			}
+
+			bitset.set(position);
+			return true;
+		}
+
+		public boolean contains(AnyObjectId objectId) {
+			int position = findPosition(objectId);
+			return 0 <= position && bitset.contains(position);
+		}
+
+		public void remove(AnyObjectId objectId) {
+			int position = findPosition(objectId);
+			if (0 <= position)
+				bitset.remove(position);
+		}
+
+		public CompressedBitmapBuilder or(Bitmap other) {
+			if (isSameCompressedBitmap(other)) {
+				bitset.or(((CompressedBitmap) other).bitmap);
+			} else if (isSameCompressedBitmapBuilder(other)) {
+				CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
+				bitset.or(b.bitset.combine());
+			} else {
+				throw new IllegalArgumentException();
+			}
+			return this;
+		}
+
+		public CompressedBitmapBuilder andNot(Bitmap other) {
+			if (isSameCompressedBitmap(other)) {
+				bitset.andNot(((CompressedBitmap) other).bitmap);
+			} else if (isSameCompressedBitmapBuilder(other)) {
+				CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
+				bitset.andNot(b.bitset.combine());
+			} else {
+				throw new IllegalArgumentException();
+			}
+			return this;
+		}
+
+		public CompressedBitmapBuilder xor(Bitmap other) {
+			if (isSameCompressedBitmap(other)) {
+				bitset.xor(((CompressedBitmap) other).bitmap);
+			} else if (isSameCompressedBitmapBuilder(other)) {
+				CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
+				bitset.xor(b.bitset.combine());
+			} else {
+				throw new IllegalArgumentException();
+			}
+			return this;
+		}
+
+		/** @return the fully built immutable bitmap */
+		public CompressedBitmap build() {
+			return new CompressedBitmap(bitset.combine());
+		}
+
+		public Iterator<BitmapObject> iterator() {
+			return build().iterator();
+		}
+
+		public int cardinality() {
+			return bitset.combine().cardinality();
+		}
+
+		public boolean removeAllOrNone(PackBitmapIndex index) {
+			if (!packIndex.equals(index))
+				return false;
+
+			EWAHCompressedBitmap curr = bitset.combine()
+					.xor(ones(indexObjectCount));
+
+			IntIterator ii = curr.intIterator();
+			if (ii.hasNext() && ii.next() < indexObjectCount)
+				return false;
+			bitset = new ComboBitset(curr);
+			return true;
+		}
+
+		private BitmapIndexImpl getBitmapIndex() {
+			return BitmapIndexImpl.this;
+		}
+	}
+
+	final class CompressedBitmap implements Bitmap {
+		private final EWAHCompressedBitmap bitmap;
+
+		private CompressedBitmap(EWAHCompressedBitmap bitmap) {
+			this.bitmap = bitmap;
+		}
+
+		public CompressedBitmap or(Bitmap other) {
+			return new CompressedBitmap(bitmap.or(bitmapOf(other)));
+		}
+
+		public CompressedBitmap andNot(Bitmap other) {
+			return new CompressedBitmap(bitmap.andNot(bitmapOf(other)));
+		}
+
+		public CompressedBitmap xor(Bitmap other) {
+			return new CompressedBitmap(bitmap.xor(bitmapOf(other)));
+		}
+
+		private EWAHCompressedBitmap bitmapOf(Bitmap other) {
+			if (isSameCompressedBitmap(other))
+				return ((CompressedBitmap) other).bitmap;
+			if (isSameCompressedBitmapBuilder(other))
+				return ((CompressedBitmapBuilder) other).build().bitmap;
+			CompressedBitmapBuilder builder = newBitmapBuilder();
+			builder.or(other);
+			return builder.build().bitmap;
+		}
+
+		private final IntIterator ofObjectType(int type) {
+			return packIndex.ofObjectType(bitmap, type).intIterator();
+		}
+
+		public Iterator<BitmapObject> iterator() {
+			final IntIterator dynamic = bitmap.andNot(ones(indexObjectCount))
+					.intIterator();
+			final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT);
+			final IntIterator trees = ofObjectType(Constants.OBJ_TREE);
+			final IntIterator blobs = ofObjectType(Constants.OBJ_BLOB);
+			final IntIterator tags = ofObjectType(Constants.OBJ_TAG);
+			return new Iterator<BitmapObject>() {
+				private final BitmapObjectImpl out = new BitmapObjectImpl();
+				private int type;
+				private IntIterator cached = dynamic;
+
+				public boolean hasNext() {
+					if (!cached.hasNext()) {
+						if (commits.hasNext()) {
+							type = Constants.OBJ_COMMIT;
+							cached = commits;
+						} else if (trees.hasNext()) {
+							type = Constants.OBJ_TREE;
+							cached = trees;
+						} else if (blobs.hasNext()) {
+							type = Constants.OBJ_BLOB;
+							cached = blobs;
+						} else if (tags.hasNext()) {
+							type = Constants.OBJ_TAG;
+							cached = tags;
+						} else {
+							return false;
+						}
+					}
+					return true;
+				}
+
+				public BitmapObject next() {
+					if (!hasNext())
+						throw new NoSuchElementException();
+
+					int position = cached.next();
+					if (position < indexObjectCount) {
+						out.type = type;
+						out.objectId = packIndex.getObject(position);
+					} else {
+						position -= indexObjectCount;
+						MutableEntry entry = mutableIndex.getObject(position);
+						out.type = entry.type;
+						out.objectId = entry;
+					}
+					return out;
+				}
+
+				public void remove() {
+					throw new UnsupportedOperationException();
+				}
+			};
+		}
+
+		EWAHCompressedBitmap getEwahCompressedBitmap() {
+			return bitmap;
+		}
+
+		private BitmapIndexImpl getPackBitmapIndex() {
+			return BitmapIndexImpl.this;
+		}
+	}
+
+	private static final class MutableBitmapIndex {
+		private final ObjectIdOwnerMap<MutableEntry>
+				revMap = new ObjectIdOwnerMap<MutableEntry>();
+
+		private final BlockList<MutableEntry>
+				revList = new BlockList<MutableEntry>();
+
+		int findPosition(AnyObjectId objectId) {
+			MutableEntry entry = revMap.get(objectId);
+			if (entry == null)
+				return -1;
+			return entry.position;
+		}
+
+		MutableEntry getObject(int position) {
+			try {
+				MutableEntry entry = revList.get(position);
+				if (entry == null)
+					throw new IllegalArgumentException(MessageFormat.format(
+							JGitText.get().objectNotFound,
+							String.valueOf(position)));
+				return entry;
+			} catch (IndexOutOfBoundsException ex) {
+				throw new IllegalArgumentException(ex);
+			}
+		}
+
+		int addObject(AnyObjectId objectId, int type) {
+			MutableEntry entry = new MutableEntry(
+					objectId, type, revList.size());
+			revList.add(entry);
+			revMap.add(entry);
+			return entry.position;
+		}
+	}
+
+	private static final class MutableEntry extends ObjectIdOwnerMap.Entry {
+		private final int type;
+
+		private final int position;
+
+		MutableEntry(AnyObjectId objectId, int type, int position) {
+			super(objectId);
+			this.type = type;
+			this.position = position;
+		}
+	}
+
+	private static final class BitmapObjectImpl extends BitmapObject {
+		private ObjectId objectId;
+
+		private int type;
+
+		@Override
+		public ObjectId getObjectId() {
+			return objectId;
+		}
+
+		@Override
+		public int getType() {
+			return type;
+		}
+	}
+
+	private boolean isSameCompressedBitmap(Bitmap other) {
+		if (other instanceof CompressedBitmap) {
+			CompressedBitmap b = (CompressedBitmap) other;
+			return this == b.getPackBitmapIndex();
+		}
+		return false;
+	}
+
+	private boolean isSameCompressedBitmapBuilder(Bitmap other) {
+		if (other instanceof CompressedBitmapBuilder) {
+			CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
+			return this == b.getBitmapIndex();
+		}
+		return false;
+	}
+
+	private static final EWAHCompressedBitmap ones(int sizeInBits) {
+		EWAHCompressedBitmap mask = new EWAHCompressedBitmap();
+		mask.addStreamOfEmptyWords(
+				true, sizeInBits / EWAHCompressedBitmap.wordinbits);
+		int remaining = sizeInBits % EWAHCompressedBitmap.wordinbits;
+		if (remaining > 0)
+			mask.add((1L << remaining) - 1, remaining);
+		return mask;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
index 95e3e00..863c553 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteArrayWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.security.MessageDigest;
@@ -51,7 +51,7 @@ import java.util.zip.CRC32;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
-import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
 
 /**
  * A {@link ByteWindow} with an underlying byte array for storage.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
index 8544158..31925d2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteBufferWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -51,7 +51,7 @@ import java.security.MessageDigest;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
-import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
 
 /**
  * A window for accessing git packs using a {@link ByteBuffer} for storage.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
index f0b43fd..ab5eb7c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ByteWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
@@ -42,14 +42,14 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;
 
-import org.eclipse.jgit.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
 
 /**
  * A window of data currently stored within a cache.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index 0bbb3ff..eb4f01c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -42,13 +42,15 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Set;
 
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
@@ -57,9 +59,6 @@ import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
 import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.util.FS;
 
 /**
@@ -147,11 +146,6 @@ class CachedObjectDirectory extends FileObjectDatabase {
 	}
 
 	@Override
-	Collection<? extends CachedPack> getCachedPacks() throws IOException {
-		return wrapped.getCachedPacks();
-	}
-
-	@Override
 	AlternateHandle[] myAlternates() {
 		if (alts == null) {
 			AlternateHandle[] src = wrapped.myAlternates();
@@ -262,6 +256,11 @@ class CachedObjectDirectory extends FileObjectDatabase {
 		wrapped.selectObjectRepresentation(packer, otp, curs);
 	}
 
+	@Override
+	Collection<PackFile> getPacks() {
+		return wrapped.getPacks();
+	}
+
 	private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry {
 		UnpackedObjectId(AnyObjectId id) {
 			super(id);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CheckoutEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java
similarity index 88%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CheckoutEntry.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java
index 6bd9f80..e968119 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CheckoutEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Robin Rosenberg <robin.rosenberg at dewire.com>
+ * Copyright (C) 2011-2013, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,19 +41,22 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
+
+import org.eclipse.jgit.lib.CheckoutEntry;
+import org.eclipse.jgit.lib.ReflogEntry;
 
 /**
  * Parsed information about a checkout.
  */
-public class CheckoutEntry {
+public class CheckoutEntryImpl implements CheckoutEntry {
 	static final String CHECKOUT_MOVING_FROM = "checkout: moving from "; //$NON-NLS-1$
 
 	private String from;
 
 	private String to;
 
-	CheckoutEntry(ReflogEntry reflogEntry) {
+	CheckoutEntryImpl(ReflogEntry reflogEntry) {
 		String comment = reflogEntry.getComment();
 		int p1 = CHECKOUT_MOVING_FROM.length();
 		int p2 = comment.indexOf(" to ", p1); //$NON-NLS-1$
@@ -62,16 +65,10 @@ public class CheckoutEntry {
 		to = comment.substring(p2 + " to ".length(), p3); //$NON-NLS-1$
 	}
 
-	/**
-	 * @return the name of the branch before checkout
-	 */
 	public String getFromBranch() {
 		return from;
 	}
 
-	/**
-	 * @return the name of the branch after checkout
-	 */
 	public String getToBranch() {
 		return to;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
index 0b23934..2f30496 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
@@ -41,10 +41,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.lang.ref.SoftReference;
 
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
+
 class DeltaBaseCache {
 	private static final int CACHE_SZ = 1024;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
similarity index 95%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
index 6c8c1f6..ba45334 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java
@@ -41,13 +41,15 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Set;
 
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
@@ -55,9 +57,6 @@ import org.eclipse.jgit.lib.ObjectDatabase;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.util.FS;
 
 abstract class FileObjectDatabase extends ObjectDatabase {
@@ -262,9 +261,6 @@ abstract class FileObjectDatabase extends ObjectDatabase {
 
 	abstract File getDirectory();
 
-	abstract Collection<? extends CachedPack> getCachedPacks()
-			throws IOException;
-
 	abstract AlternateHandle[] myAlternates();
 
 	abstract boolean tryAgain1();
@@ -292,6 +288,8 @@ abstract class FileObjectDatabase extends ObjectDatabase {
 
 	abstract FileObjectDatabase newCachedFileObjectDatabase();
 
+	abstract Collection<PackFile> getPacks();
+
 	static class AlternateHandle {
 		final FileObjectDatabase db;
 
@@ -299,11 +297,6 @@ abstract class FileObjectDatabase extends ObjectDatabase {
 			this.db = db;
 		}
 
-		@SuppressWarnings("unchecked")
-		Collection<CachedPack> getCachedPacks() throws IOException {
-			return (Collection<CachedPack>) db.getCachedPacks();
-		}
-
 		void close() {
 			db.close();
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 4c27c08..364fdeb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -44,7 +44,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.IOException;
@@ -57,6 +57,8 @@ import org.eclipse.jgit.events.ConfigChangedEvent;
 import org.eclipse.jgit.events.ConfigChangedListener;
 import org.eclipse.jgit.events.IndexChangedEvent;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.AlternateHandle;
+import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.AlternateRepository;
 import org.eclipse.jgit.lib.BaseRepositoryBuilder;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -64,9 +66,10 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.FileObjectDatabase.AlternateHandle;
-import org.eclipse.jgit.storage.file.FileObjectDatabase.AlternateRepository;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.SystemReader;
 
@@ -412,7 +415,7 @@ public class FileRepository extends Repository {
 	public ReflogReader getReflogReader(String refName) throws IOException {
 		Ref ref = getRef(refName);
 		if (ref != null)
-			return new ReflogReader(this, ref.getName());
+			return new ReflogReaderImpl(this, ref.getName());
 		return null;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileSnapshot.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
index ce556a7..708bd64 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileSnapshot.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
similarity index 85%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/GC.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 45f3823..75307de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -41,7 +41,10 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -54,6 +57,7 @@ import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -63,6 +67,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.TreeMap;
 
 import org.eclipse.jgit.dircache.DirCacheIterator;
 import org.eclipse.jgit.errors.CorruptObjectException;
@@ -70,6 +75,9 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.pack.PackWriter.ObjectIdSet;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -80,11 +88,10 @@ import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
 import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.pack.PackWriter;
-import org.eclipse.jgit.storage.pack.PackWriter.ObjectIdSet;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.eclipse.jgit.util.FileUtils;
@@ -192,8 +199,10 @@ public class GC {
 
 			if (!oldPack.shouldBeKept()) {
 				oldPack.close();
-				FileUtils.delete(nameFor(oldName, ".pack"), deleteOptions); //$NON-NLS-1$
-				FileUtils.delete(nameFor(oldName, ".idx"), deleteOptions); //$NON-NLS-1$
+				for (PackExt ext : PackExt.values()) {
+					File f = nameFor(oldName, "." + ext.getExtension()); //$NON-NLS-1$
+					FileUtils.delete(f, deleteOptions);
+				}
 			}
 		}
 		// close the complete object database. Thats my only chance to force
@@ -547,7 +556,9 @@ public class GC {
 		for (ReflogEntry e : rlEntries) {
 			if (e.getWho().getWhen().getTime() < minTime)
 				break;
-			ret.add(e.getNewId());
+			ObjectId newId = e.getNewId();
+			if (newId != null && !ObjectId.zeroId().equals(newId))
+				ret.add(newId);
 			ObjectId oldId = e.getOldId();
 			if (oldId != null && !ObjectId.zeroId().equals(oldId))
 				ret.add(oldId);
@@ -604,26 +615,25 @@ public class GC {
 
 			while (treeWalk.next()) {
 				ObjectId objectId = treeWalk.getObjectId(0);
-			    switch (treeWalk.getRawMode(0) & FileMode.TYPE_MASK) {
-			      case FileMode.TYPE_MISSING:
-			      case FileMode.TYPE_GITLINK:
-			        continue;
-			      case FileMode.TYPE_TREE:
-			      case FileMode.TYPE_FILE:
-			      case FileMode.TYPE_SYMLINK:
-			        ret.add(objectId);
-			        continue;
-			      default:
+				switch (treeWalk.getRawMode(0) & FileMode.TYPE_MASK) {
+				case FileMode.TYPE_MISSING:
+				case FileMode.TYPE_GITLINK:
+					continue;
+				case FileMode.TYPE_TREE:
+				case FileMode.TYPE_FILE:
+				case FileMode.TYPE_SYMLINK:
+					ret.add(objectId);
+					continue;
+				default:
 					throw new IOException(MessageFormat.format(
-							JGitText.get().corruptObjectInvalidMode3, String
-									.format("%o", Integer.valueOf(treeWalk //$NON-NLS-1$
-											.getRawMode(0)),
-											(objectId == null) ? "null" //$NON-NLS-1$
-													: objectId.name(), treeWalk
-											.getPathString(), repo
-											.getIndexFile())));
-			    }
-			  }
+							JGitText.get().corruptObjectInvalidMode3,
+							String.format("%o", //$NON-NLS-1$
+									Integer.valueOf(treeWalk.getRawMode(0))),
+							(objectId == null) ? "null" : objectId.name(), //$NON-NLS-1$
+							treeWalk.getPathString(), //
+							repo.getIndexFile()));
+				}
+			}
 			return ret;
 		} finally {
 			if (revWalk != null)
@@ -636,7 +646,22 @@ public class GC {
 			Set<? extends ObjectId> have, Set<ObjectId> tagTargets,
 			List<ObjectIdSet> excludeObjects) throws IOException {
 		File tmpPack = null;
-		File tmpIdx = null;
+		Map<PackExt, File> tmpExts = new TreeMap<PackExt, File>(
+				new Comparator<PackExt>() {
+					public int compare(PackExt o1, PackExt o2) {
+						// INDEX entries must be returned last, so the pack
+						// scanner does pick up the new pack until all the
+						// PackExt entries have been written.
+						if (o1 == o2)
+							return 0;
+						if (o1 == PackExt.INDEX)
+							return 1;
+						if (o2 == PackExt.INDEX)
+							return -1;
+						return Integer.signum(o1.hashCode() - o2.hashCode());
+					}
+
+				});
 		PackWriter pw = new PackWriter(repo);
 		try {
 			// prepare the PackWriter
@@ -655,9 +680,10 @@ public class GC {
 			String id = pw.computeName().getName();
 			File packdir = new File(repo.getObjectsDirectory(), "pack"); //$NON-NLS-1$
 			tmpPack = File.createTempFile("gc_", ".pack_tmp", packdir); //$NON-NLS-1$ //$NON-NLS-2$
-			tmpIdx = new File(packdir, tmpPack.getName().substring(0,
-					tmpPack.getName().lastIndexOf('.'))
-					+ ".idx_tmp"); //$NON-NLS-1$
+			final String tmpBase = tmpPack.getName()
+					.substring(0, tmpPack.getName().lastIndexOf('.'));
+			File tmpIdx = new File(packdir, tmpBase + ".idx_tmp"); //$NON-NLS-1$
+			tmpExts.put(INDEX, tmpIdx);
 
 			if (!tmpIdx.createNewFile())
 				throw new IOException(MessageFormat.format(
@@ -676,7 +702,6 @@ public class GC {
 			}
 
 			// write the packindex
-			@SuppressWarnings("resource")
 			FileChannel idxChannel = new FileOutputStream(tmpIdx).getChannel();
 			OutputStream idxStream = Channels.newOutputStream(idxChannel);
 			try {
@@ -687,38 +712,82 @@ public class GC {
 				idxChannel.close();
 			}
 
+			if (pw.prepareBitmapIndex(pm)) {
+				File tmpBitmapIdx = new File(packdir, tmpBase + ".bitmap_tmp"); //$NON-NLS-1$
+				tmpExts.put(BITMAP_INDEX, tmpBitmapIdx);
+
+				if (!tmpBitmapIdx.createNewFile())
+					throw new IOException(MessageFormat.format(
+							JGitText.get().cannotCreateIndexfile,
+							tmpBitmapIdx.getPath()));
+
+				idxChannel = new FileOutputStream(tmpBitmapIdx).getChannel();
+				idxStream = Channels.newOutputStream(idxChannel);
+				try {
+					pw.writeBitmapIndex(idxStream);
+				} finally {
+					idxChannel.force(true);
+					idxStream.close();
+					idxChannel.close();
+				}
+			}
+
 			// rename the temporary files to real files
 			File realPack = nameFor(id, ".pack"); //$NON-NLS-1$
+
+			// if the packfile already exists (because we are rewriting a
+			// packfile for the same set of objects maybe with different
+			// PackConfig) then make sure we get rid of all handles on the file.
+			// Windows will not allow for rename otherwise.
+			if (realPack.exists())
+				for (PackFile p : repo.getObjectDatabase().getPacks())
+					if (realPack.getPath().equals(p.getPackFile().getPath())) {
+						p.close();
+						break;
+					}
 			tmpPack.setReadOnly();
-			File realIdx = nameFor(id, ".idx"); //$NON-NLS-1$
-			realIdx.setReadOnly();
 			boolean delete = true;
 			try {
-				if (!tmpPack.renameTo(realPack))
-					return null;
+				FileUtils.rename(tmpPack, realPack);
 				delete = false;
-				if (!tmpIdx.renameTo(realIdx)) {
-					File newIdx = new File(realIdx.getParentFile(),
-							realIdx.getName() + ".new"); //$NON-NLS-1$
-					if (!tmpIdx.renameTo(newIdx))
-						newIdx = tmpIdx;
-					throw new IOException(MessageFormat.format(
-							JGitText.get().panicCantRenameIndexFile, newIdx,
-							realIdx));
+				for (Map.Entry<PackExt, File> tmpEntry : tmpExts.entrySet()) {
+					File tmpExt = tmpEntry.getValue();
+					tmpExt.setReadOnly();
+
+					File realExt = nameFor(
+							id, "." + tmpEntry.getKey().getExtension()); //$NON-NLS-1$
+					try {
+						FileUtils.rename(tmpExt, realExt);
+					} catch (IOException e) {
+						File newExt = new File(realExt.getParentFile(),
+								realExt.getName() + ".new"); //$NON-NLS-1$
+						if (!tmpExt.renameTo(newExt))
+							newExt = tmpExt;
+						throw new IOException(MessageFormat.format(
+								JGitText.get().panicCantRenameIndexFile, newExt,
+								realExt));
+					}
 				}
+
 			} finally {
-				if (delete && tmpPack.exists())
-					tmpPack.delete();
-				if (delete && tmpIdx.exists())
-					tmpIdx.delete();
+				if (delete) {
+					if (tmpPack.exists())
+						tmpPack.delete();
+					for (File tmpExt : tmpExts.values()) {
+						if (tmpExt.exists())
+							tmpExt.delete();
+					}
+				}
 			}
 			return repo.getObjectDatabase().openPack(realPack);
 		} finally {
 			pw.release();
 			if (tmpPack != null && tmpPack.exists())
 				tmpPack.delete();
-			if (tmpIdx != null && tmpIdx.exists())
-				tmpIdx.delete();
+			for (File tmpExt : tmpExts.values()) {
+				if (tmpExt.exists())
+					tmpExt.delete();
+			}
 		}
 	}
 
@@ -768,6 +837,18 @@ public class GC {
 		 * The number of refs stored in pack files.
 		 */
 		public long numberOfPackedRefs;
+
+		public String toString() {
+			final StringBuilder b = new StringBuilder();
+			b.append("numberOfPackedObjects=").append(numberOfPackedObjects); //$NON-NLS-1$
+			b.append(", numberOfPackFiles=").append(numberOfPackFiles); //$NON-NLS-1$
+			b.append(", numberOfLooseObjects=").append(numberOfLooseObjects); //$NON-NLS-1$
+			b.append(", numberOfLooseRefs=").append(numberOfLooseRefs); //$NON-NLS-1$
+			b.append(", numberOfPackedRefs=").append(numberOfPackedRefs); //$NON-NLS-1$
+			b.append(", sizeOfLooseObjects=").append(sizeOfLooseObjects); //$NON-NLS-1$
+			b.append(", sizeOfPackedObjects=").append(sizeOfPackedObjects); //$NON-NLS-1$
+			return b.toString();
+		}
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InflatingBitSet.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InflatingBitSet.java
new file mode 100644
index 0000000..7d8e124
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InflatingBitSet.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import javaewah.EWAHCompressedBitmap;
+import javaewah.IntIterator;
+
+/**
+ * A wrapper around the EWAHCompressedBitmap optimized for the contains
+ * operation.
+ */
+final class InflatingBitSet {
+	private static final long[] EMPTY = new long[0];
+
+	private final EWAHCompressedBitmap bitmap;
+
+	private IntIterator iterator;
+
+	private long[] inflated;
+
+	private int nextPosition = -1;
+
+	private final int sizeInBits;
+
+	InflatingBitSet(EWAHCompressedBitmap bitmap) {
+		this(bitmap, EMPTY);
+	}
+
+	private InflatingBitSet(EWAHCompressedBitmap orBitmap, long[] inflated) {
+		this.bitmap = orBitmap;
+		this.inflated = inflated;
+		this.sizeInBits = bitmap.sizeInBits();
+	}
+
+	final boolean maybeContains(int position) {
+		if (get(position))
+			return true;
+		return nextPosition <= position && position < sizeInBits;
+	}
+
+	final boolean contains(int position) {
+		if (get(position))
+			return true;
+		if (position <= nextPosition || position >= sizeInBits)
+			return position == nextPosition;
+
+		if (iterator == null) {
+			iterator = bitmap.intIterator();
+			if (iterator.hasNext())
+				nextPosition = iterator.next();
+			else
+				return false;
+		} else if (!iterator.hasNext())
+			return false;
+
+		int positionBlock = block(position);
+		if (positionBlock >= inflated.length) {
+			long[] tmp = new long[block(sizeInBits) + 1];
+			System.arraycopy(inflated, 0, tmp, 0, inflated.length);
+			inflated = tmp;
+		}
+
+		int block = block(nextPosition);
+		long word = mask(nextPosition);
+		int end = Math.max(nextPosition, position) | 63;
+		while (iterator.hasNext()) {
+			nextPosition = iterator.next();
+			if (end < nextPosition)
+				break;
+
+			int b = block(nextPosition);
+			long m = mask(nextPosition);
+			if (block == b) {
+				word |= m;
+			} else {
+				inflated[block] = word;
+				block = b;
+				word = m;
+			}
+		}
+		inflated[block] = word;
+		return block == positionBlock && (word & mask(position)) != 0;
+	}
+
+	private final boolean get(int position) {
+		int b = block(position);
+		return b < inflated.length && (inflated[b] & mask(position)) != 0;
+	}
+
+	private static final int block(int position) {
+		return position >> 6;
+	}
+
+	private static final long mask(int position) {
+		return 1L << position;
+	}
+
+	private final boolean isEmpty() {
+		return sizeInBits == 0;
+	}
+
+	final InflatingBitSet or(EWAHCompressedBitmap other) {
+		if (other.sizeInBits() == 0)
+			return this;
+		return new InflatingBitSet(bitmap.or(other), inflated);
+	}
+
+	final InflatingBitSet andNot(EWAHCompressedBitmap other) {
+		if (isEmpty())
+			return this;
+		return new InflatingBitSet(bitmap.andNot(other));
+	}
+
+	final InflatingBitSet xor(EWAHCompressedBitmap other) {
+		if (isEmpty()) {
+			if (other.sizeInBits() == 0)
+				return this;
+			return new InflatingBitSet(other);
+		}
+		return new InflatingBitSet(bitmap.xor(other));
+	}
+
+	final EWAHCompressedBitmap getBitmap() {
+		return bitmap;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedDeltaObject.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedDeltaObject.java
index 8d15fcf..f6bbae2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedDeltaObject.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -55,12 +55,12 @@ import java.util.zip.InflaterInputStream;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
+import org.eclipse.jgit.internal.storage.pack.DeltaStream;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectStream;
-import org.eclipse.jgit.storage.pack.BinaryDelta;
-import org.eclipse.jgit.storage.pack.DeltaStream;
 import org.eclipse.jgit.util.io.TeeInputStream;
 
 class LargePackedDeltaObject extends ObjectLoader {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java
index 9550be4..4d6e4dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedWholeObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LargePackedWholeObject.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
similarity index 84%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalCachedPack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
index 82503b6..b70ebcf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
@@ -41,45 +41,34 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 
 class LocalCachedPack extends CachedPack {
 	private final ObjectDirectory odb;
 
-	private final Set<ObjectId> tips;
-
 	private final String[] packNames;
 
 	private PackFile[] packs;
 
-	LocalCachedPack(ObjectDirectory odb, Set<ObjectId> tips,
-			List<String> packNames) {
+	LocalCachedPack(ObjectDirectory odb, List<String> packNames) {
 		this.odb = odb;
-
-		if (tips.size() == 1)
-			this.tips = Collections.singleton(tips.iterator().next());
-		else
-			this.tips = Collections.unmodifiableSet(tips);
-
 		this.packNames = packNames.toArray(new String[packNames.size()]);
 	}
 
-	@Override
-	public Set<ObjectId> getTips() {
-		return tips;
+	LocalCachedPack(List<PackFile> packs) {
+		odb = null;
+		packNames = null;
+		this.packs = packs.toArray(new PackFile[packs.size()]);
 	}
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java
index 08bb8e6..a42e679 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectRepresentation.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectRepresentation.java
@@ -41,12 +41,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
 
 class LocalObjectRepresentation extends StoredObjectRepresentation {
 	static LocalObjectRepresentation newWhole(PackFile f, long p, long length) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java
similarity index 89%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java
index c7ef2c9..fafe132 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LocalObjectToPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalObjectToPack.java
@@ -41,11 +41,11 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.lib.AnyObjectId;
 
 /** {@link ObjectToPack} for {@link ObjectDirectory}. */
 class LocalObjectToPack extends ObjectToPack {
@@ -58,8 +58,8 @@ class LocalObjectToPack extends ObjectToPack {
 	/** Length of the data section of the object. */
 	long length;
 
-	LocalObjectToPack(RevObject obj) {
-		super(obj);
+	LocalObjectToPack(AnyObjectId src, final int type) {
+		super(src, type);
 	}
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index 4877f02..06eb42c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -437,11 +437,16 @@ public class LockFile {
 		}
 
 		saveStatInformation();
-		if (lck.renameTo(ref))
+		if (lck.renameTo(ref)) {
+			haveLck = false;
 			return true;
-		if (!ref.exists() || deleteRef())
-			if (renameLock())
+		}
+		if (!ref.exists() || deleteRef()) {
+			if (renameLock()) {
+				haveLck = false;
 				return true;
+			}
+		}
 		unlock();
 		return false;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
similarity index 88%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 22ef45d..3b6901b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -41,7 +41,10 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -63,6 +66,9 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.jgit.errors.PackMismatchException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
@@ -72,13 +78,8 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.IO;
-import org.eclipse.jgit.util.RawParseUtils;
 
 /**
  * Traditional file system based {@link ObjectDatabase}.
@@ -115,12 +116,8 @@ public class ObjectDirectory extends FileObjectDatabase {
 
 	private final File alternatesFile;
 
-	private final File cachedPacksFile;
-
 	private final AtomicReference<PackList> packList;
 
-	private final AtomicReference<CachedPackList> cachedPacks;
-
 	private final FS fs;
 
 	private final AtomicReference<AlternateHandle[]> alternates;
@@ -158,9 +155,7 @@ public class ObjectDirectory extends FileObjectDatabase {
 		infoDirectory = new File(objects, "info"); //$NON-NLS-1$
 		packDirectory = new File(objects, "pack"); //$NON-NLS-1$
 		alternatesFile = new File(infoDirectory, "alternates"); //$NON-NLS-1$
-		cachedPacksFile = new File(infoDirectory, "cached-packs"); //$NON-NLS-1$
 		packList = new AtomicReference<PackList>(NO_PACKS);
-		cachedPacks = new AtomicReference<CachedPackList>();
 		unpackedObjectCache = new UnpackedObjectCache();
 		this.fs = fs;
 		this.shallowFile = shallowFile;
@@ -237,6 +232,7 @@ public class ObjectDirectory extends FileObjectDatabase {
 	 *         containing objects referenced by commits further back in the
 	 *         history of the repository.
 	 */
+	@Override
 	public Collection<PackFile> getPacks() {
 		PackList list = packList.get();
 		if (list == NO_PACKS)
@@ -245,83 +241,6 @@ public class ObjectDirectory extends FileObjectDatabase {
 		return Collections.unmodifiableCollection(Arrays.asList(packs));
 	}
 
-	@Override
-	Collection<? extends CachedPack> getCachedPacks() throws IOException {
-		CachedPackList list = cachedPacks.get();
-		if (list == null || list.snapshot.isModified(cachedPacksFile))
-			list = scanCachedPacks(list);
-
-		Collection<CachedPack> result = list.getCachedPacks();
-		boolean resultIsCopy = false;
-
-		for (AlternateHandle h : myAlternates()) {
-			Collection<CachedPack> altPacks = h.getCachedPacks();
-			if (altPacks.isEmpty())
-				continue;
-
-			if (result.isEmpty()) {
-				result = altPacks;
-				continue;
-			}
-
-			if (!resultIsCopy) {
-				result = new ArrayList<CachedPack>(result);
-				resultIsCopy = true;
-			}
-			result.addAll(altPacks);
-		}
-		return result;
-	}
-
-	private CachedPackList scanCachedPacks(CachedPackList old)
-			throws IOException {
-		FileSnapshot s = FileSnapshot.save(cachedPacksFile);
-		byte[] buf;
-		try {
-			buf = IO.readFully(cachedPacksFile);
-		} catch (FileNotFoundException e) {
-			buf = new byte[0];
-		}
-
-		if (old != null && old.snapshot.equals(s)
-				&& Arrays.equals(old.raw, buf)) {
-			old.snapshot.setClean(s);
-			return old;
-		}
-
-		ArrayList<LocalCachedPack> list = new ArrayList<LocalCachedPack>(4);
-		Set<ObjectId> tips = new HashSet<ObjectId>();
-		int ptr = 0;
-		while (ptr < buf.length) {
-			if (buf[ptr] == '#' || buf[ptr] == '\n') {
-				ptr = RawParseUtils.nextLF(buf, ptr);
-				continue;
-			}
-
-			if (buf[ptr] == '+') {
-				tips.add(ObjectId.fromString(buf, ptr + 2));
-				ptr = RawParseUtils.nextLF(buf, ptr + 2);
-				continue;
-			}
-
-			List<String> names = new ArrayList<String>(4);
-			while (ptr < buf.length && buf[ptr] == 'P') {
-				int end = RawParseUtils.nextLF(buf, ptr);
-				if (buf[end - 1] == '\n')
-					end--;
-				names.add(RawParseUtils.decode(buf, ptr + 2, end));
-				ptr = RawParseUtils.nextLF(buf, end);
-			}
-
-			if (!tips.isEmpty() && !names.isEmpty()) {
-				list.add(new LocalCachedPack(this, tips, names));
-				tips = new HashSet<ObjectId>();
-			}
-		}
-		list.trimToSize();
-		return new CachedPackList(s, Collections.unmodifiableList(list), buf);
-	}
-
 	/**
 	 * Add a single existing pack to the list of available pack files.
 	 *
@@ -335,10 +254,23 @@ public class ObjectDirectory extends FileObjectDatabase {
 	public PackFile openPack(final File pack)
 			throws IOException {
 		final String p = pack.getName();
-		if (p.length() != 50 || !p.startsWith("pack-") || !p.endsWith(".pack")) //$NON-NLS-1$
+		if (p.length() != 50 || !p.startsWith("pack-") || !p.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
 			throw new IOException(MessageFormat.format(JGitText.get().notAValidPack, pack));
 
-		PackFile res = new PackFile(pack);
+		// The pack and index are assumed to exist. The existence of other
+		// extensions needs to be explicitly checked.
+		//
+		int extensions = PACK.getBit() | INDEX.getBit();
+		final String base = p.substring(0, p.length() - 4);
+		for (PackExt ext : PackExt.values()) {
+			if ((extensions & ext.getBit()) == 0) {
+				final String name = base + ext.getExtension();
+				if (new File(pack.getParentFile(), name).exists())
+					extensions |= ext.getBit();
+			}
+		}
+
+		PackFile res = new PackFile(pack, extensions);
 		insertPack(res);
 		return res;
 	}
@@ -720,9 +652,14 @@ public class ObjectDirectory extends FileObjectDatabase {
 			if (indexName.length() != 49 || !indexName.endsWith(".idx")) //$NON-NLS-1$
 				continue;
 
-			final String base = indexName.substring(0, indexName.length() - 4);
-			final String packName = base + ".pack"; //$NON-NLS-1$
-			if (!names.contains(packName)) {
+			final String base = indexName.substring(0, indexName.length() - 3);
+			int extensions = 0;
+			for (PackExt ext : PackExt.values()) {
+				if (names.contains(base + ext.getExtension()))
+					extensions |= ext.getBit();
+			}
+
+			if ((extensions & PACK.getBit()) == 0) {
 				// Sometimes C Git's HTTP fetch transport leaves a
 				// .idx file behind and does not download the .pack.
 				// We have to skip over such useless indexes.
@@ -730,6 +667,7 @@ public class ObjectDirectory extends FileObjectDatabase {
 				continue;
 			}
 
+			final String packName = base + PACK.getExtension();
 			final PackFile oldPack = forReuse.remove(packName);
 			if (oldPack != null) {
 				list.add(oldPack);
@@ -737,7 +675,7 @@ public class ObjectDirectory extends FileObjectDatabase {
 			}
 
 			final File packFile = new File(packDirectory, packName);
-			list.add(new PackFile(packFile));
+			list.add(new PackFile(packFile, extensions));
 			foundNew = true;
 		}
 
@@ -869,26 +807,6 @@ public class ObjectDirectory extends FileObjectDatabase {
 		}
 	}
 
-	private static final class CachedPackList {
-		final FileSnapshot snapshot;
-
-		final Collection<LocalCachedPack> packs;
-
-		final byte[] raw;
-
-		CachedPackList(FileSnapshot sn, List<LocalCachedPack> list, byte[] buf) {
-			snapshot = sn;
-			packs = list;
-			raw = buf;
-		}
-
-		@SuppressWarnings("unchecked")
-		Collection<CachedPack> getCachedPacks() {
-			Collection p = packs;
-			return p;
-		}
-	}
-
 	@Override
 	public ObjectDatabase newCachedDatabase() {
 		return newCachedFileObjectDatabase();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
index 75a1a2c..c55c60a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.EOFException;
 import java.io.File;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryPackParser.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
index b61b75c..1846c47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryPackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.FileOutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
new file mode 100644
index 0000000..129da22
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+
+import javaewah.EWAHCompressedBitmap;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Logical representation of the bitmap data stored in the pack index.
+ * {@link ObjectId}s are encoded as a single integer in the range [0,
+ * {@link #getObjectCount()}). Compressed bitmaps are available at certain
+ * {@code ObjectId}s, which represent all of the objects reachable from that
+ * {@code ObjectId} (include the {@code ObjectId} itself). The meaning of the
+ * positions in the bitmaps can be decoded using {@link #getObject(int)} and
+ * {@link #ofObjectType(EWAHCompressedBitmap, int)}. Furthermore,
+ * {@link #findPosition(AnyObjectId)} can be used to build other bitmaps that a
+ * compatible with the encoded bitmaps available from the index.
+ */
+public abstract class PackBitmapIndex {
+	/** Flag bit denoting the bitmap should be reused during index creation. */
+	public static final int FLAG_REUSE = 1;
+
+	/**
+	 * Read an existing pack bitmap index file from a buffered stream.
+	 * <p>
+	 * The format of the file will be automatically detected and a proper access
+	 * implementation for that format will be constructed and returned to the
+	 * caller. The file may or may not be held open by the returned instance.
+	 *
+	 * @param idxFile
+	 *            existing pack .bitmap to read.
+	 * @param packIndex
+	 *            the pack index for the corresponding pack file.
+	 * @param reverseIndex
+	 *            the pack reverse index for the corresponding pack file.
+	 * @return a copy of the index in-memory.
+	 * @throws IOException
+	 *             the stream cannot be read.
+	 * @throws CorruptObjectException
+	 *             the stream does not contain a valid pack bitmap index.
+	 */
+	public static PackBitmapIndex open(
+			File idxFile, PackIndex packIndex, PackReverseIndex reverseIndex)
+			throws IOException {
+		final FileInputStream fd = new FileInputStream(idxFile);
+		try {
+			return read(fd, packIndex, reverseIndex);
+		} catch (IOException ioe) {
+			final String path = idxFile.getAbsolutePath();
+			final IOException err;
+			err = new IOException(MessageFormat.format(
+					JGitText.get().unreadablePackIndex, path));
+			err.initCause(ioe);
+			throw err;
+		} finally {
+			try {
+				fd.close();
+			} catch (IOException err2) {
+				// ignore
+			}
+		}
+	}
+
+	/**
+	 * Read an existing pack bitmap index file from a buffered stream.
+	 * <p>
+	 * The format of the file will be automatically detected and a proper access
+	 * implementation for that format will be constructed and returned to the
+	 * caller. The file may or may not be held open by the returned instance.
+	 *
+	 * @param fd
+	 *            stream to read the bitmap index file from. The stream must be
+	 *            buffered as some small IOs are performed against the stream.
+	 *            The caller is responsible for closing the stream.
+	 * @param packIndex
+	 *            the pack index for the corresponding pack file.
+	 * @param reverseIndex
+	 *            the pack reverse index for the corresponding pack file.
+	 * @return a copy of the index in-memory.
+	 * @throws IOException
+	 *             the stream cannot be read.
+	 * @throws CorruptObjectException
+	 *             the stream does not contain a valid pack bitmap index.
+	 */
+	public static PackBitmapIndex read(
+			InputStream fd, PackIndex packIndex, PackReverseIndex reverseIndex)
+			throws IOException {
+		return new PackBitmapIndexV1(fd, packIndex, reverseIndex);
+	}
+
+	/** Footer checksum applied on the bottom of the pack file. */
+	byte[] packChecksum;
+
+	/**
+	 * Finds the position in the bitmap of the object.
+	 *
+	 * @param objectId
+	 *            the id for which the bitmap position will be found.
+	 * @return the bitmap id or -1 if the object was not found.
+	 */
+	public abstract int findPosition(AnyObjectId objectId);
+
+	/**
+	 * Get the object at the bitmap position.
+	 *
+	 * @param position
+	 *            the id for which the object will be found.
+	 * @return the ObjectId.
+	 * @throws IllegalArgumentException
+	 *             when the item is not found.
+	 */
+	public abstract ObjectId getObject(int position) throws IllegalArgumentException;
+
+	/**
+	 * Returns a bitmap containing positions for objects that have the given Git
+	 * type.
+	 *
+	 * @param bitmap
+	 *            the object bitmap.
+	 * @param type
+	 *            the Git type.
+	 * @return the object bitmap with only objects of the Git type.
+	 */
+	public abstract EWAHCompressedBitmap ofObjectType(
+			EWAHCompressedBitmap bitmap, int type);
+
+	/**
+	 * Returns the previously constructed bitmap for the object.
+	 *
+	 * @param objectId
+	 *            the id for which the bitmap will be found.
+	 * @return the bitmap or null if the object was not found.
+	 */
+	public abstract EWAHCompressedBitmap getBitmap(AnyObjectId objectId);
+
+	/**
+	 * Obtain the total number of objects described by this index.
+	 * {@code getObjectCount() - 1} is the largest bit that will be set in a
+	 * bitmap.
+	 *
+	 * @return number of objects in this index, and likewise in the associated
+	 *         pack that this index was generated from.
+	 */
+	public abstract int getObjectCount();
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
new file mode 100644
index 0000000..5d31b18
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javaewah.EWAHCompressedBitmap;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.util.BlockList;
+
+/**
+ * Helper for constructing {@link PackBitmapIndex}es.
+ */
+public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
+	private static final int MAX_XOR_OFFSET_SEARCH = 10;
+
+	private final EWAHCompressedBitmap commits;
+	private final EWAHCompressedBitmap trees;
+	private final EWAHCompressedBitmap blobs;
+	private final EWAHCompressedBitmap tags;
+	private final ObjectToPack[] byOffset;
+	private final BlockList<StoredBitmap>
+			byAddOrder = new BlockList<StoredBitmap>();
+	private final ObjectIdOwnerMap<PositionEntry>
+			positionEntries = new ObjectIdOwnerMap<PositionEntry>();
+
+	/**
+	 * Creates a PackBitmapIndex used for building the contents of an index
+	 * file.
+	 *
+	 * @param byName
+	 *            objects sorted by name.
+	 */
+	public PackBitmapIndexBuilder(List<ObjectToPack> byName) {
+		super(new ObjectIdOwnerMap<StoredBitmap>());
+		byOffset = sortByOffset(byName);
+
+		int sizeInWords = Math.max(byOffset.length / 64, 4);
+		commits = new EWAHCompressedBitmap(sizeInWords);
+		trees = new EWAHCompressedBitmap(sizeInWords);
+		blobs = new EWAHCompressedBitmap(sizeInWords);
+		tags = new EWAHCompressedBitmap(sizeInWords);
+		for (int i = 0; i < byOffset.length; i++) {
+			int type = byOffset[i].getType();
+			switch (type) {
+			case Constants.OBJ_COMMIT:
+				commits.set(i);
+				break;
+			case Constants.OBJ_TREE:
+				trees.set(i);
+				break;
+			case Constants.OBJ_BLOB:
+				blobs.set(i);
+				break;
+			case Constants.OBJ_TAG:
+				tags.set(i);
+				break;
+			default:
+				throw new IllegalArgumentException(MessageFormat.format(
+						JGitText.get().badObjectType, String.valueOf(type)));
+			}
+		}
+	}
+
+	private ObjectToPack[] sortByOffset(List<ObjectToPack> entries) {
+		ObjectToPack[] result = new ObjectToPack[entries.size()];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = entries.get(i);
+			positionEntries.add(new PositionEntry(result[i], i));
+		}
+		Arrays.sort(result, new Comparator<ObjectToPack>() {
+			public int compare(ObjectToPack a, ObjectToPack b) {
+				return Long.signum(a.getOffset() - b.getOffset());
+			}
+		});
+		for (int i = 0; i < result.length; i++)
+			positionEntries.get(result[i]).offsetPosition = i;
+		return result;
+	}
+
+	/**
+	 * Stores the bitmap for the objectId.
+	 *
+	 * @param objectId
+	 *            the object id key for the bitmap.
+	 * @param bitmap
+	 *            the bitmap
+	 * @param flags
+	 *            the flags to be stored with the bitmap
+	 */
+	public void addBitmap(AnyObjectId objectId, Bitmap bitmap, int flags) {
+		if (bitmap instanceof BitmapBuilder)
+			bitmap = ((BitmapBuilder) bitmap).build();
+
+		EWAHCompressedBitmap compressed;
+		if (bitmap instanceof CompressedBitmap)
+			compressed = ((CompressedBitmap) bitmap).getEwahCompressedBitmap();
+		else
+			throw new IllegalArgumentException(bitmap.getClass().toString());
+
+		addBitmap(objectId, compressed, flags);
+	}
+
+	/**
+	 * Stores the bitmap for the objectId.
+	 *
+	 * @param objectId
+	 *            the object id key for the bitmap.
+	 * @param bitmap
+	 *            the bitmap
+	 * @param flags
+	 *            the flags to be stored with the bitmap
+	 */
+	public void addBitmap(
+			AnyObjectId objectId, EWAHCompressedBitmap bitmap, int flags) {
+		StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags);
+		getBitmaps().add(result);
+		byAddOrder.add(result);
+	}
+
+	@Override
+	public EWAHCompressedBitmap ofObjectType(
+			EWAHCompressedBitmap bitmap, int type) {
+		switch (type) {
+		case Constants.OBJ_BLOB:
+			return getBlobs().and(bitmap);
+		case Constants.OBJ_TREE:
+			return getTrees().and(bitmap);
+		case Constants.OBJ_COMMIT:
+			return getCommits().and(bitmap);
+		case Constants.OBJ_TAG:
+			return getTags().and(bitmap);
+		}
+		throw new IllegalArgumentException();
+	}
+
+	@Override
+	public int findPosition(AnyObjectId objectId) {
+		PositionEntry entry = positionEntries.get(objectId);
+		if (entry == null)
+			return -1;
+		return entry.offsetPosition;
+	}
+
+	@Override
+	public ObjectId getObject(int position) throws IllegalArgumentException {
+		ObjectId objectId = byOffset[position];
+		if (objectId == null)
+			throw new IllegalArgumentException();
+		return objectId;
+	}
+
+	/** @return the commit object bitmap. */
+	public EWAHCompressedBitmap getCommits() {
+		return commits;
+	}
+
+	/** @return the tree object bitmap. */
+	public EWAHCompressedBitmap getTrees() {
+		return trees;
+	}
+
+	/** @return the blob object bitmap. */
+	public EWAHCompressedBitmap getBlobs() {
+		return blobs;
+	}
+
+	/** @return the tag object bitmap. */
+	public EWAHCompressedBitmap getTags() {
+		return tags;
+	}
+
+	/** @return the index storage options. */
+	public int getOptions() {
+		return PackBitmapIndexV1.OPT_FULL;
+	}
+
+	/** @return the number of bitmaps. */
+	public int getBitmapCount() {
+		return getBitmaps().size();
+	}
+
+	/** Removes all the bitmaps entries added. */
+	public void clearBitmaps() {
+		byAddOrder.clear();
+		getBitmaps().clear();
+	}
+
+	@Override
+	public int getObjectCount() {
+		return byOffset.length;
+	}
+
+	/** @return an iterator over the xor compressed entries. */
+	public Iterable<StoredEntry> getCompressedBitmaps() {
+		// Add order is from oldest to newest. The reverse add order is the
+		// output order.
+		return new Iterable<StoredEntry>() {
+			public Iterator<StoredEntry> iterator() {
+				return new Iterator<StoredEntry>() {
+					private int index = byAddOrder.size() - 1;
+
+					public boolean hasNext() {
+						return index >= 0;
+					}
+
+					public StoredEntry next() {
+						if (!hasNext())
+							throw new NoSuchElementException();
+						StoredBitmap item = byAddOrder.get(index);
+						int bestXorOffset = 0;
+						EWAHCompressedBitmap bestBitmap = item.getBitmap();
+
+						// Attempt to compress the bitmap with an XOR of the
+						// previously written entries.
+						for (int i = 1; i <= MAX_XOR_OFFSET_SEARCH; i++) {
+							int curr = i + index;
+							if (curr >= byAddOrder.size())
+								break;
+
+							StoredBitmap other = byAddOrder.get(curr);
+							EWAHCompressedBitmap bitmap = other.getBitmap()
+									.xor(item.getBitmap());
+
+							if (bitmap.sizeInBytes()
+									< bestBitmap.sizeInBytes()) {
+								bestBitmap = bitmap;
+								bestXorOffset = i;
+							}
+						}
+						index--;
+
+						PositionEntry entry = positionEntries.get(item);
+						if (entry == null)
+							throw new IllegalStateException();
+						return new StoredEntry(entry.namePosition, bestBitmap,
+								bestXorOffset, item.getFlags());
+					}
+
+					public void remove() {
+						throw new UnsupportedOperationException();
+					}
+				};
+			}
+		};
+	}
+
+	/** Data object for the on disk representation of a bitmap entry. */
+	public static final class StoredEntry {
+		private final long objectId;
+		private final EWAHCompressedBitmap bitmap;
+		private final int xorOffset;
+		private final int flags;
+
+		private StoredEntry(long objectId, EWAHCompressedBitmap bitmap,
+				int xorOffset, int flags) {
+			this.objectId = objectId;
+			this.bitmap = bitmap;
+			this.xorOffset = xorOffset;
+			this.flags = flags;
+		}
+
+		/** @return the bitmap */
+		public EWAHCompressedBitmap getBitmap() {
+			return bitmap;
+		}
+
+		/** @return the xorOffset */
+		public int getXorOffset() {
+			return xorOffset;
+		}
+
+		/** @return the flags */
+		public int getFlags() {
+			return flags;
+		}
+
+		/** @return the ObjectId */
+		public long getObjectId() {
+			return objectId;
+		}
+	}
+
+	private static final class PositionEntry extends ObjectIdOwnerMap.Entry {
+		private final int namePosition;
+
+		private int offsetPosition;
+
+		private PositionEntry(AnyObjectId objectId, int namePosition) {
+			super(objectId);
+			this.namePosition = namePosition;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
new file mode 100644
index 0000000..d3e1990
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2013, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javaewah.EWAHCompressedBitmap;
+import javaewah.IntIterator;
+
+import org.eclipse.jgit.internal.storage.file.BasePackBitmapIndex.StoredBitmap;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+
+/**
+ * A PackBitmapIndex that remaps the bitmaps in the previous index to the
+ * positions in the new pack index. Note, unlike typical PackBitmapIndex
+ * implementations this implementation is not thread safe, as it is intended to
+ * be used with a PackBitmapIndexBuilder, which is also not thread safe.
+ */
+public class PackBitmapIndexRemapper extends PackBitmapIndex
+		implements Iterable<PackBitmapIndexRemapper.Entry> {
+
+	private final BasePackBitmapIndex oldPackIndex;
+	private final PackBitmapIndex newPackIndex;
+	private final ObjectIdOwnerMap<StoredBitmap> convertedBitmaps;
+	private final BitSet inflated;
+	private final int[] prevToNewMapping;
+
+	/**
+	 * A PackBitmapIndex that maps the positions in the prevBitmapIndex to the
+	 * ones in the newIndex.
+	 *
+	 * @param prevBitmapIndex
+	 *            the bitmap index with the old mapping.
+	 * @param newIndex
+	 *            the bitmap index with the new mapping.
+	 * @return a bitmap index that attempts to do the mapping between the two.
+	 */
+	public static PackBitmapIndexRemapper newPackBitmapIndex(
+			BitmapIndex prevBitmapIndex, PackBitmapIndex newIndex) {
+		if (!(prevBitmapIndex instanceof BitmapIndexImpl))
+			return new PackBitmapIndexRemapper(newIndex);
+
+		PackBitmapIndex prevIndex = ((BitmapIndexImpl) prevBitmapIndex)
+				.getPackBitmapIndex();
+		if (!(prevIndex instanceof BasePackBitmapIndex))
+			return new PackBitmapIndexRemapper(newIndex);
+
+		return new PackBitmapIndexRemapper(
+				(BasePackBitmapIndex) prevIndex, newIndex);
+	}
+
+	private PackBitmapIndexRemapper(PackBitmapIndex newPackIndex) {
+		this.oldPackIndex = null;
+		this.newPackIndex = newPackIndex;
+		this.convertedBitmaps = null;
+		this.inflated = null;
+		this.prevToNewMapping = null;
+	}
+
+	private PackBitmapIndexRemapper(
+			BasePackBitmapIndex oldPackIndex, PackBitmapIndex newPackIndex) {
+		this.oldPackIndex = oldPackIndex;
+		this.newPackIndex = newPackIndex;
+		convertedBitmaps = new ObjectIdOwnerMap<StoredBitmap>();
+		inflated = new BitSet(newPackIndex.getObjectCount());
+
+		prevToNewMapping = new int[oldPackIndex.getObjectCount()];
+		for (int pos = 0; pos < prevToNewMapping.length; pos++)
+			prevToNewMapping[pos] = newPackIndex.findPosition(
+					oldPackIndex.getObject(pos));
+	}
+
+	@Override
+	public int findPosition(AnyObjectId objectId) {
+		return newPackIndex.findPosition(objectId);
+	}
+
+	@Override
+	public ObjectId getObject(int position) throws IllegalArgumentException {
+		return newPackIndex.getObject(position);
+	}
+
+	@Override
+	public int getObjectCount() {
+		return newPackIndex.getObjectCount();
+	}
+
+	@Override
+	public EWAHCompressedBitmap ofObjectType(
+			EWAHCompressedBitmap bitmap, int type) {
+		return newPackIndex.ofObjectType(bitmap, type);
+	}
+
+	public Iterator<Entry> iterator() {
+		if (oldPackIndex == null)
+			return Collections.<Entry> emptyList().iterator();
+
+		final Iterator<StoredBitmap> it = oldPackIndex.getBitmaps().iterator();
+		return new Iterator<Entry>() {
+			private Entry entry;
+
+			public boolean hasNext() {
+				while (entry == null && it.hasNext()) {
+					StoredBitmap sb = it.next();
+					if (newPackIndex.findPosition(sb) != -1)
+						entry = new Entry(sb, sb.getFlags());
+				}
+				return entry != null;
+			}
+
+			public Entry next() {
+				if (!hasNext())
+					throw new NoSuchElementException();
+
+				Entry res = entry;
+				entry = null;
+				return res;
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	@Override
+	public EWAHCompressedBitmap getBitmap(AnyObjectId objectId) {
+		EWAHCompressedBitmap bitmap = newPackIndex.getBitmap(objectId);
+		if (bitmap != null || oldPackIndex == null)
+			return bitmap;
+
+		StoredBitmap stored = convertedBitmaps.get(objectId);
+		if (stored != null)
+			return stored.getBitmap();
+
+		StoredBitmap oldBitmap = oldPackIndex.getBitmaps().get(objectId);
+		if (oldBitmap == null)
+			return null;
+
+		if (newPackIndex.findPosition(objectId) == -1)
+			return null;
+
+		inflated.clear();
+		for (IntIterator i = oldBitmap.getBitmap().intIterator(); i.hasNext();)
+			inflated.set(prevToNewMapping[i.next()]);
+		bitmap = inflated.toEWAHCompressedBitmap();
+		convertedBitmaps.add(
+				new StoredBitmap(objectId, bitmap, null, oldBitmap.getFlags()));
+		return bitmap;
+	}
+
+	/** An entry in the old PackBitmapIndex. */
+	public final class Entry extends ObjectId {
+		private final int flags;
+
+		private Entry(AnyObjectId src, int flags) {
+			super(src);
+			this.flags = flags;
+		}
+
+		/** @return the flags associated with the bitmap. */
+		public int getFlags() {
+			return flags;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
new file mode 100644
index 0000000..1d927d3
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.Arrays;
+
+import javaewah.EWAHCompressedBitmap;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Support for the pack bitmap index v1 format, which contains experimental
+ * support for bitmaps.
+ *
+ * @see PackBitmapIndex
+ */
+class PackBitmapIndexV1 extends BasePackBitmapIndex {
+	static final byte[] MAGIC = { 'B', 'I', 'T', 'M' };
+	static final int OPT_FULL = 1;
+
+	private static final int MAX_XOR_OFFSET = 126;
+
+	private final PackIndex packIndex;
+	private final PackReverseIndex reverseIndex;
+	private final EWAHCompressedBitmap commits;
+	private final EWAHCompressedBitmap trees;
+	private final EWAHCompressedBitmap blobs;
+	private final EWAHCompressedBitmap tags;
+
+	private final ObjectIdOwnerMap<StoredBitmap> bitmaps;
+
+	PackBitmapIndexV1(final InputStream fd, PackIndex packIndex,
+			PackReverseIndex reverseIndex) throws IOException {
+		super(new ObjectIdOwnerMap<StoredBitmap>());
+		this.packIndex = packIndex;
+		this.reverseIndex = reverseIndex;
+		this.bitmaps = getBitmaps();
+
+		final byte[] scratch = new byte[32];
+		IO.readFully(fd, scratch, 0, scratch.length);
+
+		// Check the magic bytes
+		for (int i = 0; i < MAGIC.length; i++) {
+			if (scratch[i] != MAGIC[i]) {
+				byte[] actual = new byte[MAGIC.length];
+				System.arraycopy(scratch, 0, actual, 0, MAGIC.length);
+				throw new IOException(MessageFormat.format(
+						JGitText.get().expectedGot, Arrays.toString(MAGIC),
+						Arrays.toString(actual)));
+			}
+		}
+
+		// Read the version (2 bytes)
+		final int version = NB.decodeUInt16(scratch, 4);
+		if (version != 1)
+			throw new IOException(MessageFormat.format(
+					JGitText.get().unsupportedPackIndexVersion,
+					Integer.valueOf(version)));
+
+		// Read the options (2 bytes)
+		final int opts = NB.decodeUInt16(scratch, 6);
+		switch (opts) {
+		case OPT_FULL:
+			// Bitmaps are self contained within this file.
+			break;
+		default:
+			throw new IOException(MessageFormat.format(
+					JGitText.get().expectedGot, Integer.valueOf(OPT_FULL),
+					Integer.valueOf(opts)));
+		}
+
+		// Read the number of entries (1 int32)
+		long numEntries = NB.decodeUInt32(scratch, 8);
+		if (numEntries > Integer.MAX_VALUE)
+			throw new IOException(JGitText.get().indexFileIsTooLargeForJgit);
+
+		// Checksum applied on the bottom of the corresponding pack file.
+		this.packChecksum = new byte[20];
+		System.arraycopy(scratch, 12, packChecksum, 0, packChecksum.length);
+
+		// Read the bitmaps for the Git types
+		SimpleDataInput dataInput = new SimpleDataInput(fd);
+		this.commits = readBitmap(dataInput);
+		this.trees = readBitmap(dataInput);
+		this.blobs = readBitmap(dataInput);
+		this.tags = readBitmap(dataInput);
+
+		// An entry is object id, xor offset, flag byte, and a length encoded
+		// bitmap. The object id is an int32 of the nth position sorted by name.
+		// The xor offset is a single byte offset back in the list of entries.
+		StoredBitmap[] recentBitmaps = new StoredBitmap[MAX_XOR_OFFSET];
+		for (int i = 0; i < (int) numEntries; i++) {
+			IO.readFully(fd, scratch, 0, 6);
+			int nthObjectId = NB.decodeInt32(scratch, 0);
+			int xorOffset = scratch[4];
+			int flags = scratch[5];
+			EWAHCompressedBitmap bitmap = readBitmap(dataInput);
+
+			if (nthObjectId < 0)
+				throw new IOException(MessageFormat.format(
+						JGitText.get().invalidId, String.valueOf(nthObjectId)));
+			if (xorOffset < 0)
+				throw new IOException(MessageFormat.format(
+						JGitText.get().invalidId, String.valueOf(xorOffset)));
+			if (xorOffset > MAX_XOR_OFFSET)
+				throw new IOException(MessageFormat.format(
+						JGitText.get().expectedLessThanGot,
+						String.valueOf(MAX_XOR_OFFSET),
+						String.valueOf(xorOffset)));
+			if (xorOffset > i)
+				throw new IOException(MessageFormat.format(
+						JGitText.get().expectedLessThanGot, String.valueOf(i),
+						String.valueOf(xorOffset)));
+
+			ObjectId objectId = packIndex.getObjectId(nthObjectId);
+			StoredBitmap xorBitmap = null;
+			if (xorOffset > 0) {
+				int index = (i - xorOffset);
+				xorBitmap = recentBitmaps[index % recentBitmaps.length];
+				if (xorBitmap == null)
+					throw new IOException(MessageFormat.format(
+							JGitText.get().invalidId,
+							String.valueOf(xorOffset)));
+			}
+
+			StoredBitmap sb = new StoredBitmap(
+					objectId, bitmap, xorBitmap, flags);
+			bitmaps.add(sb);
+			recentBitmaps[i % recentBitmaps.length] = sb;
+		}
+	}
+
+	@Override
+	public int findPosition(AnyObjectId objectId) {
+		long offset = packIndex.findOffset(objectId);
+		if (offset == -1)
+			return -1;
+		return reverseIndex.findPostion(offset);
+	}
+
+	@Override
+	public ObjectId getObject(int position) throws IllegalArgumentException {
+		ObjectId objectId = reverseIndex.findObjectByPosition(position);
+		if (objectId == null)
+			throw new IllegalArgumentException();
+		return objectId;
+	}
+
+	@Override
+	public int getObjectCount() {
+		return (int) packIndex.getObjectCount();
+	}
+
+	@Override
+	public EWAHCompressedBitmap ofObjectType(
+			EWAHCompressedBitmap bitmap, int type) {
+		switch (type) {
+		case Constants.OBJ_BLOB:
+			return blobs.and(bitmap);
+		case Constants.OBJ_TREE:
+			return trees.and(bitmap);
+		case Constants.OBJ_COMMIT:
+			return commits.and(bitmap);
+		case Constants.OBJ_TAG:
+			return tags.and(bitmap);
+		}
+		throw new IllegalArgumentException();
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		// TODO(cranger): compare the pack checksum?
+		if (o instanceof PackBitmapIndexV1)
+			return getPackIndex() == ((PackBitmapIndexV1) o).getPackIndex();
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		return getPackIndex().hashCode();
+	}
+
+	PackIndex getPackIndex() {
+		return packIndex;
+	}
+
+	private static EWAHCompressedBitmap readBitmap(DataInput dataInput)
+			throws IOException {
+		EWAHCompressedBitmap bitmap = new EWAHCompressedBitmap();
+		bitmap.deserialize(dataInput);
+		return bitmap;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java
new file mode 100644
index 0000000..a9a742a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexWriterV1.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.BufferedOutputStream;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.text.MessageFormat;
+
+import javaewah.EWAHCompressedBitmap;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder.StoredEntry;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
+
+/**
+ * Creates the version 1 pack bitmap index files.
+ *
+ * @see PackBitmapIndexV1
+ */
+public class PackBitmapIndexWriterV1 {
+	private final DigestOutputStream out;
+	private final DataOutput dataOutput;
+
+	/**
+	 * Creates the version 1 pack bitmap index files.
+	 *
+	 * @param dst
+	 *            the output stream to which the index will be written.
+	 */
+	public PackBitmapIndexWriterV1(final OutputStream dst) {
+		out = new DigestOutputStream(dst instanceof BufferedOutputStream ? dst
+				: new SafeBufferedOutputStream(dst),
+				Constants.newMessageDigest());
+		dataOutput = new SimpleDataOutput(out);
+	}
+
+	/**
+	 * Write all object entries to the index stream.
+	 * <p>
+	 * After writing the stream passed to the factory is flushed but remains
+	 * open. Callers are always responsible for closing the output stream.
+	 *
+	 * @param bitmaps
+	 *            the index data for the bitmaps
+	 * @param packDataChecksum
+	 *            checksum signature of the entire pack data content. This is
+	 *            traditionally the last 20 bytes of the pack file's own stream.
+	 * @throws IOException
+	 *             an error occurred while writing to the output stream, or this
+	 *             index format cannot store the object data supplied.
+	 */
+	public void write(PackBitmapIndexBuilder bitmaps, byte[] packDataChecksum)
+			throws IOException {
+		if (bitmaps == null || packDataChecksum.length != 20)
+			throw new IllegalStateException();
+
+		writeHeader(bitmaps.getOptions(), bitmaps.getBitmapCount(),
+				packDataChecksum);
+		writeBody(bitmaps);
+		writeFooter();
+
+		out.flush();
+	}
+
+	private void writeHeader(
+			int options, int bitmapCount, byte[] packDataChecksum)
+			throws IOException {
+		out.write(PackBitmapIndexV1.MAGIC);
+		dataOutput.writeShort(1);
+		dataOutput.writeShort(options);
+		dataOutput.writeInt(bitmapCount);
+		out.write(packDataChecksum);
+	}
+
+	private void writeBody(PackBitmapIndexBuilder bitmaps) throws IOException {
+		writeBitmap(bitmaps.getCommits());
+		writeBitmap(bitmaps.getTrees());
+		writeBitmap(bitmaps.getBlobs());
+		writeBitmap(bitmaps.getTags());
+		writeBitmaps(bitmaps);
+	}
+
+	private void writeBitmap(EWAHCompressedBitmap bitmap) throws IOException {
+		bitmap.serialize(dataOutput);
+	}
+
+	private void writeBitmaps(PackBitmapIndexBuilder bitmaps)
+			throws IOException {
+		int bitmapCount = 0;
+		for (StoredEntry entry : bitmaps.getCompressedBitmaps()) {
+			writeBitmapEntry(entry);
+			bitmapCount++;
+		}
+
+		int expectedBitmapCount = bitmaps.getBitmapCount();
+		if (expectedBitmapCount != bitmapCount)
+			throw new IOException(MessageFormat.format(
+					JGitText.get().expectedGot,
+					String.valueOf(expectedBitmapCount),
+					String.valueOf(bitmapCount)));
+	}
+
+	private void writeBitmapEntry(StoredEntry entry) throws IOException {
+		// Write object, XOR offset, and bitmap
+		dataOutput.writeInt((int) entry.getObjectId());
+		out.write(entry.getXorOffset());
+		out.write(entry.getFlags());
+		writeBitmap(entry.getBitmap());
+	}
+
+	private void writeFooter() throws IOException {
+		out.on(false);
+		out.write(out.getMessageDigest().digest());
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
similarity index 95%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index 666df58..b52d3f7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -43,9 +43,10 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
-import static org.eclipse.jgit.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
 
 import java.io.EOFException;
 import java.io.File;
@@ -69,15 +70,15 @@ import org.eclipse.jgit.errors.PackInvalidException;
 import org.eclipse.jgit.errors.PackMismatchException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.storage.pack.BinaryDelta;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackExt;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
 import org.eclipse.jgit.util.LongList;
 import org.eclipse.jgit.util.NB;
 import org.eclipse.jgit.util.RawParseUtils;
@@ -97,6 +98,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
 
 	private final File packFile;
 
+	private final int extensions;
+
 	private File keepFile;
 
 	private volatile String packName;
@@ -124,6 +127,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
 
 	private PackReverseIndex reverseIdx;
 
+	private PackBitmapIndex bitmapIdx;
+
 	/**
 	 * Objects we have tried to read, and discovered to be corrupt.
 	 * <p>
@@ -138,10 +143,13 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
 	 *
 	 * @param packFile
 	 *            path of the <code>.pack</code> file holding the data.
+	 * @param extensions
+	 *            additional pack file extensions with the same base as the pack
 	 */
-	public PackFile(final File packFile) {
+	public PackFile(final File packFile, int extensions) {
 		this.packFile = packFile;
 		this.packLastModified = (int) (packFile.lastModified() >> 10);
+		this.extensions = extensions;
 
 		// Multiply by 31 here so we can more directly combine with another
 		// value in WindowCache.hash(), without doing the multiply there.
@@ -1048,6 +1056,24 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
 		return getReverseIdx().findNextOffset(startOffset, maxOffset);
 	}
 
+	synchronized PackBitmapIndex getBitmapIndex() throws IOException {
+		if (invalid)
+			return null;
+		if (bitmapIdx == null && hasExt(BITMAP_INDEX)) {
+			final PackBitmapIndex idx = PackBitmapIndex.open(
+					extFile(BITMAP_INDEX), idx(), getReverseIdx());
+
+			if (packChecksum == null)
+				packChecksum = idx.packChecksum;
+			else if (!Arrays.equals(packChecksum, idx.packChecksum))
+				throw new PackMismatchException(
+						JGitText.get().packChecksumMismatch);
+
+			bitmapIdx = idx;
+		}
+		return bitmapIdx;
+	}
+
 	private synchronized PackReverseIndex getReverseIdx() throws IOException {
 		if (reverseIdx == null)
 			reverseIdx = new PackReverseIndex(idx());
@@ -1085,4 +1111,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
 		String b = (dot < 0) ? p : p.substring(0, dot);
 		return new File(packFile.getParentFile(), b + '.' + ext.getExtension());
 	}
+
+	private boolean hasExt(PackExt ext) {
+		return (extensions & ext.getBit()) != 0;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
index ef987fa..44baeb1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.FileInputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index 4071fb8..8c381fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -44,7 +44,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
index 29f264b..9b1a5f3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
similarity index 88%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
index b78c5ae..6dfe74b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.BufferedOutputStream;
 import java.io.IOException;
@@ -94,24 +94,44 @@ public abstract class PackIndexWriter {
 	 * @throws IllegalArgumentException
 	 *             no recognized pack index version can support the supplied
 	 *             objects. This is likely a bug in the implementation.
+	 * @see #oldestPossibleFormat(List)
 	 */
-	@SuppressWarnings("fallthrough")
 	public static PackIndexWriter createOldestPossible(final OutputStream dst,
 			final List<? extends PackedObjectInfo> objs) {
-		int version = 1;
-		LOOP: for (final PackedObjectInfo oe : objs) {
-			switch (version) {
-			case 1:
-				if (PackIndexWriterV1.canStore(oe))
-					continue;
-				version = 2;
-			case 2:
-				break LOOP;
-			}
+		return createVersion(dst, oldestPossibleFormat(objs));
+	}
+
+	/**
+	 * Return the oldest (most widely understood) index format.
+	 * <p>
+	 * This method selects an index format that can accurate describe the
+	 * supplied objects and that will be the most compatible format with older
+	 * Git implementations.
+	 * <p>
+	 * Index version 1 is widely recognized by all Git implementations, but
+	 * index version 2 (and later) is not as well recognized as it was
+	 * introduced more than a year later. Index version 1 can only be used if
+	 * the resulting pack file is under 4 gigabytes in size; packs larger than
+	 * that limit must use index version 2.
+	 *
+	 * @param objs
+	 *            the objects the caller needs to store in the index. Entries
+	 *            will be examined until a format can be conclusively selected.
+	 * @return the index format.
+	 * @throws IllegalArgumentException
+	 *             no recognized pack index version can support the supplied
+	 *             objects. This is likely a bug in the implementation.
+	 */
+	public static int oldestPossibleFormat(
+			final List<? extends PackedObjectInfo> objs) {
+		for (final PackedObjectInfo oe : objs) {
+			if (!PackIndexWriterV1.canStore(oe))
+				return 2;
 		}
-		return createVersion(dst, version);
+		return 1;
 	}
 
+
 	/**
 	 * Create a new writer instance for a specific index format version.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
index e0458cd..6017b99 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
similarity index 98%
copy from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
index 7671cd6..770549d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
index 5425eed..9cb8349 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java
index f98618a..b671b03 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackLock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackLock.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
similarity index 89%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
index 990106b..c5aa5d3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
@@ -41,15 +41,15 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.text.MessageFormat;
 import java.util.Arrays;
 
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.file.PackIndex.MutableEntry;
 
 /**
  * <p>
@@ -108,7 +108,7 @@ public class PackReverseIndex {
 		int i64 = 0;
 		for (final MutableEntry me : index) {
 			final long o = me.getOffset();
-			if (o < Integer.MAX_VALUE)
+			if (o <= Integer.MAX_VALUE)
 				offsets32[i32++] = (int) o;
 			else
 				offsets64[i64++] = o;
@@ -120,7 +120,7 @@ public class PackReverseIndex {
 		int nth = 0;
 		for (final MutableEntry me : index) {
 			final long o = me.getOffset();
-			if (o < Integer.MAX_VALUE)
+			if (o <= Integer.MAX_VALUE)
 				nth32[Arrays.binarySearch(offsets32, (int) o)] = nth++;
 			else
 				nth64[Arrays.binarySearch(offsets64, o)] = nth++;
@@ -193,4 +193,25 @@ public class PackReverseIndex {
 			return offsets64[i64 + 1];
 		}
 	}
+
+	int findPostion(long offset) {
+		if (offset <= Integer.MAX_VALUE) {
+			final int i32 = Arrays.binarySearch(offsets32, (int) offset);
+			if (i32 < 0)
+				return -1;
+			return i32;
+		} else {
+			final int i64 = Arrays.binarySearch(offsets64, offset);
+			if (i64 < 0)
+				return -1;
+			return nth32.length + i64;
+		}
+	}
+
+	ObjectId findObjectByPosition(int nthPosition) {
+		if (nthPosition < nth32.length)
+			return index.getObjectId(nth32[nthPosition]);
+		final int i64 = nthPosition - nth32.length;
+		return index.getObjectId(nth64[i64]);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index e0ce909..c720bc5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -44,7 +44,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.lib.Constants.CHARSET;
 import static org.eclipse.jgit.lib.Constants.HEAD;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
index 60ee2b1..878fc19 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
index f336ea0..8ad7ad2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.lib.Constants.encode;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
similarity index 82%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogEntry.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
index e995dcd..60f04b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
@@ -42,20 +42,22 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.Serializable;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.CheckoutEntry;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.util.RawParseUtils;
 
 /**
  * Parsed reflog entry
  */
-public class ReflogEntry implements Serializable {
+public class ReflogEntryImpl implements Serializable, ReflogEntry {
 	private static final long serialVersionUID = 1L;
 
 	private ObjectId oldId;
@@ -66,7 +68,7 @@ public class ReflogEntry implements Serializable {
 
 	private String comment;
 
-	ReflogEntry(byte[] raw, int pos) {
+	ReflogEntryImpl(byte[] raw, int pos) {
 		oldId = ObjectId.fromString(raw, pos);
 		pos += Constants.OBJECT_ID_STRING_LENGTH;
 		if (raw[pos++] != ' ')
@@ -88,29 +90,29 @@ public class ReflogEntry implements Serializable {
 		}
 	}
 
-	/**
-	 * @return the commit id before the change
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getOldId()
 	 */
 	public ObjectId getOldId() {
 		return oldId;
 	}
 
-	/**
-	 * @return the commit id after the change
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getNewId()
 	 */
 	public ObjectId getNewId() {
 		return newId;
 	}
 
-	/**
-	 * @return user performing the change
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getWho()
 	 */
 	public PersonIdent getWho() {
 		return who;
 	}
 
-	/**
-	 * @return textual description of the change
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getComment()
 	 */
 	public String getComment() {
 		return comment;
@@ -123,13 +125,12 @@ public class ReflogEntry implements Serializable {
 				+ ", " + getComment() + "]";
 	}
 
-	/**
-	 * @return a {@link CheckoutEntry} with parsed information about a branch
-	 *         switch, or null if the entry is not a checkout
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#parseCheckout()
 	 */
 	public CheckoutEntry parseCheckout() {
-		if (getComment().startsWith(CheckoutEntry.CHECKOUT_MOVING_FROM))
-			return new CheckoutEntry(this);
+		if (getComment().startsWith(CheckoutEntryImpl.CHECKOUT_MOVING_FROM))
+			return new CheckoutEntryImpl(this);
 		else
 			return null;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
similarity index 81%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index c60f9e3..dadc631 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -1,5 +1,4 @@
 /*
- * Copyright (C) 2009, Robin Rosenberg
  * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
@@ -42,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -52,6 +51,8 @@ import java.util.Collections;
 import java.util.List;
 
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.RawParseUtils;
@@ -59,42 +60,33 @@ import org.eclipse.jgit.util.RawParseUtils;
 /**
  * Utility for reading reflog entries
  */
-public class ReflogReader {
+class ReflogReaderImpl implements ReflogReader {
 	private File logName;
 
 	/**
 	 * @param db
 	 * @param refname
 	 */
-	public ReflogReader(Repository db, String refname) {
+	ReflogReaderImpl(Repository db, String refname) {
 		logName = new File(db.getDirectory(), Constants.LOGS + '/' + refname);
 	}
 
-	/**
-	 * Get the last entry in the reflog
-	 *
-	 * @return the latest reflog entry, or null if no log
-	 * @throws IOException
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getLastEntry()
 	 */
 	public ReflogEntry getLastEntry() throws IOException {
 		return getReverseEntry(0);
 	}
 
-	/**
-	 * @return all reflog entries in reverse order
-	 * @throws IOException
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getReverseEntries()
 	 */
 	public List<ReflogEntry> getReverseEntries() throws IOException {
 		return getReverseEntries(Integer.MAX_VALUE);
 	}
 
-	/**
-	 * Get specific entry in the reflog relative to the last entry which is
-	 * considered entry zero.
-	 *
-	 * @param number
-	 * @return reflog entry or null if not found
-	 * @throws IOException
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getReverseEntry(int)
 	 */
 	public ReflogEntry getReverseEntry(int number) throws IOException {
 		if (number < 0)
@@ -112,17 +104,14 @@ public class ReflogReader {
 		while (rs >= 0) {
 			rs = RawParseUtils.prevLF(log, rs);
 			if (number == current)
-				return new ReflogEntry(log, rs < 0 ? 0 : rs + 2);
+				return new ReflogEntryImpl(log, rs < 0 ? 0 : rs + 2);
 			current++;
 		}
 		return null;
 	}
 
-	/**
-	 * @param max
-	 *            max number of entries to read
-	 * @return all reflog entries in reverse order
-	 * @throws IOException
+	/* (non-Javadoc)
+	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getReverseEntries(int)
 	 */
 	public List<ReflogEntry> getReverseEntries(int max) throws IOException {
 		final byte[] log;
@@ -136,7 +125,7 @@ public class ReflogReader {
 		List<ReflogEntry> ret = new ArrayList<ReflogEntry>();
 		while (rs >= 0 && max-- > 0) {
 			rs = RawParseUtils.prevLF(log, rs);
-			ReflogEntry entry = new ReflogEntry(log, rs < 0 ? 0 : rs + 2);
+			ReflogEntry entry = new ReflogEntryImpl(log, rs < 0 ? 0 : rs + 2);
 			ret.add(entry);
 		}
 		return ret;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogWriter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
index d7788cd..9ec3507 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ReflogWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
@@ -43,7 +43,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.lib.Constants.HEAD;
 import static org.eclipse.jgit.lib.Constants.LOGS;
@@ -67,6 +67,7 @@ import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataInput.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataInput.java
new file mode 100644
index 0000000..9cd9e80
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataInput.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * An implementation of DataInput that only handles readInt() and readLong()
+ * using the Git conversion utilities for network byte order handling. This is
+ * needed to read {@link javaewah.EWAHCompressedBitmap}s.
+ */
+class SimpleDataInput implements DataInput {
+	private final InputStream fd;
+
+	private final byte[] buf = new byte[8];
+
+	SimpleDataInput(InputStream fd) {
+		this.fd = fd;
+	}
+
+	public int readInt() throws IOException {
+		readFully(buf, 0, 4);
+		return NB.decodeInt32(buf, 0);
+	}
+
+	public long readLong() throws IOException {
+		readFully(buf, 0, 8);
+		return NB.decodeInt64(buf, 0);
+	}
+
+	public long readUnsignedInt() throws IOException {
+		readFully(buf, 0, 4);
+		return NB.decodeUInt32(buf, 0);
+	}
+
+	public void readFully(byte[] b) throws IOException {
+		readFully(b, 0, b.length);
+	}
+
+	public void readFully(byte[] b, int off, int len) throws IOException {
+		IO.readFully(fd, b, off, len);
+	}
+
+	public int skipBytes(int n) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean readBoolean() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public byte readByte() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public int readUnsignedByte() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public short readShort() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public int readUnsignedShort() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public char readChar() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public float readFloat() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public double readDouble() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public String readLine() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public String readUTF() throws IOException {
+		throw new UnsupportedOperationException();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java
similarity index 50%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java
index 7671cd6..1bcb62b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2012, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,70 +41,85 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
+import java.io.DataOutput;
 import java.io.IOException;
 import java.io.OutputStream;
 
-import org.eclipse.jgit.transport.PackedObjectInfo;
 import org.eclipse.jgit.util.NB;
 
 /**
- * Creates the version 2 pack table of contents files.
- *
- * @see PackIndexWriter
- * @see PackIndexV2
+ * An implementation of {@link DataOutput} that only handles
+ * {@link #writeInt(int)} and {@link #writeLong(long)} using the Git conversion
+ * utilities for network byte order handling. This is needed to write
+ * {@link javaewah.EWAHCompressedBitmap}s.
  */
-class PackIndexWriterV2 extends PackIndexWriter {
-	private static final int MAX_OFFSET_32 = 0x7fffffff;
-	private static final int IS_OFFSET_64 = 0x80000000;
+class SimpleDataOutput implements DataOutput {
+	private final OutputStream fd;
+
+	private final byte[] buf = new byte[8];
+
+	SimpleDataOutput(OutputStream fd) {
+		this.fd = fd;
+	}
+
+	public void writeShort(int v) throws IOException {
+		NB.encodeInt16(buf, 0, v);
+		fd.write(buf, 0, 2);
+	}
+
+	public void writeInt(int v) throws IOException {
+		NB.encodeInt32(buf, 0, v);
+		fd.write(buf, 0, 4);
+	}
+
+	public void writeLong(long v) throws IOException {
+		NB.encodeInt64(buf, 0, v);
+		fd.write(buf, 0, 8);
+	}
+
+	public void write(int b) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public void write(byte[] b) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public void write(byte[] b, int off, int len) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public void writeBoolean(boolean v) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public void writeByte(int v) throws IOException {
+		throw new UnsupportedOperationException();
+	}
 
-	PackIndexWriterV2(final OutputStream dst) {
-		super(dst);
+	public void writeChar(int v) throws IOException {
+		throw new UnsupportedOperationException();
 	}
 
-	@Override
-	protected void writeImpl() throws IOException {
-		writeTOC(2);
-		writeFanOutTable();
-		writeObjectNames();
-		writeCRCs();
-		writeOffset32();
-		writeOffset64();
-		writeChecksumFooter();
+	public void writeFloat(float v) throws IOException {
+		throw new UnsupportedOperationException();
 	}
 
-	private void writeObjectNames() throws IOException {
-		for (final PackedObjectInfo oe : entries)
-			oe.copyRawTo(out);
+	public void writeDouble(double v) throws IOException {
+		throw new UnsupportedOperationException();
 	}
 
-	private void writeCRCs() throws IOException {
-		for (final PackedObjectInfo oe : entries) {
-			NB.encodeInt32(tmp, 0, oe.getCRC());
-			out.write(tmp, 0, 4);
-		}
+	public void writeBytes(String s) throws IOException {
+		throw new UnsupportedOperationException();
 	}
 
-	private void writeOffset32() throws IOException {
-		int o64 = 0;
-		for (final PackedObjectInfo oe : entries) {
-			final long o = oe.getOffset();
-			if (o <= MAX_OFFSET_32)
-				NB.encodeInt32(tmp, 0, (int) o);
-			else
-				NB.encodeInt32(tmp, 0, IS_OFFSET_64 | o64++);
-			out.write(tmp, 0, 4);
-		}
+	public void writeChars(String s) throws IOException {
+		throw new UnsupportedOperationException();
 	}
 
-	private void writeOffset64() throws IOException {
-		for (final PackedObjectInfo oe : entries) {
-			final long o = oe.getOffset();
-			if (MAX_OFFSET_32 < o) {
-				NB.encodeInt64(tmp, 0, o);
-				out.write(tmp, 0, 8);
-			}
-		}
+	public void writeUTF(String s) throws IOException {
+		throw new UnsupportedOperationException();
 	}
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
index d059869..cb95a76 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
index 4d05c6f..ce67ae0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.util.concurrent.atomic.AtomicReferenceArray;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
similarity index 94%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index 04c42ca..e1b7606 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.lang.ref.ReferenceQueue;
@@ -54,6 +54,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.storage.file.WindowCacheConfig;
 
 /**
  * Caches slices of a {@link PackFile} in memory for faster read access.
@@ -146,40 +147,14 @@ public class WindowCache {
 	 * smaller than what what is currently cached, older entries will be purged
 	 * as soon as possible to allow the cache to meet the new limit.
 	 *
-	 * @param packedGitLimit
-	 *            maximum number of bytes to hold within this instance.
-	 * @param packedGitWindowSize
-	 *            number of bytes per window within the cache.
-	 * @param packedGitMMAP
-	 *            true to enable use of mmap when creating windows.
-	 * @param deltaBaseCacheLimit
-	 *            number of bytes to hold in the delta base cache.
-	 * @deprecated Use {@link WindowCacheConfig} instead.
-	 */
-	public static void reconfigure(final int packedGitLimit,
-			final int packedGitWindowSize, final boolean packedGitMMAP,
-			final int deltaBaseCacheLimit) {
-		final WindowCacheConfig c = new WindowCacheConfig();
-		c.setPackedGitLimit(packedGitLimit);
-		c.setPackedGitWindowSize(packedGitWindowSize);
-		c.setPackedGitMMAP(packedGitMMAP);
-		c.setDeltaBaseCacheLimit(deltaBaseCacheLimit);
-		reconfigure(c);
-	}
-
-	/**
-	 * Modify the configuration of the window cache.
-	 * <p>
-	 * The new configuration is applied immediately. If the new limits are
-	 * smaller than what what is currently cached, older entries will be purged
-	 * as soon as possible to allow the cache to meet the new limit.
-	 *
+	 * @deprecated use {@code cfg.install()} to avoid internal reference.
 	 * @param cfg
 	 *            the new window cache configuration.
 	 * @throws IllegalArgumentException
 	 *             the cache configuration contains one or more invalid
 	 *             settings, usually too low of a limit.
 	 */
+	@Deprecated
 	public static void reconfigure(final WindowCacheConfig cfg) {
 		final WindowCache nc = new WindowCache(cfg);
 		final WindowCache oc = cache;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
similarity index 90%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
index 5555a3c..8dcbeb8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import java.io.IOException;
 import java.security.MessageDigest;
@@ -60,20 +60,21 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
+import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.InflaterCache;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.storage.pack.CachedPack;
-import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.PackOutputStream;
-import org.eclipse.jgit.storage.pack.PackWriter;
 
 /** Active handle to a ByteWindow. */
 final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
@@ -104,6 +105,27 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
 	}
 
 	@Override
+	public BitmapIndex getBitmapIndex() throws IOException {
+		for (PackFile pack : db.getPacks()) {
+			PackBitmapIndex index = pack.getBitmapIndex();
+			if (index != null)
+				return new BitmapIndexImpl(index);
+		}
+		return null;
+	}
+
+	public Collection<CachedPack> getCachedPacksAndUpdate(
+			BitmapBuilder needBitmap) throws IOException {
+		for (PackFile pack : db.getPacks()) {
+			PackBitmapIndex index = pack.getBitmapIndex();
+			if (needBitmap.removeAllOrNone(index))
+				return Collections.<CachedPack> singletonList(
+						new LocalCachedPack(Collections.singletonList(pack)));
+		}
+		return Collections.emptyList();
+	}
+
+	@Override
 	public Collection<ObjectId> resolve(AbbreviatedObjectId id)
 			throws IOException {
 		if (id.isComplete())
@@ -148,8 +170,8 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
 		return sz;
 	}
 
-	public LocalObjectToPack newObjectToPack(RevObject obj) {
-		return new LocalObjectToPack(obj);
+	public LocalObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
+		return new LocalObjectToPack(objectId, type);
 	}
 
 	public void selectObjectRepresentation(PackWriter packer,
@@ -174,11 +196,6 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
 			out.writeObject(otp);
 	}
 
-	@SuppressWarnings("unchecked")
-	public Collection<CachedPack> getCachedPacks() throws IOException {
-		return (Collection<CachedPack>) db.getCachedPacks();
-	}
-
 	/**
 	 * Copy bytes from the window to a caller supplied buffer.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java
index 82a2a52..4f79ea9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WriteConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.file;
+package org.eclipse.jgit.internal.storage.file;
 
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Config.SectionParser;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/package-info.java
new file mode 100644
index 0000000..8fa76c7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * File based repository storage.
+ */
+package org.eclipse.jgit.internal.storage.file;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BaseSearch.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BaseSearch.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java
index a113aa2..b6af0b0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BaseSearch.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java
index 9971b79..97092cd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/BinaryDelta.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java
@@ -42,7 +42,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.util.QuotedString;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/CachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPack.java
similarity index 89%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/CachedPack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPack.java
index 7199c5b..6498ea3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/CachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/CachedPack.java
@@ -41,28 +41,13 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.IOException;
-import java.util.Set;
-
-import org.eclipse.jgit.lib.ObjectId;
 
 /** Describes a pack file {@link ObjectReuseAsIs} can append onto a stream. */
 public abstract class CachedPack {
 	/**
-	 * Objects that start this pack.
-	 * <p>
-	 * All objects reachable from the tips are contained within this pack. If
-	 * {@link PackWriter} is going to include everything reachable from all of
-	 * these objects, this cached pack is eligible to be appended directly onto
-	 * the output pack stream.
-	 *
-	 * @return the tip objects that describe this pack.
-	 */
-	public abstract Set<ObjectId> getTips();
-
-	/**
 	 * Get the number of objects in this pack.
 	 *
 	 * @return the total object count for the pack.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java
index 93eab19..91917b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java
@@ -41,11 +41,13 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
 
+import org.eclipse.jgit.storage.pack.PackConfig;
+
 class DeltaCache {
 	private final long size;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java
similarity index 92%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java
index 204030b..cccbc59 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaEncoder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaEncoder.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -134,7 +134,7 @@ public class DeltaEncoder {
 		}
 		buf[p++] = (byte) (((int) sz) & 0x7f);
 		size += p;
-		if (limit <= 0 || size < limit)
+		if (limit == 0 || size < limit)
 			out.write(buf, 0, p);
 	}
 
@@ -189,7 +189,7 @@ public class DeltaEncoder {
 			throws IOException {
 		if (cnt <= 0)
 			return true;
-		if (0 < limit) {
+		if (limit != 0) {
 			int hdrs = cnt / MAX_INSERT_DATA_SIZE;
 			if (cnt % MAX_INSERT_DATA_SIZE != 0)
 				hdrs++;
@@ -236,7 +236,7 @@ public class DeltaEncoder {
 			cnt -= MAX_V2_COPY;
 
 			if (buf.length < p + MAX_COPY_CMD_SIZE) {
-				if (0 < limit && limit < size + p)
+				if (limit != 0 && limit < size + p)
 					return false;
 				out.write(buf, 0, p);
 				size += p;
@@ -245,7 +245,7 @@ public class DeltaEncoder {
 		}
 
 		p = encodeCopy(p, offset, cnt);
-		if (0 < limit && limit < size + p)
+		if (limit != 0 && limit < size + p)
 			return false;
 		out.write(buf, 0, p);
 		size += p;
@@ -255,36 +255,37 @@ public class DeltaEncoder {
 	private int encodeCopy(int p, long offset, int cnt) {
 		int cmd = 0x80;
 		final int cmdPtr = p++; // save room for the command
+		byte b;
 
-		if ((offset & 0xff) != 0) {
+		if ((b = (byte) (offset & 0xff)) != 0) {
 			cmd |= 0x01;
-			buf[p++] = (byte) (offset & 0xff);
+			buf[p++] = b;
 		}
-		if ((offset & (0xff << 8)) != 0) {
+		if ((b = (byte) ((offset >>> 8) & 0xff)) != 0) {
 			cmd |= 0x02;
-			buf[p++] = (byte) ((offset >>> 8) & 0xff);
+			buf[p++] = b;
 		}
-		if ((offset & (0xff << 16)) != 0) {
+		if ((b = (byte) ((offset >>> 16) & 0xff)) != 0) {
 			cmd |= 0x04;
-			buf[p++] = (byte) ((offset >>> 16) & 0xff);
+			buf[p++] = b;
 		}
-		if ((offset & (0xff << 24)) != 0) {
+		if ((b = (byte) ((offset >>> 24) & 0xff)) != 0) {
 			cmd |= 0x08;
-			buf[p++] = (byte) ((offset >>> 24) & 0xff);
+			buf[p++] = b;
 		}
 
 		if (cnt != MAX_V2_COPY) {
-			if ((cnt & 0xff) != 0) {
+			if ((b = (byte) (cnt & 0xff)) != 0) {
 				cmd |= 0x10;
-				buf[p++] = (byte) (cnt & 0xff);
+				buf[p++] = b;
 			}
-			if ((cnt & (0xff << 8)) != 0) {
+			if ((b = (byte) ((cnt >>> 8) & 0xff)) != 0) {
 				cmd |= 0x20;
-				buf[p++] = (byte) ((cnt >>> 8) & 0xff);
+				buf[p++] = b;
 			}
-			if ((cnt & (0xff << 16)) != 0) {
+			if ((b = (byte) ((cnt >>> 16) & 0xff)) != 0) {
 				cmd |= 0x40;
-				buf[p++] = (byte) ((cnt >>> 16) & 0xff);
+				buf[p++] = b;
 			}
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java
index 4f2d5c7..de3c59a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndexScanner.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndexScanner.java
index d30690d..7e10878 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaIndexScanner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndexScanner.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 /**
  * Supports {@link DeltaIndex} by performing a partial scan of the content.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaStream.java
similarity index 99%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaStream.java
index 7275729..28b4d60 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaStream.java
@@ -43,7 +43,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.EOFException;
 import java.io.IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
new file mode 100644
index 0000000..c4b0194
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.pack;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
+import org.eclipse.jgit.storage.pack.PackConfig;
+
+final class DeltaTask implements Callable<Object> {
+	static final long MAX_METER = 9 << 20;
+
+	static final class Block {
+		private static final int MIN_TOP_PATH = 50 << 20;
+
+		final List<DeltaTask> tasks;
+		final int threads;
+		final PackConfig config;
+		final ObjectReader templateReader;
+		final DeltaCache dc;
+		final ThreadSafeProgressMonitor pm;
+		final ObjectToPack[] list;
+		final int beginIndex;
+		final int endIndex;
+
+		private long totalWeight;
+		private long bytesPerUnit;
+
+		Block(int threads, PackConfig config, ObjectReader reader,
+				DeltaCache dc, ThreadSafeProgressMonitor pm,
+				ObjectToPack[] list, int begin, int end) {
+			this.tasks = new ArrayList<DeltaTask>(threads);
+			this.threads = threads;
+			this.config = config;
+			this.templateReader = reader;
+			this.dc = dc;
+			this.pm = pm;
+			this.list = list;
+			this.beginIndex = begin;
+			this.endIndex = end;
+		}
+
+		int cost() {
+			int d = (int) (totalWeight / bytesPerUnit);
+			if (totalWeight % bytesPerUnit != 0)
+				d++;
+			return d;
+		}
+
+		synchronized DeltaWindow stealWork(DeltaTask forThread) {
+			for (;;) {
+				DeltaTask maxTask = null;
+				Slice maxSlice = null;
+				int maxWork = 0;
+
+				for (DeltaTask task : tasks) {
+					Slice s = task.remaining();
+					if (s != null && maxWork < s.size()) {
+						maxTask = task;
+						maxSlice = s;
+						maxWork = s.size();
+					}
+				}
+				if (maxTask == null)
+					return null;
+				if (maxTask.tryStealWork(maxSlice))
+					return forThread.initWindow(maxSlice);
+			}
+		}
+
+		void partitionTasks() {
+			ArrayList<WeightedPath> topPaths = computeTopPaths();
+			Iterator<WeightedPath> topPathItr = topPaths.iterator();
+			int nextTop = 0;
+			long weightPerThread = totalWeight / threads;
+			for (int i = beginIndex; i < endIndex;) {
+				DeltaTask task = new DeltaTask(this);
+				long w = 0;
+
+				// Assign the thread one top path.
+				if (topPathItr.hasNext()) {
+					WeightedPath p = topPathItr.next();
+					w += p.weight;
+					task.add(p.slice);
+				}
+
+				// Assign the task thread ~average weight.
+				int s = i;
+				for (; w < weightPerThread && i < endIndex;) {
+					if (nextTop < topPaths.size()
+							&& i == topPaths.get(nextTop).slice.beginIndex) {
+						if (s < i)
+							task.add(new Slice(s, i));
+						s = i = topPaths.get(nextTop++).slice.endIndex;
+					} else
+						w += list[i++].getWeight();
+				}
+
+				// Round up the slice to the end of a path.
+				if (s < i) {
+					int h = list[i - 1].getPathHash();
+					while (i < endIndex) {
+						if (h == list[i].getPathHash())
+							i++;
+						else
+							break;
+					}
+					task.add(new Slice(s, i));
+				}
+				if (!task.slices.isEmpty())
+					tasks.add(task);
+			}
+			while (topPathItr.hasNext()) {
+				WeightedPath p = topPathItr.next();
+				DeltaTask task = new DeltaTask(this);
+				task.add(p.slice);
+				tasks.add(task);
+			}
+
+			topPaths = null;
+		}
+
+		private ArrayList<WeightedPath> computeTopPaths() {
+			ArrayList<WeightedPath> topPaths = new ArrayList<WeightedPath>(
+					threads);
+			int cp = beginIndex;
+			int ch = list[cp].getPathHash();
+			long cw = list[cp].getWeight();
+			totalWeight = list[cp].getWeight();
+
+			for (int i = cp + 1; i < endIndex; i++) {
+				ObjectToPack o = list[i];
+				if (ch != o.getPathHash()) {
+					if (MIN_TOP_PATH < cw) {
+						if (topPaths.size() < threads) {
+							Slice s = new Slice(cp, i);
+							topPaths.add(new WeightedPath(cw, s));
+							if (topPaths.size() == threads)
+								Collections.sort(topPaths);
+						} else if (topPaths.get(0).weight < cw) {
+							Slice s = new Slice(cp, i);
+							WeightedPath p = new WeightedPath(cw, s);
+							topPaths.set(0, p);
+							if (p.compareTo(topPaths.get(1)) > 0)
+								Collections.sort(topPaths);
+						}
+					}
+					cp = i;
+					ch = o.getPathHash();
+					cw = 0;
+				}
+				if (o.isEdge() || o.doNotAttemptDelta())
+					continue;
+				cw += o.getWeight();
+				totalWeight += o.getWeight();
+			}
+
+			// Sort by starting index to identify gaps later.
+			Collections.sort(topPaths, new Comparator<WeightedPath>() {
+				public int compare(WeightedPath a, WeightedPath b) {
+					return a.slice.beginIndex - b.slice.beginIndex;
+				}
+			});
+
+			bytesPerUnit = 1;
+			while (MAX_METER <= (totalWeight / bytesPerUnit))
+				bytesPerUnit <<= 10;
+			return topPaths;
+		}
+	}
+
+	static final class WeightedPath implements Comparable<WeightedPath> {
+		final long weight;
+		final Slice slice;
+
+		WeightedPath(long weight, Slice s) {
+			this.weight = weight;
+			this.slice = s;
+		}
+
+		public int compareTo(WeightedPath o) {
+			int cmp = Long.signum(weight - o.weight);
+			if (cmp != 0)
+				return cmp;
+			return slice.beginIndex - o.slice.beginIndex;
+		}
+	}
+
+	static final class Slice {
+		final int beginIndex;
+		final int endIndex;
+
+		Slice(int b, int e) {
+			beginIndex = b;
+			endIndex = e;
+		}
+
+		final int size() {
+			return endIndex - beginIndex;
+		}
+	}
+
+	private final Block block;
+	private final LinkedList<Slice> slices;
+
+	private ObjectReader or;
+	private DeltaWindow dw;
+
+	DeltaTask(Block b) {
+		this.block = b;
+		this.slices = new LinkedList<Slice>();
+	}
+
+	void add(Slice s) {
+		if (!slices.isEmpty()) {
+			Slice last = slices.getLast();
+			if (last.endIndex == s.beginIndex) {
+				slices.removeLast();
+				slices.add(new Slice(last.beginIndex, s.endIndex));
+				return;
+			}
+		}
+		slices.add(s);
+	}
+
+	public Object call() throws Exception {
+		or = block.templateReader.newReader();
+		try {
+			DeltaWindow w;
+			for (;;) {
+				synchronized (this) {
+					if (slices.isEmpty())
+						break;
+					w = initWindow(slices.removeFirst());
+				}
+				runWindow(w);
+			}
+			while ((w = block.stealWork(this)) != null)
+				runWindow(w);
+		} finally {
+			block.pm.endWorker();
+			or.release();
+			or = null;
+		}
+		return null;
+	}
+
+	DeltaWindow initWindow(Slice s) {
+		DeltaWindow w = new DeltaWindow(block.config, block.dc,
+				or, block.pm, block.bytesPerUnit,
+				block.list, s.beginIndex, s.endIndex);
+		synchronized (this) {
+			dw = w;
+		}
+		return w;
+	}
+
+	private void runWindow(DeltaWindow w) throws IOException {
+		try {
+			w.search();
+		} finally {
+			synchronized (this) {
+				dw = null;
+			}
+		}
+	}
+
+	synchronized Slice remaining() {
+		if (!slices.isEmpty())
+			return slices.getLast();
+		DeltaWindow d = dw;
+		return d != null ? d.remaining() : null;
+	}
+
+	synchronized boolean tryStealWork(Slice s) {
+		if (!slices.isEmpty() && slices.getLast().beginIndex == s.beginIndex) {
+			slices.removeLast();
+			return true;
+		}
+		DeltaWindow d = dw;
+		return d != null ? d.tryStealWork(s) : false;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
similarity index 55%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
index 2f18788..19d06a2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -53,20 +53,19 @@ import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.storage.pack.PackConfig;
 import org.eclipse.jgit.util.TemporaryBuffer;
 
-class DeltaWindow {
-	private static final int NEXT_RES = 0;
-
-	private static final int NEXT_SRC = 1;
+final class DeltaWindow {
+	private static final boolean NEXT_RES = false;
+	private static final boolean NEXT_SRC = true;
 
 	private final PackConfig config;
-
 	private final DeltaCache deltaCache;
-
 	private final ObjectReader reader;
-
-	private final DeltaWindowEntry[] window;
+	private final ProgressMonitor monitor;
+	private final long bytesPerUnit;
+	private long bytesProcessed;
 
 	/** Maximum number of bytes to admit to the window at once. */
 	private final long maxMemory;
@@ -74,84 +73,107 @@ class DeltaWindow {
 	/** Maximum depth we should create for any delta chain. */
 	private final int maxDepth;
 
+	private final ObjectToPack[] toSearch;
+	private int cur;
+	private int end;
+
 	/** Amount of memory we have loaded right now. */
 	private long loaded;
 
 	// The object we are currently considering needs a lot of state:
 
-	/** Position of {@link #res} within {@link #window} array. */
-	private int resSlot;
-
-	/**
-	 * Maximum delta chain depth the current object can have.
-	 * <p>
-	 * This can be smaller than {@link #maxDepth}.
-	 */
-	private int resMaxDepth;
-
 	/** Window entry of the object we are currently considering. */
 	private DeltaWindowEntry res;
 
-	/** If we have a delta for {@link #res}, this is the shortest found yet. */
-	private TemporaryBuffer.Heap bestDelta;
-
-	/** If we have {@link #bestDelta}, the window position it was created by. */
-	private int bestSlot;
+	/** If we have chosen a base, the window entry it was created from. */
+	private DeltaWindowEntry bestBase;
+	private int deltaLen;
+	private Object deltaBuf;
 
 	/** Used to compress cached deltas. */
 	private Deflater deflater;
 
-	DeltaWindow(PackConfig pc, DeltaCache dc, ObjectReader or) {
+	DeltaWindow(PackConfig pc, DeltaCache dc, ObjectReader or,
+			ProgressMonitor pm, long bpu,
+			ObjectToPack[] in, int beginIndex, int endIndex) {
 		config = pc;
 		deltaCache = dc;
 		reader = or;
+		monitor = pm;
+		bytesPerUnit = bpu;
+		toSearch = in;
+		cur = beginIndex;
+		end = endIndex;
 
-		// C Git increases the window size supplied by the user by 1.
-		// We don't know why it does this, but if the user asks for
-		// window=10, it actually processes with window=11. Because
-		// the window size has the largest direct impact on the final
-		// pack file size, we match this odd behavior here to give us
-		// a better chance of producing a similar sized pack as C Git.
-		//
-		// We would prefer to directly honor the user's request since
-		// PackWriter has a minimum of 2 for the window size, but then
-		// users might complain that JGit is creating a bigger pack file.
-		//
-		window = new DeltaWindowEntry[config.getDeltaSearchWindowSize() + 1];
-		for (int i = 0; i < window.length; i++)
-			window[i] = new DeltaWindowEntry();
-
-		maxMemory = config.getDeltaSearchMemoryLimit();
+		maxMemory = Math.max(0, config.getDeltaSearchMemoryLimit());
 		maxDepth = config.getMaxDeltaDepth();
+		res = DeltaWindowEntry.createWindow(config.getDeltaSearchWindowSize());
+	}
+
+	synchronized DeltaTask.Slice remaining() {
+		int e = end;
+		int halfRemaining = (e - cur) >>> 1;
+		if (0 == halfRemaining)
+			return null;
+
+		int split = e - halfRemaining;
+		int h = toSearch[split].getPathHash();
+
+		// Attempt to split on the next path after the 50% split point.
+		for (int n = split + 1; n < e; n++) {
+			if (h != toSearch[n].getPathHash())
+				return new DeltaTask.Slice(n, e);
+		}
+
+		if (h != toSearch[cur].getPathHash()) {
+			// Try to split on the path before the 50% split point.
+			// Do not split the path currently being processed.
+			for (int p = split - 1; cur < p; p--) {
+				if (h != toSearch[p].getPathHash())
+					return new DeltaTask.Slice(p + 1, e);
+			}
+		}
+		return null;
+	}
+
+	synchronized boolean tryStealWork(DeltaTask.Slice s) {
+		if (s.beginIndex <= cur || end <= s.beginIndex)
+			return false;
+		end = s.beginIndex;
+		return true;
 	}
 
-	void search(ProgressMonitor monitor, ObjectToPack[] toSearch, int off,
-			int cnt) throws IOException {
+	void search() throws IOException {
 		try {
-			for (int end = off + cnt; off < end; off++) {
-				res = window[resSlot];
-				if (0 < maxMemory) {
+			for (;;) {
+				ObjectToPack next;
+				synchronized (this) {
+					if (end <= cur)
+						break;
+					next = toSearch[cur++];
+				}
+				if (maxMemory != 0) {
 					clear(res);
-					int tail = next(resSlot);
-					final long need = estimateSize(toSearch[off]);
-					while (maxMemory < loaded + need && tail != resSlot) {
-						clear(window[tail]);
-						tail = next(tail);
-					}
+					final long need = estimateSize(next);
+					DeltaWindowEntry n = res.next;
+					for (; maxMemory < loaded + need && n != res; n = n.next)
+						clear(n);
 				}
-				res.set(toSearch[off]);
+				res.set(next);
 
 				if (res.object.isEdge() || res.object.doNotAttemptDelta()) {
 					// We don't actually want to make a delta for
 					// them, just need to push them into the window
 					// so they can be read by other objects.
-					//
 					keepInWindow();
 				} else {
 					// Search for a delta for the current window slot.
-					//
-					monitor.update(1);
-					search();
+					if (bytesPerUnit <= (bytesProcessed += next.getWeight())) {
+						int d = (int) (bytesProcessed / bytesPerUnit);
+						monitor.update(d);
+						bytesProcessed -= d * bytesPerUnit;
+					}
+					searchInWindow();
 				}
 			}
 		} finally {
@@ -180,40 +202,29 @@ class DeltaWindow {
 		ent.set(null);
 	}
 
-	private void search() throws IOException {
-		// TODO(spearce) If the object is used as a base for other
-		// objects in this pack we should limit the depth we create
-		// for ourselves to be the remainder of our longest dependent
-		// chain and the configured maximum depth. This can happen
-		// when the dependents are being reused out a pack, but we
-		// cannot be because we are near the edge of a thin pack.
-		//
-		resMaxDepth = maxDepth;
-
+	private void searchInWindow() throws IOException {
 		// Loop through the window backwards, considering every entry.
 		// This lets us look at the bigger objects that came before.
-		//
-		for (int srcSlot = prior(resSlot); srcSlot != resSlot; srcSlot = prior(srcSlot)) {
-			DeltaWindowEntry src = window[srcSlot];
+		for (DeltaWindowEntry src = res.prev; src != res; src = src.prev) {
 			if (src.empty())
 				break;
-			if (delta(src, srcSlot) == NEXT_RES) {
-				bestDelta = null;
-				return;
-			}
+			if (delta(src) /* == NEXT_SRC */)
+				continue;
+			bestBase = null;
+			deltaBuf = null;
+			return;
 		}
 
 		// We couldn't find a suitable delta for this object, but it may
 		// still be able to act as a base for another one.
-		//
-		if (bestDelta == null) {
+		if (bestBase == null) {
 			keepInWindow();
 			return;
 		}
 
 		// Select this best matching delta as the base for the object.
 		//
-		ObjectToPack srcObj = window[bestSlot].object;
+		ObjectToPack srcObj = bestBase.object;
 		ObjectToPack resObj = res.object;
 		if (srcObj.isEdge()) {
 			// The source (the delta base) is an edge object outside of the
@@ -221,76 +232,60 @@ class DeltaWindow {
 			// has on hand, so we don't want to send it. We have to store
 			// an ObjectId and *NOT* an ObjectToPack for the base to ensure
 			// the base isn't included in the outgoing pack file.
-			//
 			resObj.setDeltaBase(srcObj.copy());
 		} else {
 			// The base is part of the pack we are sending, so it should be
 			// a direct pointer to the base.
-			//
 			resObj.setDeltaBase(srcObj);
 		}
-		resObj.setDeltaDepth(srcObj.getDeltaDepth() + 1);
+
+		int depth = srcObj.getDeltaDepth() + 1;
+		resObj.setDeltaDepth(depth);
 		resObj.clearReuseAsIs();
 		cacheDelta(srcObj, resObj);
 
-		// Discard the cached best result, otherwise it leaks.
-		//
-		bestDelta = null;
-
-		// If this should be the end of a chain, don't keep
-		// it in the window. Just move on to the next object.
-		//
-		if (resObj.getDeltaDepth() == maxDepth)
-			return;
+		if (depth < maxDepth) {
+			// Reorder the window so that the best base will be tested
+			// first for the next object, and the current object will
+			// be the second candidate to consider before any others.
+			res.makeNext(bestBase);
+			res = bestBase.next;
+		}
 
-		shuffleBaseUpInPriority();
-		keepInWindow();
+		bestBase = null;
+		deltaBuf = null;
 	}
 
-	private int delta(final DeltaWindowEntry src, final int srcSlot)
+	private boolean delta(final DeltaWindowEntry src)
 			throws IOException {
 		// Objects must use only the same type as their delta base.
-		// If we are looking at something where that isn't true we
-		// have exhausted everything of the correct type and should
-		// move on to the next thing to examine.
-		//
 		if (src.type() != res.type()) {
 			keepInWindow();
 			return NEXT_RES;
 		}
 
-		// Only consider a source with a short enough delta chain.
-		if (src.depth() > resMaxDepth)
+		// If the sizes are radically different, this is a bad pairing.
+		if (res.size() < src.size() >>> 4)
 			return NEXT_SRC;
 
-		// Estimate a reasonable upper limit on delta size.
-		int msz = deltaSizeLimit(res, resMaxDepth, src);
-		if (msz <= 8)
+		int msz = deltaSizeLimit(src);
+		if (msz <= 8) // Nearly impossible to fit useful delta.
 			return NEXT_SRC;
 
 		// If we have to insert a lot to make this work, find another.
 		if (res.size() - src.size() > msz)
 			return NEXT_SRC;
 
-		// If the sizes are radically different, this is a bad pairing.
-		if (res.size() < src.size() / 16)
-			return NEXT_SRC;
-
 		DeltaIndex srcIndex;
 		try {
 			srcIndex = index(src);
 		} catch (LargeObjectException tooBig) {
 			// If the source is too big to work on, skip it.
-			dropFromWindow(srcSlot);
 			return NEXT_SRC;
 		} catch (IOException notAvailable) {
-			if (src.object.isEdge()) {
-				// This is an edge that is suddenly not available.
-				dropFromWindow(srcSlot);
+			if (src.object.isEdge()) // Missing edges are OK.
 				return NEXT_SRC;
-			} else {
-				throw notAvailable;
-			}
+			throw notAvailable;
 		}
 
 		byte[] resBuf;
@@ -301,48 +296,75 @@ class DeltaWindow {
 			return NEXT_RES;
 		}
 
-		// If we already have a delta for the current object, abort
-		// encoding early if this new pairing produces a larger delta.
-		if (bestDelta != null && bestDelta.length() < msz)
-			msz = (int) bestDelta.length();
-
-		TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(msz);
 		try {
-			if (!srcIndex.encode(delta, resBuf, msz))
-				return NEXT_SRC;
+			OutputStream delta = msz <= (8 << 10)
+				? new ArrayStream(msz)
+				: new TemporaryBuffer.Heap(msz);
+			if (srcIndex.encode(delta, resBuf, msz))
+				selectDeltaBase(src, delta);
 		} catch (IOException deltaTooBig) {
-			// This only happens when the heap overflows our limit.
-			return NEXT_SRC;
+			// Unlikely, encoder should see limit and return false.
 		}
+		return NEXT_SRC;
+	}
+
+	private void selectDeltaBase(DeltaWindowEntry src, OutputStream delta) {
+		bestBase = src;
 
-		if (isBetterDelta(src, delta)) {
-			bestDelta = delta;
-			bestSlot = srcSlot;
+		if (delta instanceof ArrayStream) {
+			ArrayStream a = (ArrayStream) delta;
+			deltaBuf = a.buf;
+			deltaLen = a.cnt;
+		} else {
+			TemporaryBuffer.Heap b = (TemporaryBuffer.Heap) delta;
+			deltaBuf = b;
+			deltaLen = (int) b.length();
 		}
+	}
 
-		return NEXT_SRC;
+	private int deltaSizeLimit(DeltaWindowEntry src) {
+		if (bestBase == null) {
+			// Any delta should be no more than 50% of the original size
+			// (for text files deflate of whole form should shrink 50%).
+			int n = res.size() >>> 1;
+
+			// Evenly distribute delta size limits over allowed depth.
+			// If src is non-delta (depth = 0), delta <= 50% of original.
+			// If src is almost at limit (9/10), delta <= 10% of original.
+			return n * (maxDepth - src.depth()) / maxDepth;
+		}
+
+		// With a delta base chosen any new delta must be "better".
+		// Retain the distribution described above.
+		int d = bestBase.depth();
+		int n = deltaLen;
+
+		// If src is whole (depth=0) and base is near limit (depth=9/10)
+		// any delta using src can be 10x larger and still be better.
+		//
+		// If src is near limit (depth=9/10) and base is whole (depth=0)
+		// a new delta dependent on src must be 1/10th the size.
+		return n * (maxDepth - src.depth()) / (maxDepth - d);
 	}
 
 	private void cacheDelta(ObjectToPack srcObj, ObjectToPack resObj) {
-		if (Integer.MAX_VALUE < bestDelta.length())
-			return;
-
-		int rawsz = (int) bestDelta.length();
-		if (deltaCache.canCache(rawsz, srcObj, resObj)) {
+		if (deltaCache.canCache(deltaLen, srcObj, resObj)) {
 			try {
-				byte[] zbuf = new byte[deflateBound(rawsz)];
-
+				byte[] zbuf = new byte[deflateBound(deltaLen)];
 				ZipStream zs = new ZipStream(deflater(), zbuf);
-				bestDelta.writeTo(zs, null);
-				bestDelta = null;
+				if (deltaBuf instanceof byte[])
+					zs.write((byte[]) deltaBuf, 0, deltaLen);
+				else
+					((TemporaryBuffer.Heap) deltaBuf).writeTo(zs, null);
+				deltaBuf = null;
 				int len = zs.finish();
 
-				resObj.setCachedDelta(deltaCache.cache(zbuf, len, rawsz));
-				resObj.setCachedSize(rawsz);
+				resObj.setCachedDelta(deltaCache.cache(zbuf, len, deltaLen));
+				resObj.setCachedSize(deltaLen);
 			} catch (IOException err) {
-				deltaCache.credit(rawsz);
+				deltaCache.credit(deltaLen);
 			} catch (OutOfMemoryError err) {
-				deltaCache.credit(rawsz);
+				deltaCache.credit(deltaLen);
 			}
 		}
 	}
@@ -351,76 +373,8 @@ class DeltaWindow {
 		return insz + ((insz + 7) >> 3) + ((insz + 63) >> 6) + 11;
 	}
 
-	private void shuffleBaseUpInPriority() {
-		// Shuffle the entire window so that the best match we just used
-		// is at our current index, and our current object is at the index
-		// before it. Slide any entries in between to make space.
-		//
-		window[resSlot] = window[bestSlot];
-
-		DeltaWindowEntry next = res;
-		int slot = prior(resSlot);
-		for (; slot != bestSlot; slot = prior(slot)) {
-			DeltaWindowEntry e = window[slot];
-			window[slot] = next;
-			next = e;
-		}
-		window[slot] = next;
-	}
-
 	private void keepInWindow() {
-		resSlot = next(resSlot);
-	}
-
-	private int next(int slot) {
-		if (++slot == window.length)
-			return 0;
-		return slot;
-	}
-
-	private int prior(int slot) {
-		if (slot == 0)
-			return window.length - 1;
-		return slot - 1;
-	}
-
-	private void dropFromWindow(@SuppressWarnings("unused") int srcSlot) {
-		// We should drop the current source entry from the window,
-		// it is somehow invalid for us to work with.
-	}
-
-	private boolean isBetterDelta(DeltaWindowEntry src,
-			TemporaryBuffer.Heap resDelta) {
-		if (bestDelta == null)
-			return true;
-
-		// If both delta sequences are the same length, use the one
-		// that has a shorter delta chain since it would be faster
-		// to access during reads.
-		//
-		if (resDelta.length() == bestDelta.length())
-			return src.depth() < window[bestSlot].depth();
-
-		return resDelta.length() < bestDelta.length();
-	}
-
-	private static int deltaSizeLimit(DeltaWindowEntry res, int maxDepth,
-			DeltaWindowEntry src) {
-		// Ideally the delta is at least 50% of the original size,
-		// but we also want to account for delta header overhead in
-		// the pack file (to point to the delta base) so subtract off
-		// some of those header bytes from the limit.
-		//
-		final int limit = res.size() / 2 - 20;
-
-		// Distribute the delta limit over the entire chain length.
-		// This is weighted such that deeper items in the chain must
-		// be even smaller than if they were earlier in the chain, as
-		// they cost significantly more to unpack due to the increased
-		// number of recursive unpack calls.
-		//
-		final int remainingDepth = maxDepth - src.depth();
-		return (limit * remainingDepth) / maxDepth;
+		res = res.next;
 	}
 
 	private DeltaIndex index(DeltaWindowEntry ent)
@@ -438,7 +392,7 @@ class DeltaWindow {
 				e.setObjectId(ent.object);
 				throw e;
 			}
-			if (0 < maxMemory)
+			if (maxMemory != 0)
 				loaded += idx.getIndexSize() - idx.getSourceSize();
 			ent.index = idx;
 		}
@@ -452,7 +406,7 @@ class DeltaWindow {
 			checkLoadable(ent, ent.size());
 
 			buf = PackWriter.buffer(config, reader, ent.object);
-			if (0 < maxMemory)
+			if (maxMemory != 0)
 				loaded += buf.length;
 			ent.buffer = buf;
 		}
@@ -460,17 +414,15 @@ class DeltaWindow {
 	}
 
 	private void checkLoadable(DeltaWindowEntry ent, long need) {
-		if (maxMemory <= 0)
+		if (maxMemory == 0)
 			return;
 
-		int tail = next(resSlot);
-		while (maxMemory < loaded + need) {
-			DeltaWindowEntry cur = window[tail];
-			clear(cur);
-			if (cur == ent)
+		DeltaWindowEntry n = res.next;
+		for (; maxMemory < loaded + need; n = n.next) {
+			clear(n);
+			if (n == ent)
 				throw new LargeObjectException.ExceedsLimit(
 						maxMemory, loaded + need);
-			tail = next(tail);
 		}
 	}
 
@@ -532,4 +484,28 @@ class DeltaWindow {
 			throw new UnsupportedOperationException();
 		}
 	}
+
+	static final class ArrayStream extends OutputStream {
+		final byte[] buf;
+		int cnt;
+
+		ArrayStream(int max) {
+			buf = new byte[max];
+		}
+
+		@Override
+		public void write(int b) throws IOException {
+			if (cnt == buf.length)
+				throw new IOException();
+			buf[cnt++] = (byte) b;
+		}
+
+		@Override
+		public void write(byte[] b, int off, int len) throws IOException {
+			if (len > buf.length - cnt)
+				throw new IOException();
+			System.arraycopy(b, off, buf, cnt, len);
+			cnt += len;
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java
similarity index 65%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java
index 0f1e632..958bae1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaWindowEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java
@@ -41,9 +41,11 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
-class DeltaWindowEntry {
+final class DeltaWindowEntry {
+	DeltaWindowEntry prev;
+	DeltaWindowEntry next;
 	ObjectToPack object;
 
 	/** Complete contents of this object. Lazily loaded. */
@@ -52,29 +54,65 @@ class DeltaWindowEntry {
 	/** Index of this object's content, to encode other deltas. Lazily loaded. */
 	DeltaIndex index;
 
-	void set(ObjectToPack object) {
+	final void set(ObjectToPack object) {
 		this.object = object;
 		this.index = null;
 		this.buffer = null;
 	}
 
 	/** @return current delta chain depth of this object. */
-	int depth() {
+	final int depth() {
 		return object.getDeltaDepth();
 	}
 
 	/** @return type of the object in this window entry. */
-	int type() {
+	final int type() {
 		return object.getType();
 	}
 
 	/** @return estimated unpacked size of the object, in bytes . */
-	int size() {
+	final int size() {
 		return object.getWeight();
 	}
 
 	/** @return true if there is no object stored in this entry. */
-	boolean empty() {
+	final boolean empty() {
 		return object == null;
 	}
+
+	final void makeNext(DeltaWindowEntry e) {
+		// Disconnect e from the chain.
+		e.prev.next = e.next;
+		e.next.prev = e.prev;
+
+		// Insert e after this.
+		e.next = next;
+		e.prev = this;
+		next.prev = e;
+		next = e;
+	}
+
+	static DeltaWindowEntry createWindow(int cnt) {
+		// C Git increases the window size supplied by the user by 1.
+		// We don't know why it does this, but if the user asks for
+		// window=10, it actually processes with window=11. Because
+		// the window size has the largest direct impact on the final
+		// pack file size, we match this odd behavior here to give us
+		// a better chance of producing a similar sized pack as C Git.
+		//
+		// We would prefer to directly honor the user's request since
+		// PackWriter has a minimum of 2 for the window size, but then
+		// users might complain that JGit is creating a bigger pack file.
+		DeltaWindowEntry res = new DeltaWindowEntry();
+		DeltaWindowEntry p = res;
+		for (int i = 0; i < cnt; i++) {
+			DeltaWindowEntry e = new DeltaWindowEntry();
+			e.prev = p;
+			p.next = e;
+			p = e;
+		}
+		p.next = res;
+		res.prev = p;
+		return res;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/IntSet.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/IntSet.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/IntSet.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/IntSet.java
index edb4c3d..a9b9754 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/IntSet.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/IntSet.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 class IntSet {
 	private int[] set;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
similarity index 92%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
index 795b004..00b6b65 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectReuseAsIs.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.IOException;
 import java.util.Collection;
@@ -49,9 +49,10 @@ import java.util.List;
 
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.revwalk.RevObject;
 
 /**
  * Extension of {@link ObjectReader} that supports reusing objects in packs.
@@ -71,13 +72,13 @@ public interface ObjectReuseAsIs {
 	 * object state, such as to remember what file and offset contains the
 	 * object's pack encoded data.
 	 *
-	 * @param obj
-	 *            identity of the object that will be packed. The object's
-	 *            parsed status is undefined here. Implementers must not rely on
-	 *            the object being parsed.
+	 * @param objectId
+	 *            the id of the object that will be packed.
+	 * @param type
+	 *            the Git type of the object that will be packed.
 	 * @return a new instance for this object.
 	 */
-	public ObjectToPack newObjectToPack(RevObject obj);
+	public ObjectToPack newObjectToPack(AnyObjectId objectId, int type);
 
 	/**
 	 * Select the best object representation for a packer.
@@ -88,7 +89,7 @@ public interface ObjectReuseAsIs {
 	 * the writer can select the most suitable representation to reuse into the
 	 * output stream.
 	 * <p>
-	 * If the implementation returns CachedPack from {@link #getCachedPacks()},
+	 * If the implementation returns CachedPack from {@link #getCachedPacksAndUpdate(BitmapBuilder)}
 	 * it must consider the representation of any object that is stored in any
 	 * of the offered CachedPacks. PackWriter relies on this behavior to prune
 	 * duplicate objects out of the pack stream when it selects a CachedPack and
@@ -200,20 +201,6 @@ public interface ObjectReuseAsIs {
 			StoredObjectRepresentationNotAvailableException;
 
 	/**
-	 * Obtain the available cached packs.
-	 * <p>
-	 * A cached pack has known starting points and may be sent entirely as-is,
-	 * with almost no effort on the sender's part.
-	 *
-	 * @return the available cached packs.
-	 * @throws IOException
-	 *             the cached packs cannot be listed from the repository.
-	 *             Callers may choose to ignore this and continue as-if there
-	 *             were no cached packs.
-	 */
-	public Collection<CachedPack> getCachedPacks() throws IOException;
-
-	/**
 	 * Append an entire pack's contents onto the output stream.
 	 * <p>
 	 * The entire pack, excluding its header and trailing footer is sent.
@@ -232,4 +219,22 @@ public interface ObjectReuseAsIs {
 	 */
 	public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack,
 			boolean validate) throws IOException;
+
+	/**
+	 * Obtain the available cached packs that match the bitmap and update
+	 * the bitmap by removing the items that are in the CachedPack.
+	 * <p>
+	 * A cached pack has known starting points and may be sent entirely as-is,
+	 * with almost no effort on the sender's part.
+	 *
+	 * @param needBitmap
+	 *            the bitmap that contains all of the objects the client wants.
+	 * @return the available cached packs.
+	 * @throws IOException
+	 *             the cached packs cannot be listed from the repository.
+	 *             Callers may choose to ignore this and continue as-if there
+	 *             were no cached packs.
+	 */
+	public Collection<CachedPack> getCachedPacksAndUpdate(
+			BitmapBuilder needBitmap) throws IOException;
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java
similarity index 83%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java
index 1b8a9bf..33cb6af 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ObjectToPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java
@@ -42,12 +42,11 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.transport.PackedObjectInfo;
 
 /**
@@ -58,28 +57,16 @@ import org.eclipse.jgit.transport.PackedObjectInfo;
  * each object as they are written to the output stream.
  */
 public class ObjectToPack extends PackedObjectInfo {
-	private static final int WANT_WRITE = 1 << 0;
-
-	private static final int REUSE_AS_IS = 1 << 1;
-
+	private static final int REUSE_AS_IS = 1 << 0;
+	private static final int DELTA_ATTEMPTED = 1 << 1;
 	private static final int DO_NOT_DELTA = 1 << 2;
-
 	private static final int EDGE = 1 << 3;
-
-	private static final int DELTA_ATTEMPTED = 1 << 4;
-
 	private static final int ATTEMPT_DELTA_MASK = REUSE_AS_IS | DELTA_ATTEMPTED;
-
 	private static final int TYPE_SHIFT = 5;
-
 	private static final int EXT_SHIFT = 8;
-
 	private static final int EXT_MASK = 0xf;
-
 	private static final int DELTA_SHIFT = 12;
-
 	private static final int NON_EXT_MASK = ~(EXT_MASK << EXT_SHIFT);
-
 	private static final int NON_DELTA_MASK = 0xfff;
 
 	/** Other object being packed that this will delta against. */
@@ -88,11 +75,11 @@ public class ObjectToPack extends PackedObjectInfo {
 	/**
 	 * Bit field, from bit 0 to bit 31:
 	 * <ul>
-	 * <li>1 bit: wantWrite</li>
 	 * <li>1 bit: canReuseAsIs</li>
+	 * <li>1 bit: deltaAttempted</li>
 	 * <li>1 bit: doNotDelta</li>
 	 * <li>1 bit: edgeObject</li>
-	 * <li>1 bit: deltaAttempted</li>
+	 * <li>1 bit: unused</li>
 	 * <li>3 bits: type</li>
 	 * <li>4 bits: subclass flags (if any)</li>
 	 * <li>--</li>
@@ -121,23 +108,11 @@ public class ObjectToPack extends PackedObjectInfo {
 	}
 
 	/**
-	 * Construct for the specified object.
-	 *
-	 * @param obj
-	 *            identity of the object that will be packed. The object's
-	 *            parsed status is undefined here. Implementers must not rely on
-	 *            the object being parsed.
-	 */
-	public ObjectToPack(RevObject obj) {
-		this(obj, obj.getType());
-	}
-
-	/**
 	 * @return delta base object id if object is going to be packed in delta
 	 *         representation; null otherwise - if going to be packed as a
 	 *         whole object.
 	 */
-	public ObjectId getDeltaBaseId() {
+	public final ObjectId getDeltaBaseId() {
 		return deltaBase;
 	}
 
@@ -147,7 +122,7 @@ public class ObjectToPack extends PackedObjectInfo {
 	 *         pack; null otherwise - if going to be packed as a whole
 	 *         object or delta base is specified only as id.
 	 */
-	public ObjectToPack getDeltaBase() {
+	public final ObjectToPack getDeltaBase() {
 		if (deltaBase instanceof ObjectToPack)
 			return (ObjectToPack) deltaBase;
 		return null;
@@ -163,22 +138,22 @@ public class ObjectToPack extends PackedObjectInfo {
 	 *            whole object.
 	 *
 	 */
-	void setDeltaBase(ObjectId deltaBase) {
+	final void setDeltaBase(ObjectId deltaBase) {
 		this.deltaBase = deltaBase;
 	}
 
-	void setCachedDelta(DeltaCache.Ref data){
+	final void setCachedDelta(DeltaCache.Ref data) {
 		cachedDelta = data;
 	}
 
-	DeltaCache.Ref popCachedDelta() {
+	final DeltaCache.Ref popCachedDelta() {
 		DeltaCache.Ref r = cachedDelta;
 		if (r != null)
 			cachedDelta = null;
 		return r;
 	}
 
-	void clearDeltaBase() {
+	final void clearDeltaBase() {
 		this.deltaBase = null;
 
 		if (cachedDelta != null) {
@@ -192,7 +167,7 @@ public class ObjectToPack extends PackedObjectInfo {
 	 * @return true if object is going to be written as delta; false
 	 *         otherwise.
 	 */
-	public boolean isDeltaRepresentation() {
+	public final boolean isDeltaRepresentation() {
 		return deltaBase != null;
 	}
 
@@ -202,40 +177,52 @@ public class ObjectToPack extends PackedObjectInfo {
 	 *
 	 * @return true if object is already written; false otherwise.
 	 */
-	public boolean isWritten() {
-		return getOffset() != 0;
+	public final boolean isWritten() {
+		return 1 < getOffset(); // markWantWrite sets 1.
 	}
 
 	/** @return the type of this object. */
-	public int getType() {
+	public final int getType() {
 		return (flags >> TYPE_SHIFT) & 0x7;
 	}
 
-	int getDeltaDepth() {
+	final int getDeltaDepth() {
 		return flags >>> DELTA_SHIFT;
 	}
 
-	void setDeltaDepth(int d) {
+	final void setDeltaDepth(int d) {
 		flags = (d << DELTA_SHIFT) | (flags & NON_DELTA_MASK);
 	}
 
-	boolean wantWrite() {
-		return (flags & WANT_WRITE) != 0;
+	final int getChainLength() {
+		return getDeltaDepth();
 	}
 
-	void markWantWrite() {
-		flags |= WANT_WRITE;
+	final void setChainLength(int len) {
+		setDeltaDepth(len);
+	}
+
+	final void clearChainLength() {
+		flags &= NON_DELTA_MASK;
+	}
+
+	final boolean wantWrite() {
+		return getOffset() == 1;
+	}
+
+	final void markWantWrite() {
+		setOffset(1);
 	}
 
 	/**
 	 * @return true if an existing representation was selected to be reused
 	 *         as-is into the pack stream.
 	 */
-	public boolean isReuseAsIs() {
+	public final boolean isReuseAsIs() {
 		return (flags & REUSE_AS_IS) != 0;
 	}
 
-	void setReuseAsIs() {
+	final void setReuseAsIs() {
 		flags |= REUSE_AS_IS;
 	}
 
@@ -250,35 +237,28 @@ public class ObjectToPack extends PackedObjectInfo {
 		flags &= ~REUSE_AS_IS;
 	}
 
-	boolean isDoNotDelta() {
+	final boolean isDoNotDelta() {
 		return (flags & DO_NOT_DELTA) != 0;
 	}
 
-	void setDoNotDelta(boolean noDelta) {
-		if (noDelta)
-			flags |= DO_NOT_DELTA;
-		else
-			flags &= ~DO_NOT_DELTA;
+	final void setDoNotDelta() {
+		flags |= DO_NOT_DELTA;
 	}
 
-	boolean isEdge() {
+	final boolean isEdge() {
 		return (flags & EDGE) != 0;
 	}
 
-	void setEdge() {
+	final void setEdge() {
 		flags |= EDGE;
 	}
 
-	boolean doNotAttemptDelta() {
+	final boolean doNotAttemptDelta() {
 		// Do not attempt if delta attempted and object reuse.
 		return (flags & ATTEMPT_DELTA_MASK) == ATTEMPT_DELTA_MASK;
 	}
 
-	boolean isDeltaAttempted() {
-		return (flags & DELTA_ATTEMPTED) != 0;
-	}
-
-	void setDeltaAttempted(boolean deltaAttempted) {
+	final void setDeltaAttempted(boolean deltaAttempted) {
 		if (deltaAttempted)
 			flags |= DELTA_ATTEMPTED;
 		else
@@ -286,7 +266,7 @@ public class ObjectToPack extends PackedObjectInfo {
 	}
 
 	/** @return the extended flags on this object, in the range [0x0, 0xf]. */
-	protected int getExtendedFlags() {
+	protected final int getExtendedFlags() {
 		return (flags >>> EXT_SHIFT) & EXT_MASK;
 	}
 
@@ -300,7 +280,7 @@ public class ObjectToPack extends PackedObjectInfo {
 	 *            the flag mask to test, must be between 0x0 and 0xf.
 	 * @return true if any of the bits matching the mask are non-zero.
 	 */
-	protected boolean isExtendedFlag(int flag) {
+	protected final boolean isExtendedFlag(int flag) {
 		return (flags & (flag << EXT_SHIFT)) != 0;
 	}
 
@@ -313,7 +293,7 @@ public class ObjectToPack extends PackedObjectInfo {
 	 * @param flag
 	 *            the bits to set, must be between 0x0 and 0xf.
 	 */
-	protected void setExtendedFlag(int flag) {
+	protected final void setExtendedFlag(int flag) {
 		flags |= (flag & EXT_MASK) << EXT_SHIFT;
 	}
 
@@ -326,7 +306,7 @@ public class ObjectToPack extends PackedObjectInfo {
 	 * @param flag
 	 *            the bits to clear, must be between 0x0 and 0xf.
 	 */
-	protected void clearExtendedFlag(int flag) {
+	protected final void clearExtendedFlag(int flag) {
 		flags &= ~((flag & EXT_MASK) << EXT_SHIFT);
 	}
 
@@ -340,11 +320,11 @@ public class ObjectToPack extends PackedObjectInfo {
 	 *            additional flag bits to store in the flags field. Due to space
 	 *            constraints only values [0x0, 0xf] are permitted.
 	 */
-	protected void setExtendedFlags(int extFlags) {
+	protected final void setExtendedFlags(int extFlags) {
 		flags = ((extFlags & EXT_MASK) << EXT_SHIFT) | (flags & NON_EXT_MASK);
 	}
 
-	int getFormat() {
+	final int getFormat() {
 		if (isReuseAsIs()) {
 			if (isDeltaRepresentation())
 				return StoredObjectRepresentation.PACK_DELTA;
@@ -354,27 +334,27 @@ public class ObjectToPack extends PackedObjectInfo {
 	}
 
 	// Overload weight into CRC since we don't need them at the same time.
-	int getWeight() {
+	final int getWeight() {
 		return getCRC();
 	}
 
-	void setWeight(int weight) {
+	final void setWeight(int weight) {
 		setCRC(weight);
 	}
 
-	int getPathHash() {
+	final int getPathHash() {
 		return pathHash;
 	}
 
-	void setPathHash(int hc) {
+	final void setPathHash(int hc) {
 		pathHash = hc;
 	}
 
-	int getCachedSize() {
+	final int getCachedSize() {
 		return pathHash;
 	}
 
-	void setCachedSize(int sz) {
+	final void setCachedSize(int sz) {
 		pathHash = sz;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
similarity index 59%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
index cb33308..4ee27cc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
@@ -41,25 +41,59 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 /** A pack file extension. */
 public class PackExt {
+	private static volatile PackExt[] VALUES = new PackExt[] {};
 
 	/** A pack file extension. */
-	public static final PackExt PACK = new PackExt("pack"); //$NON-NLS-1$
+	public static final PackExt PACK = newPackExt("pack"); //$NON-NLS-1$
 
 	/** A pack index file extension. */
-	public static final PackExt INDEX = new PackExt("idx"); //$NON-NLS-1$
+	public static final PackExt INDEX = newPackExt("idx"); //$NON-NLS-1$
 
-	private final String ext;
+	/** A pack bitmap index file extension. */
+	public static final PackExt BITMAP_INDEX = newPackExt("bitmap"); //$NON-NLS-1$
+
+	/** @return all of the PackExt values. */
+	public static PackExt[] values() {
+		return VALUES;
+	}
 
 	/**
+	 * Returns a PackExt for the file extension and registers it in the values
+	 * array.
+	 *
 	 * @param ext
 	 *            the file extension.
+	 * @return the PackExt for the ext
 	 */
-	public PackExt(String ext) {
+	public synchronized static PackExt newPackExt(String ext) {
+		PackExt[] dst = new PackExt[VALUES.length + 1];
+		for (int i = 0; i < VALUES.length; i++) {
+			PackExt packExt = VALUES[i];
+			if (packExt.getExtension().equals(ext))
+				return packExt;
+			dst[i] = packExt;
+		}
+		if (VALUES.length >= 32)
+			throw new IllegalStateException(
+					"maximum number of pack extensions exceeded"); //$NON-NLS-1$
+
+		PackExt value = new PackExt(ext, VALUES.length);
+		dst[VALUES.length] = value;
+		VALUES = dst;
+		return value;
+	}
+
+	private final String ext;
+
+	private final int pos;
+
+	private PackExt(String ext, int pos) {
 		this.ext = ext;
+		this.pos = pos;
 	}
 
 	/** @return the file extension. */
@@ -67,21 +101,19 @@ public class PackExt {
 		return ext;
 	}
 
-	@Override
-	public boolean equals(Object obj) {
-		if (obj instanceof PackExt) {
-			return ((PackExt) obj).getExtension().equals(getExtension());
-		}
-		return false;
+	/** @return the position of the extension in the values array. */
+	public int getPosition() {
+		return pos;
 	}
 
-	@Override
-	public int hashCode() {
-		return getExtension().hashCode();
+	/** @return the bit mask of the extension e.g {@code 1 << getPosition()}. */
+	public int getBit() {
+		return 1 << getPosition();
 	}
 
 	@Override
 	public String toString() {
-		return String.format("PackExt[%s]", getExtension()); //$NON-NLS-1$
+		return String.format("PackExt[%s, bit=0x%s]", getExtension(), //$NON-NLS-1$
+				Integer.toHexString(getBit()));
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java
similarity index 73%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java
index 9092825..be1e3d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java
@@ -42,12 +42,15 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
+
+import static org.eclipse.jgit.lib.Constants.OBJ_OFS_DELTA;
+import static org.eclipse.jgit.lib.Constants.OBJ_REF_DELTA;
+import static org.eclipse.jgit.lib.Constants.PACK_SIGNATURE;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.security.MessageDigest;
-import java.util.zip.CRC32;
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
@@ -64,18 +67,18 @@ public final class PackOutputStream extends OutputStream {
 
 	private final PackWriter packWriter;
 
-	private final CRC32 crc = new CRC32();
-
 	private final MessageDigest md = Constants.newMessageDigest();
 
 	private long count;
 
-	private byte[] headerBuffer = new byte[32];
+	private final byte[] headerBuffer = new byte[32];
 
-	private byte[] copyBuffer;
+	private final byte[] copyBuffer = new byte[64 << 10];
 
 	private long checkCancelAt;
 
+	private boolean ofsDelta;
+
 	/**
 	 * Initialize a pack output stream.
 	 * <p>
@@ -99,15 +102,14 @@ public final class PackOutputStream extends OutputStream {
 	}
 
 	@Override
-	public void write(final int b) throws IOException {
+	public final void write(final int b) throws IOException {
 		count++;
 		out.write(b);
-		crc.update(b);
 		md.update((byte) b);
 	}
 
 	@Override
-	public void write(final byte[] b, int off, int len)
+	public final void write(final byte[] b, int off, int len)
 			throws IOException {
 		while (0 < len) {
 			final int n = Math.min(len, BYTES_TO_WRITE_BEFORE_CANCEL_CHECK);
@@ -122,7 +124,6 @@ public final class PackOutputStream extends OutputStream {
 			}
 
 			out.write(b, off, n);
-			crc.update(b, off, n);
 			md.update(b, off, n);
 
 			off += n;
@@ -135,11 +136,13 @@ public final class PackOutputStream extends OutputStream {
 		out.flush();
 	}
 
-	void writeFileHeader(int version, long objectCount) throws IOException {
-		System.arraycopy(Constants.PACK_SIGNATURE, 0, headerBuffer, 0, 4);
+	final void writeFileHeader(int version, long objectCount)
+			throws IOException {
+		System.arraycopy(PACK_SIGNATURE, 0, headerBuffer, 0, 4);
 		NB.encodeInt32(headerBuffer, 4, version);
 		NB.encodeInt32(headerBuffer, 8, (int) objectCount);
 		write(headerBuffer, 0, 12);
+		ofsDelta = packWriter.isDeltaBaseAsOffset();
 	}
 
 	/**
@@ -157,7 +160,7 @@ public final class PackOutputStream extends OutputStream {
 	 *             examine the type of exception and possibly its message to
 	 *             distinguish between these cases.
 	 */
-	public void writeObject(ObjectToPack otp) throws IOException {
+	public final void writeObject(ObjectToPack otp) throws IOException {
 		packWriter.writeObject(this, otp);
 	}
 
@@ -177,52 +180,52 @@ public final class PackOutputStream extends OutputStream {
 	 * @throws IOException
 	 *             the underlying stream refused to accept the header.
 	 */
-	public void writeHeader(ObjectToPack otp, long rawLength)
+	public final void writeHeader(ObjectToPack otp, long rawLength)
 			throws IOException {
-		if (otp.isDeltaRepresentation()) {
-			if (packWriter.isDeltaBaseAsOffset()) {
-				ObjectToPack baseInPack = otp.getDeltaBase();
-				if (baseInPack != null && baseInPack.isWritten()) {
-					final long start = count;
-					int n = encodeTypeSize(Constants.OBJ_OFS_DELTA, rawLength);
-					write(headerBuffer, 0, n);
-
-					long offsetDiff = start - baseInPack.getOffset();
-					n = headerBuffer.length - 1;
-					headerBuffer[n] = (byte) (offsetDiff & 0x7F);
-					while ((offsetDiff >>= 7) > 0)
-						headerBuffer[--n] = (byte) (0x80 | (--offsetDiff & 0x7F));
-					write(headerBuffer, n, headerBuffer.length - n);
-					return;
-				}
-			}
-
-			int n = encodeTypeSize(Constants.OBJ_REF_DELTA, rawLength);
+		ObjectToPack b = otp.getDeltaBase();
+		if (b != null && (b.isWritten() & ofsDelta)) {
+			int n = objectHeader(rawLength, OBJ_OFS_DELTA, headerBuffer);
+			n = ofsDelta(count - b.getOffset(), headerBuffer, n);
+			write(headerBuffer, 0, n);
+		} else if (otp.isDeltaRepresentation()) {
+			int n = objectHeader(rawLength, OBJ_REF_DELTA, headerBuffer);
 			otp.getDeltaBaseId().copyRawTo(headerBuffer, n);
-			write(headerBuffer, 0, n + Constants.OBJECT_ID_LENGTH);
+			write(headerBuffer, 0, n + 20);
 		} else {
-			int n = encodeTypeSize(otp.getType(), rawLength);
+			int n = objectHeader(rawLength, otp.getType(), headerBuffer);
 			write(headerBuffer, 0, n);
 		}
 	}
 
-	private int encodeTypeSize(int type, long rawLength) {
-		long nextLength = rawLength >>> 4;
-		headerBuffer[0] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (type << 4) | (rawLength & 0x0F));
-		rawLength = nextLength;
-		int n = 1;
-		while (rawLength > 0) {
-			nextLength >>>= 7;
-			headerBuffer[n++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (rawLength & 0x7F));
-			rawLength = nextLength;
+	private static final int objectHeader(long len, int type, byte[] buf) {
+		byte b = (byte) ((type << 4) | (len & 0x0F));
+		int n = 0;
+		for (len >>>= 4; len != 0; len >>>= 7) {
+			buf[n++] = (byte) (0x80 | b);
+			b = (byte) (len & 0x7F);
 		}
+		buf[n++] = b;
+		return n;
+	}
+
+	private static final int ofsDelta(long diff, byte[] buf, int p) {
+		p += ofsDeltaVarIntLength(diff);
+		int n = p;
+		buf[--n] = (byte) (diff & 0x7F);
+		while ((diff >>>= 7) != 0)
+			buf[--n] = (byte) (0x80 | (--diff & 0x7F));
+		return p;
+	}
+
+	private static final int ofsDeltaVarIntLength(long v) {
+		int n = 1;
+		for (; (v >>>= 7) != 0; n++)
+			--v;
 		return n;
 	}
 
 	/** @return a temporary buffer writers can use to copy data with. */
-	public byte[] getCopyBuffer() {
-		if (copyBuffer == null)
-			copyBuffer = new byte[16 * 1024];
+	public final byte[] getCopyBuffer() {
 		return copyBuffer;
 	}
 
@@ -231,22 +234,12 @@ public final class PackOutputStream extends OutputStream {
 	}
 
 	/** @return total number of bytes written since stream start. */
-	public long length() {
+	public final long length() {
 		return count;
 	}
 
-	/** @return obtain the current CRC32 register. */
-	int getCRC32() {
-		return (int) crc.getValue();
-	}
-
-	/** Reinitialize the CRC32 register for a new region. */
-	void resetCRC32() {
-		crc.reset();
-	}
-
 	/** @return obtain the current SHA-1 digest. */
-	byte[] getDigest() {
+	final byte[] getDigest() {
 		return md.digest();
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
similarity index 82%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 1cf1781..a712259 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -42,10 +42,15 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
-import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_DELTA;
-import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
+import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA;
+import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -57,10 +62,8 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -72,6 +75,8 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedOutputStream;
 import java.util.zip.Deflater;
 import java.util.zip.DeflaterOutputStream;
 
@@ -81,9 +86,15 @@ import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexWriterV1;
+import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
 import org.eclipse.jgit.lib.BatchingProgressMonitor;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.BitmapObject;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
@@ -98,12 +109,11 @@ import org.eclipse.jgit.revwalk.DepthWalk;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevFlagSet;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.storage.file.PackIndexWriter;
+import org.eclipse.jgit.storage.pack.PackConfig;
 import org.eclipse.jgit.util.BlockList;
 import org.eclipse.jgit.util.TemporaryBuffer;
 
@@ -200,12 +210,12 @@ public class PackWriter {
 	}
 
 	@SuppressWarnings("unchecked")
-	private final BlockList<ObjectToPack> objectsLists[] = new BlockList[Constants.OBJ_TAG + 1];
+	private final BlockList<ObjectToPack> objectsLists[] = new BlockList[OBJ_TAG + 1];
 	{
-		objectsLists[Constants.OBJ_COMMIT] = new BlockList<ObjectToPack>();
-		objectsLists[Constants.OBJ_TREE] = new BlockList<ObjectToPack>();
-		objectsLists[Constants.OBJ_BLOB] = new BlockList<ObjectToPack>();
-		objectsLists[Constants.OBJ_TAG] = new BlockList<ObjectToPack>();
+		objectsLists[OBJ_COMMIT] = new BlockList<ObjectToPack>();
+		objectsLists[OBJ_TREE] = new BlockList<ObjectToPack>();
+		objectsLists[OBJ_BLOB] = new BlockList<ObjectToPack>();
+		objectsLists[OBJ_TAG] = new BlockList<ObjectToPack>();
 	}
 
 	private final ObjectIdOwnerMap<ObjectToPack> objectsMap = new ObjectIdOwnerMap<ObjectToPack>();
@@ -213,6 +223,9 @@ public class PackWriter {
 	// edge objects for thin packs
 	private List<ObjectToPack> edgeObjects = new BlockList<ObjectToPack>();
 
+	// Objects the client is known to have already.
+	private BitmapBuilder haveObjects;
+
 	private List<CachedPack> cachedPacks = new ArrayList<CachedPack>(2);
 
 	private Set<ObjectId> tagTargets = Collections.emptySet();
@@ -254,16 +267,26 @@ public class PackWriter {
 
 	private boolean useCachedPacks;
 
+	private boolean useBitmaps;
+
 	private boolean ignoreMissingUninteresting = true;
 
 	private boolean pruneCurrentObjectList;
 
 	private boolean shallowPack;
 
+	private boolean canBuildBitmaps;
+
+	private boolean indexDisabled;
+
 	private int depth;
 
 	private Collection<? extends ObjectId> unshallowObjects;
 
+	private PackBitmapIndexBuilder writeBitmaps;
+
+	private CRC32 crc32;
+
 	/**
 	 * Create writer for specified repository.
 	 * <p>
@@ -441,6 +464,32 @@ public class PackWriter {
 		useCachedPacks = useCached;
 	}
 
+	/** @return true to use bitmaps for ObjectWalks, if available. */
+	public boolean isUseBitmaps() {
+		return useBitmaps;
+	}
+
+	/**
+	 * @param useBitmaps
+	 *            if set to true, bitmaps will be used when preparing a pack.
+	 */
+	public void setUseBitmaps(boolean useBitmaps) {
+		this.useBitmaps = useBitmaps;
+	}
+
+	/** @return true if the index file cannot be created by this PackWriter. */
+	public boolean isIndexDisabled() {
+		return indexDisabled || !cachedPacks.isEmpty();
+	}
+
+	/**
+	 * @param noIndex
+	 *            true to disable creation of the index file.
+	 */
+	public void setIndexDisabled(boolean noIndex) {
+		this.indexDisabled = noIndex;
+	}
+
 	/**
 	 * @return true to ignore objects that are uninteresting and also not found
 	 *         on local disk; false to throw a {@link MissingObjectException}
@@ -506,10 +555,10 @@ public class PackWriter {
 		if (stats.totalObjects == 0) {
 			long objCnt = 0;
 
-			objCnt += objectsLists[Constants.OBJ_COMMIT].size();
-			objCnt += objectsLists[Constants.OBJ_TREE].size();
-			objCnt += objectsLists[Constants.OBJ_BLOB].size();
-			objCnt += objectsLists[Constants.OBJ_TAG].size();
+			objCnt += objectsLists[OBJ_COMMIT].size();
+			objCnt += objectsLists[OBJ_TREE].size();
+			objCnt += objectsLists[OBJ_BLOB].size();
+			objCnt += objectsLists[OBJ_TAG].size();
 
 			for (CachedPack pack : cachedPacks)
 				objCnt += pack.getObjectCount();
@@ -782,16 +831,35 @@ public class PackWriter {
 	 * @return ObjectId representing SHA-1 name of a pack that was created.
 	 */
 	public ObjectId computeName() {
-		final byte[] buf = new byte[Constants.OBJECT_ID_LENGTH];
+		final byte[] buf = new byte[OBJECT_ID_LENGTH];
 		final MessageDigest md = Constants.newMessageDigest();
 		for (ObjectToPack otp : sortByName()) {
 			otp.copyRawTo(buf, 0);
-			md.update(buf, 0, Constants.OBJECT_ID_LENGTH);
+			md.update(buf, 0, OBJECT_ID_LENGTH);
 		}
 		return ObjectId.fromRaw(md.digest());
 	}
 
 	/**
+	 * Returns the index format version that will be written.
+	 * <p>
+	 * This method can only be invoked after
+	 * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} has
+	 * been invoked and completed successfully.
+	 *
+	 * @return the index format version.
+	 */
+	public int getIndexVersion() {
+		int indexVersion = config.getIndexVersion();
+		if (indexVersion <= 0) {
+			for (BlockList<ObjectToPack> objs : objectsLists)
+				indexVersion = Math.max(indexVersion,
+						PackIndexWriter.oldestPossibleFormat(objs));
+		}
+		return indexVersion;
+	}
+
+	/**
 	 * Create an index file to match the pack file just written.
 	 * <p>
 	 * This method can only be invoked after
@@ -806,34 +874,54 @@ public class PackWriter {
 	 *             the index data could not be written to the supplied stream.
 	 */
 	public void writeIndex(final OutputStream indexStream) throws IOException {
-		if (!cachedPacks.isEmpty())
+		if (isIndexDisabled())
 			throw new IOException(JGitText.get().cachedPacksPreventsIndexCreation);
 
 		long writeStart = System.currentTimeMillis();
-		final List<ObjectToPack> list = sortByName();
-		final PackIndexWriter iw;
-		int indexVersion = config.getIndexVersion();
-		if (indexVersion <= 0)
-			iw = PackIndexWriter.createOldestPossible(indexStream, list);
-		else
-			iw = PackIndexWriter.createVersion(indexStream, indexVersion);
-		iw.write(list, packcsum);
+		final PackIndexWriter iw = PackIndexWriter.createVersion(
+				indexStream, getIndexVersion());
+		iw.write(sortByName(), packcsum);
+		stats.timeWriting += System.currentTimeMillis() - writeStart;
+	}
+
+	/**
+	 * Create a bitmap index file to match the pack file just written.
+	 * <p>
+	 * This method can only be invoked after
+	 * {@link #prepareBitmapIndex(ProgressMonitor)} has been invoked and
+	 * completed successfully. Writing a corresponding bitmap index is an
+	 * optional feature that not all pack users may require.
+	 *
+	 * @param bitmapIndexStream
+	 *            output for the bitmap index data. Caller is responsible for
+	 *            closing this stream.
+	 * @throws IOException
+	 *             the index data could not be written to the supplied stream.
+	 */
+	public void writeBitmapIndex(final OutputStream bitmapIndexStream)
+			throws IOException {
+		if (writeBitmaps == null)
+			throw new IOException(JGitText.get().bitmapsMustBePrepared);
+
+		long writeStart = System.currentTimeMillis();
+		final PackBitmapIndexWriterV1 iw = new PackBitmapIndexWriterV1(bitmapIndexStream);
+		iw.write(writeBitmaps, packcsum);
 		stats.timeWriting += System.currentTimeMillis() - writeStart;
 	}
 
 	private List<ObjectToPack> sortByName() {
 		if (sortedByName == null) {
 			int cnt = 0;
-			cnt += objectsLists[Constants.OBJ_COMMIT].size();
-			cnt += objectsLists[Constants.OBJ_TREE].size();
-			cnt += objectsLists[Constants.OBJ_BLOB].size();
-			cnt += objectsLists[Constants.OBJ_TAG].size();
+			cnt += objectsLists[OBJ_COMMIT].size();
+			cnt += objectsLists[OBJ_TREE].size();
+			cnt += objectsLists[OBJ_BLOB].size();
+			cnt += objectsLists[OBJ_TAG].size();
 
 			sortedByName = new BlockList<ObjectToPack>(cnt);
-			sortedByName.addAll(objectsLists[Constants.OBJ_COMMIT]);
-			sortedByName.addAll(objectsLists[Constants.OBJ_TREE]);
-			sortedByName.addAll(objectsLists[Constants.OBJ_BLOB]);
-			sortedByName.addAll(objectsLists[Constants.OBJ_TAG]);
+			sortedByName.addAll(objectsLists[OBJ_COMMIT]);
+			sortedByName.addAll(objectsLists[OBJ_TREE]);
+			sortedByName.addAll(objectsLists[OBJ_BLOB]);
+			sortedByName.addAll(objectsLists[OBJ_TAG]);
 			Collections.sort(sortedByName);
 		}
 		return sortedByName;
@@ -859,6 +947,9 @@ public class PackWriter {
 		case WRITING:
 			task = JGitText.get().writingObjects;
 			break;
+		case BUILDING_BITMAPS:
+			task = JGitText.get().buildingBitmaps;
+			break;
 		default:
 			throw new IllegalArgumentException(
 					MessageFormat.format(JGitText.get().illegalPackingPhase, phase));
@@ -924,8 +1015,13 @@ public class PackWriter {
 		if (config.isDeltaCompress())
 			searchForDeltas(compressMonitor);
 
-		final PackOutputStream out = new PackOutputStream(writeMonitor,
-				packStream, this);
+		crc32 = new CRC32();
+		final PackOutputStream out = new PackOutputStream(
+			writeMonitor,
+			isIndexDisabled()
+				? packStream
+				: new CheckedOutputStream(packStream, crc32),
+			this);
 
 		long objCnt = getObjectCount();
 		stats.totalObjects = objCnt;
@@ -998,39 +1094,41 @@ public class PackWriter {
 
 	private void searchForReuse(ProgressMonitor monitor) throws IOException {
 		long cnt = 0;
-		cnt += objectsLists[Constants.OBJ_COMMIT].size();
-		cnt += objectsLists[Constants.OBJ_TREE].size();
-		cnt += objectsLists[Constants.OBJ_BLOB].size();
-		cnt += objectsLists[Constants.OBJ_TAG].size();
+		cnt += objectsLists[OBJ_COMMIT].size();
+		cnt += objectsLists[OBJ_TREE].size();
+		cnt += objectsLists[OBJ_BLOB].size();
+		cnt += objectsLists[OBJ_TAG].size();
 
 		long start = System.currentTimeMillis();
 		beginPhase(PackingPhase.FINDING_SOURCES, monitor, cnt);
-
 		if (cnt <= 4096) {
 			// For small object counts, do everything as one list.
 			BlockList<ObjectToPack> tmp = new BlockList<ObjectToPack>((int) cnt);
-			tmp.addAll(objectsLists[Constants.OBJ_TAG]);
-			tmp.addAll(objectsLists[Constants.OBJ_COMMIT]);
-			tmp.addAll(objectsLists[Constants.OBJ_TREE]);
-			tmp.addAll(objectsLists[Constants.OBJ_BLOB]);
+			tmp.addAll(objectsLists[OBJ_TAG]);
+			tmp.addAll(objectsLists[OBJ_COMMIT]);
+			tmp.addAll(objectsLists[OBJ_TREE]);
+			tmp.addAll(objectsLists[OBJ_BLOB]);
 			searchForReuse(monitor, tmp);
 			if (pruneCurrentObjectList) {
 				// If the list was pruned, we need to re-prune the main lists.
-				pruneEdgesFromObjectList(objectsLists[Constants.OBJ_COMMIT]);
-				pruneEdgesFromObjectList(objectsLists[Constants.OBJ_TREE]);
-				pruneEdgesFromObjectList(objectsLists[Constants.OBJ_BLOB]);
-				pruneEdgesFromObjectList(objectsLists[Constants.OBJ_TAG]);
+				pruneEdgesFromObjectList(objectsLists[OBJ_COMMIT]);
+				pruneEdgesFromObjectList(objectsLists[OBJ_TREE]);
+				pruneEdgesFromObjectList(objectsLists[OBJ_BLOB]);
+				pruneEdgesFromObjectList(objectsLists[OBJ_TAG]);
 			}
-
 		} else {
-			searchForReuse(monitor, objectsLists[Constants.OBJ_TAG]);
-			searchForReuse(monitor, objectsLists[Constants.OBJ_COMMIT]);
-			searchForReuse(monitor, objectsLists[Constants.OBJ_TREE]);
-			searchForReuse(monitor, objectsLists[Constants.OBJ_BLOB]);
+			searchForReuse(monitor, objectsLists[OBJ_TAG]);
+			searchForReuse(monitor, objectsLists[OBJ_COMMIT]);
+			searchForReuse(monitor, objectsLists[OBJ_TREE]);
+			searchForReuse(monitor, objectsLists[OBJ_BLOB]);
 		}
-
 		endPhase(monitor);
 		stats.timeSearchingForReuse = System.currentTimeMillis() - start;
+
+		if (config.isReuseDeltas() && config.getCutDeltaChains()) {
+			cutDeltaChains(objectsLists[OBJ_TREE]);
+			cutDeltaChains(objectsLists[OBJ_BLOB]);
+		}
 	}
 
 	private void searchForReuse(ProgressMonitor monitor, List<ObjectToPack> list)
@@ -1041,6 +1139,29 @@ public class PackWriter {
 			pruneEdgesFromObjectList(list);
 	}
 
+	private void cutDeltaChains(BlockList<ObjectToPack> list)
+			throws IOException {
+		int max = config.getMaxDeltaDepth();
+		for (int idx = list.size() - 1; idx >= 0; idx--) {
+			int d = 0;
+			ObjectToPack b = list.get(idx).getDeltaBase();
+			while (b != null) {
+				if (d < b.getChainLength())
+					break;
+				b.setChainLength(++d);
+				if (d >= max && b.isDeltaRepresentation()) {
+					reselectNonDelta(b);
+					break;
+				}
+				b = b.getDeltaBase();
+			}
+		}
+		if (config.isDeltaCompress()) {
+			for (ObjectToPack otp : list)
+				otp.clearChainLength();
+		}
+	}
+
 	private void searchForDeltas(ProgressMonitor monitor)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
@@ -1049,12 +1170,12 @@ public class PackWriter {
 		// bother examining those types here.
 		//
 		ObjectToPack[] list = new ObjectToPack[
-				  objectsLists[Constants.OBJ_TREE].size()
-				+ objectsLists[Constants.OBJ_BLOB].size()
+				  objectsLists[OBJ_TREE].size()
+				+ objectsLists[OBJ_BLOB].size()
 				+ edgeObjects.size()];
 		int cnt = 0;
-		cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_TREE);
-		cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_BLOB);
+		cnt = findObjectsNeedingDelta(list, cnt, OBJ_TREE);
+		cnt = findObjectsNeedingDelta(list, cnt, OBJ_BLOB);
 		if (cnt == 0)
 			return;
 		int nonEdgeCnt = cnt;
@@ -1080,7 +1201,9 @@ public class PackWriter {
 		AsyncObjectSizeQueue<ObjectToPack> sizeQueue = reader.getObjectSize(
 				Arrays.<ObjectToPack> asList(list).subList(0, cnt), false);
 		try {
-			final long limit = config.getBigFileThreshold();
+			final long limit = Math.min(
+					config.getBigFileThreshold(),
+					Integer.MAX_VALUE);
 			for (;;) {
 				try {
 					if (!sizeQueue.next())
@@ -1090,13 +1213,13 @@ public class PackWriter {
 					if (ignoreMissingUninteresting) {
 						ObjectToPack otp = sizeQueue.getCurrent();
 						if (otp != null && otp.isEdge()) {
-							otp.setDoNotDelta(true);
+							otp.setDoNotDelta();
 							continue;
 						}
 
 						otp = objectsMap.get(notFound.getObjectId());
 						if (otp != null && otp.isEdge()) {
-							otp.setDoNotDelta(true);
+							otp.setDoNotDelta();
 							continue;
 						}
 					}
@@ -1108,14 +1231,10 @@ public class PackWriter {
 					otp = objectsMap.get(sizeQueue.getObjectId());
 
 				long sz = sizeQueue.getSize();
-				if (limit <= sz || Integer.MAX_VALUE <= sz)
-					otp.setDoNotDelta(true); // too big, avoid costly files
-
-				else if (sz <= DeltaIndex.BLKSZ)
-					otp.setDoNotDelta(true); // too small, won't work
-
-				else
+				if (DeltaIndex.BLKSZ < sz && sz < limit)
 					otp.setWeight((int) sz);
+				else
+					otp.setDoNotDelta(); // too small, or too big
 				monitor.update(1);
 			}
 		} finally {
@@ -1167,9 +1286,7 @@ public class PackWriter {
 			return;
 
 		final long searchStart = System.currentTimeMillis();
-		beginPhase(PackingPhase.COMPRESSING, monitor, nonEdgeCnt);
 		searchForDeltas(monitor, list, cnt);
-		endPhase(monitor);
 		stats.deltaSearchNonEdgeObjects = nonEdgeCnt;
 		stats.timeCompressing = System.currentTimeMillis() - searchStart;
 
@@ -1190,6 +1307,17 @@ public class PackWriter {
 		return cnt;
 	}
 
+	private void reselectNonDelta(ObjectToPack otp) throws IOException {
+		otp.clearDeltaBase();
+		otp.clearReuseAsIs();
+		boolean old = reuseDeltas;
+		reuseDeltas = false;
+		reuseSupport.selectObjectRepresentation(this,
+				NullProgressMonitor.INSTANCE,
+				Collections.singleton(otp));
+		reuseDeltas = old;
+	}
+
 	private void searchForDeltas(final ProgressMonitor monitor,
 			final ObjectToPack[] list, final int cnt)
 			throws MissingObjectException, IncorrectObjectTypeException,
@@ -1197,70 +1325,58 @@ public class PackWriter {
 		int threads = config.getThreads();
 		if (threads == 0)
 			threads = Runtime.getRuntime().availableProcessors();
+		if (threads <= 1 || cnt <= config.getDeltaSearchWindowSize())
+			singleThreadDeltaSearch(monitor, list, cnt);
+		else
+			parallelDeltaSearch(monitor, list, cnt, threads);
+	}
 
-		if (threads <= 1 || cnt <= 2 * config.getDeltaSearchWindowSize()) {
-			DeltaCache dc = new DeltaCache(config);
-			DeltaWindow dw = new DeltaWindow(config, dc, reader);
-			dw.search(monitor, list, 0, cnt);
-			return;
+	private void singleThreadDeltaSearch(ProgressMonitor monitor,
+			ObjectToPack[] list, int cnt) throws IOException {
+		long totalWeight = 0;
+		for (int i = 0; i < cnt; i++) {
+			ObjectToPack o = list[i];
+			if (!o.isEdge() && !o.doNotAttemptDelta())
+				totalWeight += o.getWeight();
 		}
 
-		final DeltaCache dc = new ThreadSafeDeltaCache(config);
-		final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
+		long bytesPerUnit = 1;
+		while (DeltaTask.MAX_METER <= (totalWeight / bytesPerUnit))
+			bytesPerUnit <<= 10;
+		int cost = (int) (totalWeight / bytesPerUnit);
+		if (totalWeight % bytesPerUnit != 0)
+			cost++;
 
-		// Guess at the size of batch we want. Because we don't really
-		// have a way for a thread to steal work from another thread if
-		// it ends early, we over partition slightly so the work units
-		// are a bit smaller.
-		//
-		int estSize = cnt / (threads * 2);
-		if (estSize < 2 * config.getDeltaSearchWindowSize())
-			estSize = 2 * config.getDeltaSearchWindowSize();
-
-		final List<DeltaTask> myTasks = new ArrayList<DeltaTask>(threads * 2);
-		for (int i = 0; i < cnt;) {
-			final int start = i;
-			final int batchSize;
-
-			if (cnt - i < estSize) {
-				// If we don't have enough to fill the remaining block,
-				// schedule what is left over as a single block.
-				//
-				batchSize = cnt - i;
-			} else {
-				// Try to split the block at the end of a path.
-				//
-				int end = start + estSize;
-				while (end < cnt) {
-					ObjectToPack a = list[end - 1];
-					ObjectToPack b = list[end];
-					if (a.getPathHash() == b.getPathHash())
-						end++;
-					else
-						break;
-				}
-				batchSize = end - start;
-			}
-			i += batchSize;
-			myTasks.add(new DeltaTask(config, reader, dc, pm, batchSize, start, list));
-		}
-		pm.startWorkers(myTasks.size());
+		beginPhase(PackingPhase.COMPRESSING, monitor, cost);
+		new DeltaWindow(config, new DeltaCache(config), reader,
+				monitor, bytesPerUnit,
+				list, 0, cnt).search();
+		endPhase(monitor);
+	}
 
-		final Executor executor = config.getExecutor();
-		final List<Throwable> errors = Collections
-				.synchronizedList(new ArrayList<Throwable>());
+	private void parallelDeltaSearch(ProgressMonitor monitor,
+			ObjectToPack[] list, int cnt, int threads) throws IOException {
+		DeltaCache dc = new ThreadSafeDeltaCache(config);
+		ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
+		DeltaTask.Block taskBlock = new DeltaTask.Block(threads, config,
+				reader, dc, pm,
+				list, 0, cnt);
+		taskBlock.partitionTasks();
+		beginPhase(PackingPhase.COMPRESSING, monitor, taskBlock.cost());
+		pm.startWorkers(taskBlock.tasks.size());
+
+		Executor executor = config.getExecutor();
+		final List<Throwable> errors =
+				Collections.synchronizedList(new ArrayList<Throwable>(threads));
 		if (executor instanceof ExecutorService) {
 			// Caller supplied us a service, use it directly.
-			//
-			runTasks((ExecutorService) executor, pm, myTasks, errors);
-
+			runTasks((ExecutorService) executor, pm, taskBlock, errors);
 		} else if (executor == null) {
 			// Caller didn't give us a way to run the tasks, spawn up a
 			// temporary thread pool and make sure it tears down cleanly.
-			//
 			ExecutorService pool = Executors.newFixedThreadPool(threads);
 			try {
-				runTasks(pool, pm, myTasks, errors);
+				runTasks(pool, pm, taskBlock, errors);
 			} finally {
 				pool.shutdown();
 				for (;;) {
@@ -1277,8 +1393,7 @@ public class PackWriter {
 			// The caller gave us an executor, but it might not do
 			// asynchronous execution.  Wrap everything and hope it
 			// can schedule these for us.
-			//
-			for (final DeltaTask task : myTasks) {
+			for (final DeltaTask task : taskBlock.tasks) {
 				executor.execute(new Runnable() {
 					public void run() {
 						try {
@@ -1316,12 +1431,14 @@ public class PackWriter {
 			fail.initCause(err);
 			throw fail;
 		}
+		endPhase(monitor);
 	}
 
-	private void runTasks(ExecutorService pool, ThreadSafeProgressMonitor pm,
-			List<DeltaTask> tasks, List<Throwable> errors) throws IOException {
-		List<Future<?>> futures = new ArrayList<Future<?>>(tasks.size());
-		for (DeltaTask task : tasks)
+	private static void runTasks(ExecutorService pool,
+			ThreadSafeProgressMonitor pm,
+			DeltaTask.Block tb, List<Throwable> errors) throws IOException {
+		List<Future<?>> futures = new ArrayList<Future<?>>(tb.tasks.size());
+		for (DeltaTask task : tb.tasks)
 			futures.add(pool.submit(task));
 
 		try {
@@ -1342,10 +1459,10 @@ public class PackWriter {
 	}
 
 	private void writeObjects(PackOutputStream out) throws IOException {
-		writeObjects(out, objectsLists[Constants.OBJ_COMMIT]);
-		writeObjects(out, objectsLists[Constants.OBJ_TAG]);
-		writeObjects(out, objectsLists[Constants.OBJ_TREE]);
-		writeObjects(out, objectsLists[Constants.OBJ_BLOB]);
+		writeObjects(out, objectsLists[OBJ_COMMIT]);
+		writeObjects(out, objectsLists[OBJ_TAG]);
+		writeObjects(out, objectsLists[OBJ_TREE]);
+		writeObjects(out, objectsLists[OBJ_BLOB]);
 	}
 
 	private void writeObjects(PackOutputStream out, List<ObjectToPack> list)
@@ -1380,13 +1497,7 @@ public class PackWriter {
 			// (for example due to a concurrent repack) and a different base
 			// was chosen, forcing a cycle. Select something other than a
 			// delta, and write this object.
-			//
-			reuseDeltas = false;
-			otp.clearDeltaBase();
-			otp.clearReuseAsIs();
-			reuseSupport.selectObjectRepresentation(this,
-					NullProgressMonitor.INSTANCE,
-					Collections.singleton(otp));
+			reselectNonDelta(otp);
 		}
 		otp.markWantWrite();
 
@@ -1395,12 +1506,12 @@ public class PackWriter {
 			if (otp.isWritten())
 				return; // Delta chain cycle caused this to write already.
 
-			out.resetCRC32();
+			crc32.reset();
 			otp.setOffset(out.length());
 			try {
 				reuseSupport.copyObjectAsIs(out, otp, reuseValidate);
 				out.endObject();
-				otp.setCRC(out.getCRC32());
+				otp.setCRC((int) crc32.getValue());
 				typeStats.reusedObjects++;
 				if (otp.isDeltaRepresentation()) {
 					typeStats.reusedDeltas++;
@@ -1434,7 +1545,7 @@ public class PackWriter {
 		else
 			writeWholeObjectDeflate(out, otp);
 		out.endObject();
-		otp.setCRC(out.getCRC32());
+		otp.setCRC((int) crc32.getValue());
 	}
 
 	private void writeBase(PackOutputStream out, ObjectToPack base)
@@ -1448,7 +1559,7 @@ public class PackWriter {
 		final Deflater deflater = deflater();
 		final ObjectLoader ldr = reader.open(otp, otp.getType());
 
-		out.resetCRC32();
+		crc32.reset();
 		otp.setOffset(out.length());
 		out.writeHeader(otp, ldr.getSize());
 
@@ -1462,7 +1573,7 @@ public class PackWriter {
 			final ObjectToPack otp) throws IOException {
 		writeBase(out, otp.getDeltaBase());
 
-		out.resetCRC32();
+		crc32.reset();
 		otp.setOffset(out.length());
 
 		DeltaCache.Ref ref = otp.popCachedDelta();
@@ -1540,51 +1651,36 @@ public class PackWriter {
 		stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want));
 		stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have));
 
+		walker.setRetainBody(false);
+
+		canBuildBitmaps = config.isBuildBitmaps()
+				&& !shallowPack
+				&& have.isEmpty()
+				&& (excludeInPacks == null || excludeInPacks.length == 0);
+		if (!shallowPack && useBitmaps) {
+			BitmapIndex bitmapIndex = reader.getBitmapIndex();
+			if (bitmapIndex != null) {
+				PackWriterBitmapWalker bitmapWalker = new PackWriterBitmapWalker(
+						walker, bitmapIndex, countingMonitor);
+				findObjectsToPackUsingBitmaps(bitmapWalker, want, have);
+				endPhase(countingMonitor);
+				stats.timeCounting = System.currentTimeMillis() - countingStart;
+				return;
+			}
+		}
+
 		List<ObjectId> all = new ArrayList<ObjectId>(want.size() + have.size());
 		all.addAll(want);
 		all.addAll(have);
 
-		final Map<ObjectId, CachedPack> tipToPack = new HashMap<ObjectId, CachedPack>();
-		final RevFlag inCachedPack = walker.newFlag("inCachedPack"); //$NON-NLS-1$
 		final RevFlag include = walker.newFlag("include"); //$NON-NLS-1$
 		final RevFlag added = walker.newFlag("added"); //$NON-NLS-1$
 
-		final RevFlagSet keepOnRestart = new RevFlagSet();
-		keepOnRestart.add(inCachedPack);
-
-		walker.setRetainBody(false);
 		walker.carry(include);
 
 		int haveEst = have.size();
 		if (have.isEmpty()) {
 			walker.sort(RevSort.COMMIT_TIME_DESC);
-			if (useCachedPacks && reuseSupport != null) {
-				Set<ObjectId> need = new HashSet<ObjectId>(want);
-				List<CachedPack> shortCircuit = new LinkedList<CachedPack>();
-
-				for (CachedPack pack : reuseSupport.getCachedPacks()) {
-					if (need.containsAll(pack.getTips())) {
-						need.removeAll(pack.getTips());
-						shortCircuit.add(pack);
-					}
-
-					for (ObjectId id : pack.getTips()) {
-						tipToPack.put(id, pack);
-						all.add(id);
-					}
-				}
-
-				if (need.isEmpty() && !shortCircuit.isEmpty()) {
-					cachedPacks.addAll(shortCircuit);
-					for (CachedPack pack : shortCircuit)
-						countingMonitor.update((int) pack.getObjectCount());
-					endPhase(countingMonitor);
-					stats.timeCounting = System.currentTimeMillis() - countingStart;
-					return;
-				}
-
-				haveEst += tipToPack.size();
-			}
 		} else {
 			walker.sort(RevSort.TOPO);
 			if (thin)
@@ -1602,10 +1698,6 @@ public class PackWriter {
 					RevObject o = q.next();
 					if (o == null)
 						break;
-
-					if (tipToPack.containsKey(o))
-						o.add(inCachedPack);
-
 					if (have.contains(o))
 						haveObjs.add(o);
 					if (want.contains(o)) {
@@ -1661,20 +1753,6 @@ public class PackWriter {
 		while ((c = walker.next()) != null) {
 			if (exclude(c))
 				continue;
-			if (c.has(inCachedPack)) {
-				CachedPack pack = tipToPack.get(c);
-				if (includesAllTips(pack, include, walker)) {
-					useCachedPack(walker, keepOnRestart, //
-							wantObjs, haveObjs, pack);
-					commits = new BlockList<RevCommit>();
-
-					endPhase(countingMonitor);
-					beginPhase(PackingPhase.COUNTING, countingMonitor,
-							ProgressMonitor.UNKNOWN);
-					continue;
-				}
-			}
-
 			if (c.has(RevFlag.UNINTERESTING)) {
 				if (baseTrees.size() <= maxBases)
 					baseTrees.add(c.getTree());
@@ -1761,6 +1839,34 @@ public class PackWriter {
 		stats.timeCounting = System.currentTimeMillis() - countingStart;
 	}
 
+	private void findObjectsToPackUsingBitmaps(
+			PackWriterBitmapWalker bitmapWalker, Set<? extends ObjectId> want,
+			Set<? extends ObjectId> have)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException {
+		BitmapBuilder haveBitmap = bitmapWalker.findObjects(have, null);
+		bitmapWalker.reset();
+		BitmapBuilder wantBitmap = bitmapWalker.findObjects(want, haveBitmap);
+		BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap);
+
+		if (useCachedPacks && reuseSupport != null
+				&& (excludeInPacks == null || excludeInPacks.length == 0))
+			cachedPacks.addAll(
+					reuseSupport.getCachedPacksAndUpdate(needBitmap));
+
+		for (BitmapObject obj : needBitmap) {
+			ObjectId objectId = obj.getObjectId();
+			if (exclude(objectId)) {
+				needBitmap.remove(objectId);
+				continue;
+			}
+			addObject(objectId, obj.getType(), 0);
+		}
+
+		if (thin)
+			haveObjects = haveBitmap;
+	}
+
 	private static void pruneEdgesFromObjectList(List<ObjectToPack> list) {
 		final int size = list.size();
 		int src = 0;
@@ -1779,34 +1885,6 @@ public class PackWriter {
 			list.remove(list.size() - 1);
 	}
 
-	private void useCachedPack(ObjectWalk walker, RevFlagSet keepOnRestart,
-			List<RevObject> wantObj, List<RevObject> baseObj, CachedPack pack)
-			throws MissingObjectException, IncorrectObjectTypeException,
-			IOException {
-		cachedPacks.add(pack);
-		for (ObjectId id : pack.getTips())
-			baseObj.add(walker.lookupOrNull(id));
-
-		setThin(true);
-		walker.resetRetain(keepOnRestart);
-		walker.sort(RevSort.TOPO);
-		walker.sort(RevSort.BOUNDARY, true);
-
-		for (RevObject id : wantObj)
-			walker.markStart(id);
-		for (RevObject id : baseObj)
-			walker.markUninteresting(id);
-	}
-
-	private static boolean includesAllTips(CachedPack pack, RevFlag include,
-			ObjectWalk walker) {
-		for (ObjectId id : pack.getTips()) {
-			if (!walker.lookupOrNull(id).has(include))
-				return false;
-		}
-		return true;
-	}
-
 	/**
 	 * Include one object to the output file.
 	 * <p>
@@ -1826,13 +1904,18 @@ public class PackWriter {
 	}
 
 	private void addObject(final RevObject object, final int pathHashCode) {
+		addObject(object, object.getType(), pathHashCode);
+	}
+
+	private void addObject(
+			final AnyObjectId src, final int type, final int pathHashCode) {
 		final ObjectToPack otp;
 		if (reuseSupport != null)
-			otp = reuseSupport.newObjectToPack(object);
+			otp = reuseSupport.newObjectToPack(src, type);
 		else
-			otp = new ObjectToPack(object);
+			otp = new ObjectToPack(src, type);
 		otp.setPathHash(pathHashCode);
-		objectsLists[object.getType()].add(otp);
+		objectsLists[type].add(otp);
 		objectsMap.add(otp);
 	}
 
@@ -1887,7 +1970,7 @@ public class PackWriter {
 			if (ptr != null && !ptr.isEdge()) {
 				otp.setDeltaBase(ptr);
 				otp.setReuseAsIs();
-			} else if (thin && ptr != null && ptr.isEdge()) {
+			} else if (thin && have(ptr, baseId)) {
 				otp.setDeltaBase(baseId);
 				otp.setReuseAsIs();
 			} else {
@@ -1911,23 +1994,83 @@ public class PackWriter {
 			otp.clearReuseAsIs();
 		}
 
-		otp.setDeltaAttempted(next.wasDeltaAttempted());
+		otp.setDeltaAttempted(reuseDeltas & next.wasDeltaAttempted());
 		otp.select(next);
 	}
 
+	private final boolean have(ObjectToPack ptr, AnyObjectId objectId) {
+		return (ptr != null && ptr.isEdge())
+				|| (haveObjects != null && haveObjects.contains(objectId));
+	}
+
+	/**
+	 * Prepares the bitmaps to be written to the pack index. Bitmaps can be used
+	 * to speed up fetches and clones by storing the entire object graph at
+	 * selected commits.
+	 *
+	 * This method can only be invoked after
+	 * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} has
+	 * been invoked and completed successfully. Writing a corresponding bitmap
+	 * index is an optional feature that not all pack users may require.
+	 *
+	 * @param pm
+	 *            progress monitor to report bitmap building work.
+	 * @return whether a bitmap index may be written.
+	 * @throws IOException
+	 *             when some I/O problem occur during reading objects.
+	 */
+	public boolean prepareBitmapIndex(ProgressMonitor pm) throws IOException {
+		if (!canBuildBitmaps || getObjectCount() > Integer.MAX_VALUE
+				|| !cachedPacks.isEmpty())
+			return false;
+
+		if (pm == null)
+			pm = NullProgressMonitor.INSTANCE;
+
+		writeBitmaps = new PackBitmapIndexBuilder(sortByName());
+		PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
+				reader, writeBitmaps, pm, stats.interestingObjects);
+
+		int numCommits = objectsLists[OBJ_COMMIT].size();
+		Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits =
+				bitmapPreparer.doCommitSelection(numCommits);
+
+		beginPhase(PackingPhase.BUILDING_BITMAPS, pm, selectedCommits.size());
+
+		PackWriterBitmapWalker walker = bitmapPreparer.newBitmapWalker();
+		AnyObjectId last = null;
+		for (PackWriterBitmapPreparer.BitmapCommit cmit : selectedCommits) {
+			if (cmit.isReuseWalker())
+				walker.reset();
+			else
+				walker = bitmapPreparer.newBitmapWalker();
+
+			BitmapBuilder bitmap = walker.findObjects(
+					Collections.singleton(cmit), null);
+
+			if (last != null && cmit.isReuseWalker() && !bitmap.contains(last))
+				throw new IllegalStateException(MessageFormat.format(
+						JGitText.get().bitmapMissingObject, cmit.name(),
+						last.name()));
+			last = cmit;
+			writeBitmaps.addBitmap(cmit, bitmap.build(), cmit.getFlags());
+
+			pm.update(1);
+		}
+
+		endPhase(pm);
+		return true;
+	}
+
 	private boolean reuseDeltaFor(ObjectToPack otp) {
-		switch (otp.getType()) {
-		case Constants.OBJ_COMMIT:
-			return reuseDeltaCommits;
-		case Constants.OBJ_TREE:
-			return true;
-		case Constants.OBJ_BLOB:
+		int type = otp.getType();
+		if ((type & 2) != 0) // OBJ_TREE(2) or OBJ_BLOB(3)
 			return true;
-		case Constants.OBJ_TAG:
+		if (type == OBJ_COMMIT)
+			return reuseDeltaCommits;
+		if (type == OBJ_TAG)
 			return false;
-		default:
-			return true;
-		}
+		return true;
 	}
 
 	/** Summary of how PackWriter created the pack. */
@@ -2038,10 +2181,10 @@ public class PackWriter {
 
 		{
 			objectTypes = new ObjectType[5];
-			objectTypes[Constants.OBJ_COMMIT] = new ObjectType();
-			objectTypes[Constants.OBJ_TREE] = new ObjectType();
-			objectTypes[Constants.OBJ_BLOB] = new ObjectType();
-			objectTypes[Constants.OBJ_TAG] = new ObjectType();
+			objectTypes[OBJ_COMMIT] = new ObjectType();
+			objectTypes[OBJ_TREE] = new ObjectType();
+			objectTypes[OBJ_BLOB] = new ObjectType();
+			objectTypes[OBJ_TAG] = new ObjectType();
 		}
 
 		/**
@@ -2262,10 +2405,10 @@ public class PackWriter {
 
 		State snapshot() {
 			long objCnt = 0;
-			objCnt += objectsLists[Constants.OBJ_COMMIT].size();
-			objCnt += objectsLists[Constants.OBJ_TREE].size();
-			objCnt += objectsLists[Constants.OBJ_BLOB].size();
-			objCnt += objectsLists[Constants.OBJ_TAG].size();
+			objCnt += objectsLists[OBJ_COMMIT].size();
+			objCnt += objectsLists[OBJ_TREE].size();
+			objCnt += objectsLists[OBJ_BLOB].size();
+			objCnt += objectsLists[OBJ_TAG].size();
 			// Exclude CachedPacks.
 
 			long bytesUsed = OBJECT_TO_PACK_SIZE * objCnt;
@@ -2291,7 +2434,10 @@ public class PackWriter {
 		COMPRESSING,
 
 		/** Writing objects phase. */
-		WRITING;
+		WRITING,
+
+		/** Building bitmaps phase. */
+		BUILDING_BITMAPS;
 	}
 
 	/** Summary of the current state of a PackWriter. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
new file mode 100644
index 0000000..4eabdb5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.pack;
+
+import static org.eclipse.jgit.internal.storage.file.PackBitmapIndex.FLAG_REUSE;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javaewah.EWAHCompressedBitmap;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexRemapper;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.BlockList;
+
+/** Helper class for the PackWriter to select commits for pack index bitmaps. */
+class PackWriterBitmapPreparer {
+
+	private static final Comparator<BitmapBuilder> BUILDER_BY_CARDINALITY_DSC =
+			new Comparator<BitmapBuilder>() {
+		public int compare(BitmapBuilder a, BitmapBuilder b) {
+			return Integer.signum(b.cardinality() - a.cardinality());
+		}
+	};
+
+	private final ObjectReader reader;
+	private final ProgressMonitor pm;
+	private final Set<? extends ObjectId> want;
+	private final PackBitmapIndexBuilder writeBitmaps;
+	private final BitmapIndexImpl commitBitmapIndex;
+	private final PackBitmapIndexRemapper bitmapRemapper;
+	private final BitmapIndexImpl bitmapIndex;
+	private final int minCommits = 100;
+	private final int maxCommits = 5000;
+
+	PackWriterBitmapPreparer(ObjectReader reader,
+			PackBitmapIndexBuilder writeBitmaps, ProgressMonitor pm,
+			Set<? extends ObjectId> want) throws IOException {
+		this.reader = reader;
+		this.writeBitmaps = writeBitmaps;
+		this.pm = pm;
+		this.want = want;
+		this.commitBitmapIndex = new BitmapIndexImpl(writeBitmaps);
+		this.bitmapRemapper = PackBitmapIndexRemapper.newPackBitmapIndex(
+				reader.getBitmapIndex(), writeBitmaps);
+		this.bitmapIndex = new BitmapIndexImpl(bitmapRemapper);
+	}
+
+	Collection<BitmapCommit> doCommitSelection(int expectedNumCommits)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException {
+		pm.beginTask(JGitText.get().selectingCommits, ProgressMonitor.UNKNOWN);
+		RevWalk rw = new RevWalk(reader);
+		WalkResult result = findPaths(rw, expectedNumCommits);
+		pm.endTask();
+
+		int totCommits = result.commitsByOldest.length - result.commitStartPos;
+		BlockList<BitmapCommit> selections = new BlockList<BitmapCommit>(
+				totCommits / minCommits + 1);
+		for (BitmapCommit reuse : result.reuse)
+			selections.add(reuse);
+
+		if (totCommits == 0) {
+			for (AnyObjectId id : result.peeledWant)
+				selections.add(new BitmapCommit(id, false, 0));
+			return selections;
+		}
+
+		pm.beginTask(JGitText.get().selectingCommits, totCommits);
+
+		for (BitmapBuilder bitmapableCommits : result.paths) {
+			int cardinality = bitmapableCommits.cardinality();
+
+			List<List<BitmapCommit>> running = new ArrayList<
+					List<BitmapCommit>>();
+
+			// Insert bitmaps at the offsets suggested by the
+			// nextSelectionDistance() heuristic.
+			int index = -1;
+			int nextIn = nextSelectionDistance(0, cardinality);
+			int nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
+			boolean mustPick = nextIn == 0;
+			for (RevCommit c : result) {
+				if (!bitmapableCommits.contains(c))
+					continue;
+
+				index++;
+				nextIn--;
+				pm.update(1);
+
+				// Always pick the items in want and prefer merge commits.
+				if (result.peeledWant.remove(c)) {
+					if (nextIn > 0)
+						nextFlg = 0;
+				} else if (!mustPick && ((nextIn > 0)
+						|| (c.getParentCount() <= 1 && nextIn > -minCommits))) {
+					continue;
+				}
+
+				int flags = nextFlg;
+				nextIn = nextSelectionDistance(index, cardinality);
+				nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
+				mustPick = nextIn == 0;
+
+				BitmapBuilder fullBitmap = commitBitmapIndex.newBitmapBuilder();
+				rw.reset();
+				rw.markStart(c);
+				for (AnyObjectId objectId : result.reuse)
+					rw.markUninteresting(rw.parseCommit(objectId));
+				rw.setRevFilter(
+						PackWriterBitmapWalker.newRevFilter(null, fullBitmap));
+
+				while (rw.next() != null) {
+					// Work is done in the RevFilter.
+				}
+
+				List<List<BitmapCommit>> matches = new ArrayList<
+						List<BitmapCommit>>();
+				for (List<BitmapCommit> list : running) {
+					BitmapCommit last = list.get(list.size() - 1);
+					if (fullBitmap.contains(last))
+						matches.add(list);
+				}
+
+				List<BitmapCommit> match;
+				if (matches.isEmpty()) {
+					match = new ArrayList<BitmapCommit>();
+					running.add(match);
+				} else {
+					match = matches.get(0);
+					// Append to longest
+					for (List<BitmapCommit> list : matches) {
+						if (list.size() > match.size())
+							match = list;
+					}
+				}
+				match.add(new BitmapCommit(c, !match.isEmpty(), flags));
+				writeBitmaps.addBitmap(c, fullBitmap, 0);
+			}
+
+			for (List<BitmapCommit> list : running)
+				selections.addAll(list);
+		}
+		writeBitmaps.clearBitmaps(); // Remove the temporary commit bitmaps.
+
+		// Add the remaining peeledWant
+		for (AnyObjectId remainingWant : result.peeledWant)
+			selections.add(new BitmapCommit(remainingWant, false, 0));
+
+		pm.endTask();
+		return selections;
+	}
+
+	private WalkResult findPaths(RevWalk rw, int expectedNumCommits)
+			throws MissingObjectException, IOException {
+		BitmapBuilder reuseBitmap = commitBitmapIndex.newBitmapBuilder();
+		List<BitmapCommit> reuse = new ArrayList<BitmapCommit>();
+		for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
+			if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE)
+				continue;
+
+			RevObject ro = rw.peel(rw.parseAny(entry));
+			if (ro instanceof RevCommit) {
+				RevCommit rc = (RevCommit) ro;
+				reuse.add(new BitmapCommit(rc, false, entry.getFlags()));
+				rw.markUninteresting(rc);
+
+				EWAHCompressedBitmap bitmap = bitmapRemapper.ofObjectType(
+						bitmapRemapper.getBitmap(rc), Constants.OBJ_COMMIT);
+				writeBitmaps.addBitmap(rc, bitmap, 0);
+				reuseBitmap.add(rc, Constants.OBJ_COMMIT);
+			}
+		}
+		writeBitmaps.clearBitmaps(); // Remove temporary bitmaps
+
+		// Do a RevWalk by commit time descending. Keep track of all the paths
+		// from the wants.
+		List<BitmapBuilder> paths = new ArrayList<BitmapBuilder>(want.size());
+		Set<RevCommit> peeledWant = new HashSet<RevCommit>(want.size());
+		for (AnyObjectId objectId : want) {
+			RevObject ro = rw.peel(rw.parseAny(objectId));
+			if (ro instanceof RevCommit && !reuseBitmap.contains(ro)) {
+				RevCommit rc = (RevCommit) ro;
+				peeledWant.add(rc);
+				rw.markStart(rc);
+
+				BitmapBuilder bitmap = commitBitmapIndex.newBitmapBuilder();
+				bitmap.or(reuseBitmap);
+				bitmap.add(rc, Constants.OBJ_COMMIT);
+				paths.add(bitmap);
+			}
+		}
+
+		// Update the paths from the wants and create a list of commits in
+		// reverse iteration order.
+		RevCommit[] commits = new RevCommit[expectedNumCommits];
+		int pos = commits.length;
+		RevCommit rc;
+		while ((rc = rw.next()) != null) {
+			commits[--pos] = rc;
+			for (BitmapBuilder path : paths) {
+				if (path.contains(rc)) {
+					for (RevCommit c : rc.getParents())
+						path.add(c, Constants.OBJ_COMMIT);
+				}
+			}
+
+			pm.update(1);
+		}
+
+		// Remove the reused bitmaps from the paths
+		if (!reuse.isEmpty())
+			for (BitmapBuilder bitmap : paths)
+				bitmap.andNot(reuseBitmap);
+
+		// Sort the paths
+		List<BitmapBuilder> distinctPaths = new ArrayList<BitmapBuilder>(paths.size());
+		while (!paths.isEmpty()) {
+			Collections.sort(paths, BUILDER_BY_CARDINALITY_DSC);
+			BitmapBuilder largest = paths.remove(0);
+			distinctPaths.add(largest);
+
+			// Update the remaining paths, by removing the objects from
+			// the path that was just added.
+			for (int i = paths.size() - 1; i >= 0; i--)
+				paths.get(i).andNot(largest);
+		}
+
+		return new WalkResult(peeledWant, commits, pos, distinctPaths, reuse);
+	}
+
+	private int nextSelectionDistance(int idx, int cardinality) {
+		if (idx > cardinality)
+			throw new IllegalArgumentException();
+		int idxFromStart = cardinality - idx;
+		int mustRegionEnd = 100;
+		if (idxFromStart <= mustRegionEnd)
+			return 0;
+
+		// Commits more toward the start will have more bitmaps.
+		int minRegionEnd = 20000;
+		if (idxFromStart <= minRegionEnd)
+			return Math.min(idxFromStart - mustRegionEnd, minCommits);
+
+		// Commits more toward the end will have fewer.
+		int next = Math.min(idxFromStart - minRegionEnd, maxCommits);
+		return Math.max(next, minCommits);
+	}
+
+	PackWriterBitmapWalker newBitmapWalker() {
+		return new PackWriterBitmapWalker(
+				new ObjectWalk(reader), bitmapIndex, null);
+	}
+
+	static final class BitmapCommit extends ObjectId {
+		private final boolean reuseWalker;
+		private final int flags;
+
+		private BitmapCommit(
+				AnyObjectId objectId, boolean reuseWalker, int flags) {
+			super(objectId);
+			this.reuseWalker = reuseWalker;
+			this.flags = flags;
+		}
+
+		boolean isReuseWalker() {
+			return reuseWalker;
+		}
+
+		int getFlags() {
+			return flags;
+		}
+	}
+
+	private static final class WalkResult implements Iterable<RevCommit> {
+		private final Set<? extends ObjectId> peeledWant;
+		private final RevCommit[] commitsByOldest;
+		private final int commitStartPos;
+		private final List<BitmapBuilder> paths;
+		private final Iterable<BitmapCommit> reuse;
+
+		private WalkResult(Set<? extends ObjectId> peeledWant,
+				RevCommit[] commitsByOldest, int commitStartPos,
+				List<BitmapBuilder> paths, Iterable<BitmapCommit> reuse) {
+			this.peeledWant = peeledWant;
+			this.commitsByOldest = commitsByOldest;
+			this.commitStartPos = commitStartPos;
+			this.paths = paths;
+			this.reuse = reuse;
+		}
+
+		public Iterator<RevCommit> iterator() {
+			return new Iterator<RevCommit>() {
+				int pos = commitStartPos;
+
+				public boolean hasNext() {
+					return pos < commitsByOldest.length;
+				}
+
+				public RevCommit next() {
+					return commitsByOldest[pos++];
+				}
+
+				public void remove() {
+					throw new UnsupportedOperationException();
+				}
+			};
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
new file mode 100644
index 0000000..2e9ce1d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.pack;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+
+/** Helper class for PackWriter to do ObjectWalks with pack index bitmaps. */
+final class PackWriterBitmapWalker {
+
+	private final ObjectWalk walker;
+
+	private final BitmapIndex bitmapIndex;
+
+	private final ProgressMonitor pm;
+
+	PackWriterBitmapWalker(
+			ObjectWalk walker, BitmapIndex bitmapIndex, ProgressMonitor pm) {
+		this.walker = walker;
+		this.bitmapIndex = bitmapIndex;
+		this.pm = (pm == null) ? NullProgressMonitor.INSTANCE : pm;
+	}
+
+	BitmapBuilder findObjects(Set<? extends ObjectId> start, BitmapBuilder seen)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException {
+		final BitmapBuilder bitmapResult = bitmapIndex.newBitmapBuilder();
+
+		for (ObjectId obj : start) {
+			Bitmap bitmap = bitmapIndex.getBitmap(obj);
+			if (bitmap != null)
+				bitmapResult.or(bitmap);
+		}
+
+		boolean marked = false;
+		for (ObjectId obj : start) {
+			if (!bitmapResult.contains(obj)) {
+				walker.markStart(walker.parseAny(obj));
+				marked = true;
+			}
+		}
+
+		if (marked) {
+			walker.setRevFilter(newRevFilter(seen, bitmapResult));
+
+			while (walker.next() != null) {
+				// Iterate through all of the commits. The BitmapRevFilter does
+				// the work.
+				pm.update(1);
+			}
+
+			RevObject ro;
+			while ((ro = walker.nextObject()) != null) {
+				bitmapResult.add(ro, ro.getType());
+				pm.update(1);
+			}
+		}
+
+		return bitmapResult;
+	}
+
+	void reset() {
+		walker.reset();
+	}
+
+	static RevFilter newRevFilter(
+			final BitmapBuilder seen, final BitmapBuilder bitmapResult) {
+		if (seen != null) {
+			return new BitmapRevFilter() {
+				protected boolean load(RevCommit cmit) {
+					if (seen.contains(cmit))
+						return false;
+					return bitmapResult.add(cmit, Constants.OBJ_COMMIT);
+				}
+			};
+		}
+		return new BitmapRevFilter() {
+			@Override
+			protected boolean load(RevCommit cmit) {
+				return bitmapResult.add(cmit, Constants.OBJ_COMMIT);
+			}
+		};
+	}
+
+	static abstract class BitmapRevFilter extends RevFilter {
+		protected abstract boolean load(RevCommit cmit);
+
+		@Override
+		public final boolean include(RevWalk walker, RevCommit cmit) {
+			if (load(cmit))
+				return true;
+			for (RevCommit p : cmit.getParents())
+				p.add(RevFlag.SEEN);
+			return false;
+		}
+
+		@Override
+		public final RevFilter clone() {
+			return this;
+		}
+
+		@Override
+		public final boolean requiresCommitBody() {
+			return false;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/StoredObjectRepresentation.java
similarity index 98%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/StoredObjectRepresentation.java
index 543bc2f..c48b1bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/StoredObjectRepresentation.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/StoredObjectRepresentation.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import org.eclipse.jgit.lib.ObjectId;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ThreadSafeDeltaCache.java
similarity index 96%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ThreadSafeDeltaCache.java
index 2492a05..eb9cb38 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/ThreadSafeDeltaCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ThreadSafeDeltaCache.java
@@ -41,10 +41,12 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.pack;
+package org.eclipse.jgit.internal.storage.pack;
 
 import java.util.concurrent.locks.ReentrantLock;
 
+import org.eclipse.jgit.storage.pack.PackConfig;
+
 class ThreadSafeDeltaCache extends DeltaCache {
 	private final ReentrantLock lock;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/package-info.java
new file mode 100644
index 0000000..2474e47
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Reading/writing Git pack files.
+ */
+package org.eclipse.jgit.internal.storage.pack;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index e408c79..9db4a61 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -57,7 +57,7 @@ import org.eclipse.jgit.util.NB;
  * with this instance can alter at any time, if this instance is modified to
  * represent a different object name.
  */
-public abstract class AnyObjectId implements Comparable<Object> {
+public abstract class AnyObjectId implements Comparable<AnyObjectId> {
 
 	/**
 	 * Compare to object identifier byte sequences for equality.
@@ -184,10 +184,6 @@ public abstract class AnyObjectId implements Comparable<Object> {
 		return NB.compareUInt32(w5, other.w5);
 	}
 
-	public final int compareTo(final Object other) {
-		return compareTo(((AnyObjectId) other));
-	}
-
 	/**
 	 * Compare this ObjectId to a network-byte-order ObjectId.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index a0f4418..28d9059 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -64,9 +64,9 @@ import java.util.List;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.IO;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
index 98c2647..b369d0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
@@ -53,9 +53,11 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.transport.ReceiveCommand;
 
@@ -242,41 +244,119 @@ public class BatchRefUpdate {
 	 * @param walk
 	 *            a RevWalk to parse tags in case the storage system wants to
 	 *            store them pre-peeled, a common performance optimization.
-	 * @param update
+	 * @param monitor
 	 *            progress monitor to receive update status on.
 	 * @throws IOException
 	 *             the database is unable to accept the update. Individual
 	 *             command status must be tested to determine if there is a
 	 *             partial failure, or a total failure.
 	 */
-	public void execute(RevWalk walk, ProgressMonitor update)
+	public void execute(RevWalk walk, ProgressMonitor monitor)
 			throws IOException {
-		update.beginTask(JGitText.get().updatingReferences, commands.size());
+		monitor.beginTask(JGitText.get().updatingReferences, commands.size());
+		List<ReceiveCommand> commands2 = new ArrayList<ReceiveCommand>(
+				commands.size());
+		List<String> namesToCheck = new ArrayList<String>(commands.size());
+		// First delete refs. This may free the name space for some of the
+		// updates.
 		for (ReceiveCommand cmd : commands) {
 			try {
-				update.update(1);
-
 				if (cmd.getResult() == NOT_ATTEMPTED) {
 					cmd.updateType(walk);
-					RefUpdate ru = newUpdate(cmd);
 					switch (cmd.getType()) {
-					case DELETE:
-						cmd.setResult(ru.delete(walk));
-						continue;
-
 					case CREATE:
+						namesToCheck.add(cmd.getRefName());
+						commands2.add(cmd);
+						break;
 					case UPDATE:
 					case UPDATE_NONFASTFORWARD:
-						cmd.setResult(ru.update(walk));
-						continue;
+						commands2.add(cmd);
+						break;
+					case DELETE:
+						RefUpdate rud = newUpdate(cmd);
+						monitor.update(1);
+						cmd.setResult(rud.delete(walk));
 					}
 				}
 			} catch (IOException err) {
-				cmd.setResult(REJECTED_OTHER_REASON, MessageFormat.format(
-						JGitText.get().lockError, err.getMessage()));
+				cmd.setResult(
+						REJECTED_OTHER_REASON,
+						MessageFormat.format(JGitText.get().lockError,
+								err.getMessage()));
+			}
+		}
+		if (!commands2.isEmpty()) {
+			// What part of the name space is already taken
+			Collection<String> takenNames = new HashSet<String>(refdb.getRefs(
+					RefDatabase.ALL).keySet());
+			Collection<String> takenPrefixes = getTakenPrefixes(takenNames);
+
+			// Now to the update that may require more room in the name space
+			for (ReceiveCommand cmd : commands2) {
+				try {
+					if (cmd.getResult() == NOT_ATTEMPTED) {
+						cmd.updateType(walk);
+						RefUpdate ru = newUpdate(cmd);
+						SWITCH: switch (cmd.getType()) {
+						case DELETE:
+							// Performed in the first phase
+							break;
+						case UPDATE:
+						case UPDATE_NONFASTFORWARD:
+							RefUpdate ruu = newUpdate(cmd);
+							cmd.setResult(ruu.update(walk));
+							break;
+						case CREATE:
+							for (String prefix : getPrefixes(cmd.getRefName())) {
+								if (takenNames.contains(prefix)) {
+									cmd.setResult(Result.LOCK_FAILURE);
+									break SWITCH;
+								}
+							}
+							if (takenPrefixes.contains(cmd.getRefName())) {
+								cmd.setResult(Result.LOCK_FAILURE);
+								break SWITCH;
+							}
+							ru.setCheckConflicting(false);
+							addRefToPrefixes(takenPrefixes, cmd.getRefName());
+							takenNames.add(cmd.getRefName());
+							cmd.setResult(ru.update(walk));
+						}
+					}
+				} catch (IOException err) {
+					cmd.setResult(REJECTED_OTHER_REASON, MessageFormat.format(
+							JGitText.get().lockError, err.getMessage()));
+				} finally {
+					monitor.update(1);
+				}
 			}
 		}
-		update.endTask();
+		monitor.endTask();
+	}
+
+	private static Collection<String> getTakenPrefixes(
+			final Collection<String> names) {
+		Collection<String> ref = new HashSet<String>();
+		for (String name : names)
+			ref.addAll(getPrefixes(name));
+		return ref;
+	}
+
+	private static void addRefToPrefixes(Collection<String> prefixes,
+			String name) {
+		for (String prefix : getPrefixes(name)) {
+			prefixes.add(prefix);
+		}
+	}
+
+	static Collection<String> getPrefixes(String s) {
+		Collection<String> ret = new HashSet<String>();
+		int p1 = s.indexOf('/');
+		while (p1 > 0) {
+			ret.add(s.substring(0, p1));
+			p1 = s.indexOf('/', p1 + 1);
+		}
+		return ret;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
new file mode 100644
index 0000000..3c0e2c1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2012, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lib;
+
+import java.util.Iterator;
+
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
+
+/**
+ * A compressed bitmap representation of the entire object graph.
+ *
+ * @since 3.0
+ */
+public interface BitmapIndex {
+	/**
+	 * Get the bitmap for the id. The returned bitmap is immutable and the
+	 * bitwise operations return the result of the operation in a new Bitmap.
+	 *
+	 * @param objectId
+	 *            the object ID
+	 * @return the Bitmap for the objectId or null, if one does not exist.
+	 */
+	Bitmap getBitmap(AnyObjectId objectId);
+
+	/** @return a new {@code BitmapBuilder} based on the values in the index. */
+	BitmapBuilder newBitmapBuilder();
+
+	/**
+	 * A bitmap representation of ObjectIds that can be iterated to return the
+	 * underlying {@code ObjectId}s or operated on with other {@code Bitmap}s.
+	 */
+	public interface Bitmap extends Iterable<BitmapObject> {
+		/**
+		 * Bitwise-OR the current bitmap with the value from the other
+		 * bitmap.
+		 *
+		 * @param other
+		 *            the other bitmap
+		 * @return a bitmap that is the bitwise-OR.
+		 */
+		Bitmap or(Bitmap other);
+
+		/**
+		 * Bitwise-AND-NOT the current bitmap with the value from the other
+		 * bitmap.
+		 *
+		 * @param other
+		 *            the other bitmap
+		 * @return a bitmap that is the bitwise-AND-NOT.
+		 */
+		Bitmap andNot(Bitmap other);
+
+		/**
+		 * Bitwise-XOR the current bitmap with the value from the other
+		 * bitmap.
+		 *
+		 * @param other
+		 *            the other bitmap
+		 * @return a bitmap that is the bitwise-XOR.
+		 */
+		Bitmap xor(Bitmap other);
+
+		/**
+		 * Returns an iterator over a set of elements of type BitmapObject. The
+		 * BitmapObject instance is reused across calls to
+		 * {@link Iterator#next()} for performance reasons.
+		 *
+		 * @return an Iterator.
+		 */
+		Iterator<BitmapObject> iterator();
+	}
+
+	/**
+	 * A builder for a bitmap. The bitwise operations update the builder and
+	 * return a reference to the current builder.
+	 */
+	public interface BitmapBuilder extends Bitmap {
+		/**
+		 * Adds the id and the existing bitmap for the id, if one exists, to the
+		 * bitmap.
+		 *
+		 * @param objectId
+		 *            the object ID
+		 * @param type
+		 *            the Git object type. See {@link Constants}.
+		 * @return true if the value was not contained or able to be loaded.
+		 */
+		boolean add(AnyObjectId objectId, int type);
+
+		/**
+		 * Whether the bitmap has the id set.
+		 *
+		 * @param objectId
+		 *            the object ID
+		 * @return whether the bitmap currently contains the object ID
+		 */
+		boolean contains(AnyObjectId objectId);
+
+		/**
+		 * Remove the id from the bitmap.
+		 *
+		 * @param objectId
+		 *            the object ID
+		 */
+		void remove(AnyObjectId objectId);
+
+		/**
+		 * Bitwise-OR the current bitmap with the value from the other bitmap.
+		 *
+		 * @param other
+		 *            the other bitmap
+		 * @return the current builder.
+		 */
+		BitmapBuilder or(Bitmap other);
+
+		/**
+		 * Bitwise-AND-NOT the current bitmap with the value from the other
+		 * bitmap.
+		 *
+		 * @param other
+		 *            the other bitmap
+		 * @return the current builder.
+		 */
+		BitmapBuilder andNot(Bitmap other);
+
+		/**
+		 * Bitwise-XOR the current bitmap with the value from the other bitmap.
+		 *
+		 * @param other
+		 *            the other bitmap
+		 * @return the current builder.
+		 */
+		BitmapBuilder xor(Bitmap other);
+
+		/** @return the fully built immutable bitmap */
+		Bitmap build();
+
+		/**
+		 * Determines if the entire bitmap index is contained in the bitmap. If
+		 * it is, the matching bits are removed from the bitmap and true is
+		 * returned. If the bitmap index is null, false is returned.
+		 *
+		 * @param bitmapIndex
+		 *            the bitmap index to check if it is completely contained
+		 *            inside of the current bitmap.
+		 * @return {@code true} if the bitmap index was a complete match.
+		 */
+		boolean removeAllOrNone(PackBitmapIndex bitmapIndex);
+
+		/** @return the number of elements in the bitmap. */
+		int cardinality();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapObject.java
similarity index 82%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedListener.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapObject.java
index 0ac43b5..345016c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPacksChangedListener.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2012, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,17 +41,25 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.lib;
 
-import org.eclipse.jgit.events.RepositoryListener;
+/**
+ * Base object type accessed during bitmap expansion.
+ *
+ * @since 3.0
+ */
+public abstract class BitmapObject {
+	/**
+	 * Get Git object type. See {@link Constants}.
+	 *
+	 * @return object type
+	 */
+	public abstract int getType();
 
-/** Receives {@link DfsPacksChangedEvent}s. */
-public interface DfsPacksChangedListener extends RepositoryListener {
 	/**
-	 * Invoked when all packs in a repository are listed.
+	 * Get the name of this object.
 	 *
-	 * @param event
-	 *            information about the packs.
+	 * @return unique hash of this object.
 	 */
-	void onPacksChanged(DfsPacksChangedEvent event);
-}
+	public abstract ObjectId getObjectId();
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java
new file mode 100644
index 0000000..d6608cd
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java
@@ -0,0 +1,20 @@
+package org.eclipse.jgit.lib;
+
+/**
+ * Parsed information about a checkout.
+ *
+ * @since 3.0
+ */
+public interface CheckoutEntry {
+
+	/**
+	 * @return the name of the branch before checkout
+	 */
+	public abstract String getFromBranch();
+
+	/**
+	 * @return the name of the branch after checkout
+	 */
+	public abstract String getToBranch();
+
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index af04b10..8240ac8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -1002,8 +1002,11 @@ public class Config {
 		ConfigLine e = new ConfigLine();
 		for (;;) {
 			int input = in.read();
-			if (-1 == input)
+			if (-1 == input) {
+				if (e.section != null)
+					newEntries.add(e);
 				break;
+			}
 
 			final char c = (char) input;
 			if ('\n' == c) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 8fd84c8..3ff4eef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler at sap.com>
  * Copyright (C) 2010, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2012-2013, Robin Rosenberg
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -112,12 +113,6 @@ public class ConfigConstants {
 	/** The "blockSize" key */
 	public static final String CONFIG_KEY_BLOCK_SIZE = "blockSize";
 
-	/** The "readAheadLimit" key */
-	public static final String CONFIG_KEY_READ_AHEAD_LIMIT = "readAheadLimit";
-
-	/** The "readAheadThreads" key */
-	public static final String CONFIG_KEY_READ_AHEAD_THREADS = "readAheadThreads";
-
 	/** The "deltaBaseCacheLimit" key */
 	public static final String CONFIG_KEY_DELTA_BASE_CACHE_LIMIT = "deltaBaseCacheLimit";
 
@@ -194,4 +189,40 @@ public class ConfigConstants {
 
 	/** The "ff" key */
 	public static final String CONFIG_KEY_FF = "ff";
+
+	/**
+	 * The "checkstat" key
+	 * @since 3.0
+	 */
+	public static final String CONFIG_KEY_CHECKSTAT = "checkstat";
+
+	/**
+         * The "renamelimit" key in the "diff section"
+         * @since 3.0
+         */
+	public static final String CONFIG_KEY_RENAMELIMIT = "renamelimit";
+
+	/**
+         * The "noprefix" key in the "diff section"
+         * @since 3.0
+         */
+	public static final String CONFIG_KEY_NOPREFIX = "noprefix";
+
+	/**
+         * A "renamelimit" value in the "diff section"
+         * @since 3.0
+         */
+	public static final String CONFIG_RENAMELIMIT_COPY = "copy";
+
+	/**
+         * A "renamelimit" value in the "diff section"
+         * @since 3.0
+         */
+	public static final String CONFIG_RENAMELIMIT_COPIES = "copies";
+
+	/**
+         * The "renames" key in the "diff section"
+         * @since 3.0
+         */
+	public static final String CONFIG_KEY_RENAMES = "renames";
 }
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 907742e..058addf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -265,6 +265,13 @@ public final class Constants {
 	/** Packed refs file */
 	public static final String PACKED_REFS = "packed-refs";
 
+	/**
+	 * Excludes-file
+	 *
+	 * @since 3.0
+	 */
+	public static final String INFO_EXCLUDE = "info/exclude";
+
 	/** The environment variable that contains the system user name */
 	public static final String OS_USER_NAME_KEY = "user.name";
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 23aca37..0fc3d4a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -74,6 +74,25 @@ public class CoreConfig {
 		INPUT;
 	}
 
+	/**
+	 * Permissible values for {@code core.checkstat}
+	 *
+	 * @since 3.0
+	 */
+	public static enum CheckStat {
+		/**
+		 * Only check the size and whole second part of time stamp when
+		 * comparing the stat info in the dircache with actual file stat info.
+		 */
+		MINIMAL,
+
+		/**
+		 * Check as much of the dircache stat info as possible. Implementation
+		 * limits may apply.
+		 */
+		DEFAULT
+	}
+
 	private final int compression;
 
 	private final int packIndexVersion;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index d32eb99..3365444 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -2,6 +2,7 @@
  * Copyright (C) 2007, Dave Watson <dwatson at mimvista.com>
  * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg at dewire.com>
  * Copyright (C) 2010, Jens Baumgart <jens.baumgart at sap.com>
+ * Copyright (C) 2013, Robin Stocker <robin at nibor.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -49,7 +50,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.jgit.dircache.DirCache;
@@ -85,6 +88,105 @@ import org.eclipse.jgit.treewalk.filter.TreeFilter;
  */
 public class IndexDiff {
 
+	/**
+	 * Represents the state of the index for a certain path regarding the stages
+	 * - which stages exist for a path and which not (base, ours, theirs).
+	 * <p>
+	 * This is used for figuring out what kind of conflict occurred.
+	 *
+	 * @see IndexDiff#getConflictingStageStates()
+	 * @since 3.0
+	 */
+	public static enum StageState {
+		/**
+		 * Exists in base, but neither in ours nor in theirs.
+		 */
+		BOTH_DELETED(1),
+
+		/**
+		 * Only exists in ours.
+		 */
+		ADDED_BY_US(2),
+
+		/**
+		 * Exists in base and ours, but no in theirs.
+		 */
+		DELETED_BY_THEM(3),
+
+		/**
+		 * Only exists in theirs.
+		 */
+		ADDED_BY_THEM(4),
+
+		/**
+		 * Exists in base and theirs, but not in ours.
+		 */
+		DELETED_BY_US(5),
+
+		/**
+		 * Exists in ours and theirs, but not in base.
+		 */
+		BOTH_ADDED(6),
+
+		/**
+		 * Exists in all stages, content conflict.
+		 */
+		BOTH_MODIFIED(7);
+
+		private final int stageMask;
+
+		private StageState(int stageMask) {
+			this.stageMask = stageMask;
+		}
+
+		int getStageMask() {
+			return stageMask;
+		}
+
+		/**
+		 * @return whether there is a "base" stage entry
+		 */
+		public boolean hasBase() {
+			return (stageMask & 1) != 0;
+		}
+
+		/**
+		 * @return whether there is an "ours" stage entry
+		 */
+		public boolean hasOurs() {
+			return (stageMask & 2) != 0;
+		}
+
+		/**
+		 * @return whether there is a "theirs" stage entry
+		 */
+		public boolean hasTheirs() {
+			return (stageMask & 4) != 0;
+		}
+
+		static StageState fromMask(int stageMask) {
+			// bits represent: theirs, ours, base
+			switch (stageMask) {
+			case 1: // 0b001
+				return BOTH_DELETED;
+			case 2: // 0b010
+				return ADDED_BY_US;
+			case 3: // 0b011
+				return DELETED_BY_THEM;
+			case 4: // 0b100
+				return ADDED_BY_THEM;
+			case 5: // 0b101
+				return DELETED_BY_US;
+			case 6: // 0b110
+				return BOTH_ADDED;
+			case 7: // 0b111
+				return BOTH_MODIFIED;
+			default:
+				return null;
+			}
+		}
+	}
+
 	private static final class ProgressReportingFilter extends TreeFilter {
 
 		private final ProgressMonitor monitor;
@@ -156,7 +258,7 @@ public class IndexDiff {
 
 	private Set<String> untracked = new HashSet<String>();
 
-	private Set<String> conflicts = new HashSet<String>();
+	private Map<String, StageState> conflicts = new HashMap<String, StageState>();
 
 	private Set<String> ignored;
 
@@ -295,9 +397,13 @@ public class IndexDiff {
 			if (dirCacheIterator != null) {
 				final DirCacheEntry dirCacheEntry = dirCacheIterator
 						.getDirCacheEntry();
-				if (dirCacheEntry != null && dirCacheEntry.getStage() > 0) {
-					conflicts.add(treeWalk.getPathString());
-					continue;
+				if (dirCacheEntry != null) {
+					int stage = dirCacheEntry.getStage();
+					if (stage > 0) {
+						String path = treeWalk.getPathString();
+						addConflict(path, stage);
+						continue;
+					}
 				}
 			}
 
@@ -355,6 +461,18 @@ public class IndexDiff {
 			return true;
 	}
 
+	private void addConflict(String path, int stage) {
+		StageState existingStageStates = conflicts.get(path);
+		byte stageMask = 0;
+		if (existingStageStates != null)
+			stageMask |= existingStageStates.getStageMask();
+		// stage 1 (base) should be shifted 0 times
+		int shifts = stage - 1;
+		stageMask |= (1 << shifts);
+		StageState stageState = StageState.fromMask(stageMask);
+		conflicts.put(path, stageState);
+	}
+
 	/**
 	 * @return list of files added to the index, not in the tree
 	 */
@@ -398,9 +516,19 @@ public class IndexDiff {
 	}
 
 	/**
-	 * @return list of files that are in conflict
+	 * @return list of files that are in conflict, corresponds to the keys of
+	 *         {@link #getConflictingStageStates()}
 	 */
 	public Set<String> getConflicting() {
+		return conflicts.keySet();
+	}
+
+	/**
+	 * @return the map from each path of {@link #getConflicting()} to its
+	 *         corresponding {@link StageState}
+	 * @since 3.0
+	 */
+	public Map<String, StageState> getConflictingStageStates() {
 		return conflicts;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index ee4f3d9..3bd0250 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -52,10 +52,10 @@ import java.util.Set;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
 
 /**
  * Reads an {@link ObjectDatabase} for a single thread.
@@ -437,6 +437,32 @@ public abstract class ObjectReader {
 	}
 
 	/**
+	 * Advise the reader to avoid unreachable objects.
+	 * <p>
+	 * While enabled the reader will skip over anything previously proven to be
+	 * unreachable. This may be dangerous in the face of concurrent writes.
+	 *
+	 * @param avoid
+	 *            true to avoid unreachable objects.
+	 * @since 3.0
+	 */
+	public void setAvoidUnreachableObjects(boolean avoid) {
+		// Do nothing by default.
+	}
+
+	/**
+	 * An index that can be used to speed up ObjectWalks.
+	 *
+	 * @return the index or null if one does not exist.
+	 * @throws IOException
+	 *             when the index fails to load
+	 * @since 3.0
+	 */
+	public BitmapIndex getBitmapIndex() throws IOException {
+		return null;
+	}
+
+	/**
 	 * Release any resources used by this reader.
 	 * <p>
 	 * A reader that has been released can be used again, but may need to be
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index 394af29..8d59cb4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -178,6 +178,8 @@ public abstract class RefUpdate {
 	 */
 	private boolean detachingSymbolicRef;
 
+	private boolean checkConflicting = true;
+
 	/**
 	 * Construct a new update operation for the reference.
 	 * <p>
@@ -529,7 +531,7 @@ public abstract class RefUpdate {
 		final String myName = getRef().getLeaf().getName();
 		if (myName.startsWith(Constants.R_HEADS)) {
 			Ref head = getRefDatabase().getRef(Constants.HEAD);
-			while (head.isSymbolic()) {
+			while (head != null && head.isSymbolic()) {
 				head = head.getTarget();
 				if (myName.equals(head.getName()))
 					return result = Result.REJECTED_CURRENT_BRANCH;
@@ -564,7 +566,7 @@ public abstract class RefUpdate {
 	public Result link(String target) throws IOException {
 		if (!target.startsWith(Constants.R_REFS))
 			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().illegalArgumentNotA, Constants.R_REFS));
-		if (getRefDatabase().isNameConflicting(getName()))
+		if (checkConflicting && getRefDatabase().isNameConflicting(getName()))
 			return Result.LOCK_FAILURE;
 		try {
 			if (!tryLock(false))
@@ -598,7 +600,8 @@ public abstract class RefUpdate {
 		RevObject newObj;
 		RevObject oldObj;
 
-		if (getRefDatabase().isNameConflicting(getName()))
+		// don't make expensive conflict check if this is an existing Ref
+		if (oldValue == null && checkConflicting && getRefDatabase().isNameConflicting(getName()))
 			return Result.LOCK_FAILURE;
 		try {
 			if (!tryLock(true))
@@ -630,6 +633,17 @@ public abstract class RefUpdate {
 		}
 	}
 
+	/**
+	 * Enable/disable the check for conflicting ref names. By default conflicts
+	 * are checked explicitly.
+	 *
+	 * @param check
+	 * @since 3.0
+	 */
+	public void setCheckConflicting(boolean check) {
+		checkConflicting = check;
+	}
+
 	private static RevObject safeParse(final RevWalk rw, final AnyObjectId id)
 			throws IOException {
 		try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
index 8acac42..747fa62 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
@@ -51,7 +51,7 @@ import java.io.StringWriter;
 import java.util.Collection;
 import java.util.Map;
 
-import org.eclipse.jgit.storage.file.RefDirectory;
+import org.eclipse.jgit.internal.storage.file.RefDirectory;
 import org.eclipse.jgit.util.RefList;
 import org.eclipse.jgit.util.RefMap;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java
similarity index 71%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java
index dfef797..e2102e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsRefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2011-2013, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -40,34 +40,42 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+package org.eclipse.jgit.lib;
 
-package org.eclipse.jgit.storage.dfs;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
 
-import java.io.IOException;
+/**
+ * Parsed reflog entry
+ *
+ * @since 3.0
+ */
+public interface ReflogEntry {
 
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.RefRename;
-import org.eclipse.jgit.lib.RefUpdate.Result;
+	/**
+	 * @return the commit id before the change
+	 */
+	public abstract ObjectId getOldId();
+
+	/**
+	 * @return the commit id after the change
+	 */
+	public abstract ObjectId getNewId();
 
-final class DfsRefRename extends RefRename {
-	DfsRefRename(DfsRefUpdate src, DfsRefUpdate dst) {
-		super(src, dst);
-	}
+	/**
+	 * @return user performing the change
+	 */
+	public abstract PersonIdent getWho();
 
-	@Override
-	protected Result doRename() throws IOException {
-		// TODO Correctly handle renaming foo/bar to foo.
-		// TODO Batch these together into one log update.
+	/**
+	 * @return textual description of the change
+	 */
+	public abstract String getComment();
 
-		destination.setExpectedOldObjectId(ObjectId.zeroId());
-		destination.setNewObjectId(source.getRef().getObjectId());
-		switch (destination.update()) {
-		case NEW:
-			source.delete();
-			return Result.RENAMED;
+	/**
+	 * @return a {@link CheckoutEntry} with parsed information about a branch
+	 *         switch, or null if the entry is not a checkout
+	 */
+	public abstract CheckoutEntry parseCheckout();
 
-		default:
-			return destination.getResult();
-		}
-	}
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
similarity index 64%
copy from org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
index f9185e8..fdab883 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2013, Robin Rosenberg <robin.rosenberg at dewire.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,32 +43,47 @@
 
 package org.eclipse.jgit.lib;
 
-import java.io.File;
-
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import java.io.IOException;
+import java.util.List;
 
 /**
- * Base class to support constructing a {@link Repository}.
- * <p>
- * Applications must set one of {@link #setGitDir(File)} or
- * {@link #setWorkTree(File)}, or use {@link #readEnvironment()} or
- * {@link #findGitDir()} in order to configure the minimum property set
- * necessary to open a repository.
- * <p>
- * Single repository applications trying to be compatible with other Git
- * implementations are encouraged to use a model such as:
- *
- * <pre>
- * new RepositoryBuilder() //
- * 		.setGitDir(gitDirArgument) // --git-dir if supplied, no-op if null
- * 		.readEnviroment() // scan environment GIT_* variables
- * 		.findGitDir() // scan up the file system tree
- * 		.build()
- * </pre>
+ * Utility for reading reflog entries
  *
- * @see FileRepositoryBuilder
+ * @since 3.0
  */
-public class RepositoryBuilder extends
-		BaseRepositoryBuilder<RepositoryBuilder, Repository> {
-	// Empty implementation, everything is inherited.
-}
+public interface ReflogReader {
+
+	/**
+	 * Get the last entry in the reflog
+	 *
+	 * @return the latest reflog entry, or null if no log
+	 * @throws IOException
+	 */
+	public abstract ReflogEntry getLastEntry() throws IOException;
+
+	/**
+	 * @return all reflog entries in reverse order
+	 * @throws IOException
+	 */
+	public abstract List<ReflogEntry> getReverseEntries() throws IOException;
+
+	/**
+	 * Get specific entry in the reflog relative to the last entry which is
+	 * considered entry zero.
+	 *
+	 * @param number
+	 * @return reflog entry or null if not found
+	 * @throws IOException
+	 */
+	public abstract ReflogEntry getReverseEntry(int number) throws IOException;
+
+	/**
+	 * @param max
+	 *            max number of entries to read
+	 * @return all reflog entries in reverse order
+	 * @throws IOException
+	 */
+	public abstract List<ReflogEntry> getReverseEntries(int max)
+			throws IOException;
+
+}
\ No newline at end of file
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 b36f7e8..79f02c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -81,9 +81,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.CheckoutEntry;
-import org.eclipse.jgit.storage.file.ReflogEntry;
-import org.eclipse.jgit.storage.file.ReflogReader;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.treewalk.TreeWalk;
@@ -757,7 +754,7 @@ public abstract class Repository {
 
 	private String resolveReflogCheckout(int checkoutNo)
 			throws IOException {
-		List<ReflogEntry> reflogEntries = new ReflogReader(this, Constants.HEAD)
+		List<ReflogEntry> reflogEntries = getReflogReader(Constants.HEAD)
 				.getReverseEntries();
 		for (ReflogEntry entry : reflogEntries) {
 			CheckoutEntry checkout = entry.parseCheckout();
@@ -778,7 +775,7 @@ public abstract class Repository {
 					JGitText.get().invalidReflogRevision, time));
 		}
 		assert number >= 0;
-		ReflogReader reader = new ReflogReader(this, ref.getName());
+		ReflogReader reader = getReflogReader(ref.getName());
 		ReflogEntry entry = reader.getReverseEntry(number);
 		if (entry == null)
 			throw new RevisionSyntaxException(MessageFormat.format(
@@ -1269,7 +1266,9 @@ public abstract class Repository {
 	 * @param refName
 	 * @return a {@link ReflogReader} for the supplied refname, or null if the
 	 *         named ref does not exist.
-	 * @throws IOException the ref could not be accessed.
+	 * @throws IOException
+	 *             the ref could not be accessed.
+	 * @since 3.0
 	 */
 	public abstract ReflogReader getReflogReader(String refName)
 			throws IOException;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
index f9185e8..e989caf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryBuilder.java
@@ -45,8 +45,6 @@ package org.eclipse.jgit.lib;
 
 import java.io.File;
 
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-
 /**
  * Base class to support constructing a {@link Repository}.
  * <p>
@@ -57,7 +55,7 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
  * <p>
  * Single repository applications trying to be compatible with other Git
  * implementations are encouraged to use a model such as:
- *
+ * 
  * <pre>
  * new RepositoryBuilder() //
  * 		.setGitDir(gitDirArgument) // --git-dir if supplied, no-op if null
@@ -65,8 +63,8 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
  * 		.findGitDir() // scan up the file system tree
  * 		.build()
  * </pre>
- *
- * @see FileRepositoryBuilder
+ * 
+ * @see org.eclipse.jgit.storage.file.FileRepositoryBuilder
  */
 public class RepositoryBuilder extends
 		BaseRepositoryBuilder<RepositoryBuilder, Repository> {
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 5132eb9..c7d957c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -52,7 +52,7 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.RawParseUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
index 937c76e..4fac2eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
@@ -70,6 +70,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return "Bare"; }
 	},
 
@@ -90,6 +93,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return true; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_normal; }
 	},
 
@@ -109,6 +115,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_conflicts; }
 	},
 
@@ -130,6 +139,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_merged; }
 	},
 
@@ -149,6 +161,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_conflicts; }
 	},
 
@@ -170,6 +185,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_merged; }
 	},
 
@@ -189,6 +207,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_conflicts; }
 	},
 
@@ -210,6 +231,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_merged; }
 	},
 
@@ -230,6 +254,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return true; }
 
 		@Override
+		public boolean isRebasing() { return true; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_rebaseOrApplyMailbox; }
 	},
 
@@ -250,6 +277,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return true; }
 
 		@Override
+		public boolean isRebasing() { return true; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_rebase; }
 	},
 
@@ -270,6 +300,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return true; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_applyMailbox; }
 	},
 
@@ -290,6 +323,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return true; }
 
 		@Override
+		public boolean isRebasing() { return true; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_rebaseWithMerge; }
 	},
 
@@ -310,6 +346,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return true; }
 
 		@Override
+		public boolean isRebasing() { return true; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_rebaseInteractive; }
 	},
 
@@ -333,6 +372,9 @@ public enum RepositoryState {
 		public boolean canAmend() { return false; }
 
 		@Override
+		public boolean isRebasing() { return false; }
+
+		@Override
 		public String getDescription() { return JGitText.get().repositoryState_bisecting; }
 	};
 
@@ -357,6 +399,12 @@ public enum RepositoryState {
 	public abstract boolean canAmend();
 
 	/**
+	 * @return true if the repository is currently in a rebase
+	 * @since 3.0
+	 */
+	public abstract boolean isRebasing();
+
+	/**
 	 * @return a human readable description of the state.
 	 */
 	public abstract String getDescription();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
index 9e8e256..ff85f9b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
@@ -157,10 +157,7 @@ public class ThreadSafeProgressMonitor implements ProgressMonitor {
 	}
 
 	public void update(int completed) {
-		int old = pendingUpdates.getAndAdd(completed);
-		if (isMainThread())
-			doUpdates();
-		else if (old == 0)
+		if (0 == pendingUpdates.getAndAdd(completed))
 			process.release();
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
index 2e6fc41..c5e615e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008-2009, Google Inc.
  * Copyright (C) 2009, Matthias Sohn <matthias.sohn at sap.com>
+ * Copyright (C) 2012, Research In Motion Limited
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -66,9 +67,19 @@ public abstract class MergeStrategy {
 	/** Simple strategy to merge paths, without simultaneous edits. */
 	public static final ThreeWayMergeStrategy SIMPLE_TWO_WAY_IN_CORE = new StrategySimpleTwoWayInCore();
 
-	/** Simple strategy to merge paths. It tries to merge also contents. Multiple merge bases are not supported */
+	/**
+	 * Simple strategy to merge paths. It tries to merge also contents. Multiple
+	 * merge bases are not supported
+	 */
 	public static final ThreeWayMergeStrategy RESOLVE = new StrategyResolve();
 
+	/**
+	 * Recursive strategy to merge paths. It tries to merge also contents.
+	 * Multiple merge bases are supported
+	 * @since 3.0
+	 */
+	public static final ThreeWayMergeStrategy RECURSIVE = new StrategyRecursive();
+
 	private static final HashMap<String, MergeStrategy> STRATEGIES = new HashMap<String, MergeStrategy>();
 
 	static {
@@ -76,6 +87,7 @@ public abstract class MergeStrategy {
 		register(THEIRS);
 		register(SIMPLE_TWO_WAY_IN_CORE);
 		register(RESOLVE);
+		register(RECURSIVE);
 	}
 
 	/**
@@ -103,7 +115,8 @@ public abstract class MergeStrategy {
 	public static synchronized void register(final String name,
 			final MergeStrategy imp) {
 		if (STRATEGIES.containsKey(name))
-			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().mergeStrategyAlreadyExistsAsDefault, name));
+			throw new IllegalArgumentException(MessageFormat.format(
+					JGitText.get().mergeStrategyAlreadyExistsAsDefault, name));
 		STRATEGIES.put(name, imp);
 	}
 
@@ -146,7 +159,7 @@ public abstract class MergeStrategy {
 
 	/**
 	 * Create a new merge instance.
-	 * 
+	 *
 	 * @param db
 	 *            repository database the merger will read from, and eventually
 	 *            write results back to.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
index fd94cfb..1346328 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
@@ -47,6 +47,8 @@ import java.io.IOException;
 import java.text.MessageFormat;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.NoMergeBaseException;
+import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
@@ -186,19 +188,20 @@ public abstract class Merger {
 	/**
 	 * Create an iterator to walk the merge base of two commits.
 	 *
-	 * @param aIdx
-	 *            index of the first commit in {@link #sourceObjects}.
-	 * @param bIdx
-	 *            index of the second commit in {@link #sourceObjects}.
+	 * @param a
+	 *            the first commit in {@link #sourceObjects}.
+	 * @param b
+	 *            the second commit in {@link #sourceObjects}.
 	 * @return the new iterator
 	 * @throws IncorrectObjectTypeException
 	 *             one of the input objects is not a commit.
 	 * @throws IOException
 	 *             objects are missing or multiple merge bases were found.
+	 * @since 3.0
 	 */
-	protected AbstractTreeIterator mergeBase(final int aIdx, final int bIdx)
+	protected AbstractTreeIterator mergeBase(RevCommit a, RevCommit b)
 			throws IOException {
-		RevCommit base = getBaseCommit(aIdx, bIdx);
+		RevCommit base = getBaseCommit(a, b);
 		return (base == null) ? new EmptyTreeIterator() : openTree(base.getTree());
 	}
 
@@ -224,18 +227,39 @@ public abstract class Merger {
 		if (sourceCommits[bIdx] == null)
 			throw new IncorrectObjectTypeException(sourceObjects[bIdx],
 					Constants.TYPE_COMMIT);
+		return getBaseCommit(sourceCommits[aIdx], sourceCommits[bIdx]);
+	}
+
+	/**
+	 * Return the merge base of two commits.
+	 *
+	 * @param a
+	 *            the first commit in {@link #sourceObjects}.
+	 * @param b
+	 *            the second commit in {@link #sourceObjects}.
+	 * @return the merge base of two commits
+	 * @throws IncorrectObjectTypeException
+	 *             one of the input objects is not a commit.
+	 * @throws IOException
+	 *             objects are missing or multiple merge bases were found.
+	 * @since 3.0
+	 */
+	protected RevCommit getBaseCommit(RevCommit a, RevCommit b)
+			throws IncorrectObjectTypeException, IOException {
 		walk.reset();
 		walk.setRevFilter(RevFilter.MERGE_BASE);
-		walk.markStart(sourceCommits[aIdx]);
-		walk.markStart(sourceCommits[bIdx]);
+		walk.markStart(a);
+		walk.markStart(b);
 		final RevCommit base = walk.next();
 		if (base == null)
 			return null;
 		final RevCommit base2 = walk.next();
 		if (base2 != null) {
-			throw new IOException(MessageFormat.format(JGitText.get().multipleMergeBasesFor
-					, sourceCommits[aIdx].name(), sourceCommits[bIdx].name()
-					, base.name(), base2.name()));
+			throw new NoMergeBaseException(
+					MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED,
+					MessageFormat.format(
+					JGitText.get().multipleMergeBasesFor, a.name(), b.name(),
+					base.name(), base2.name()));
 		}
 		return base;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
new file mode 100644
index 0000000..5802850
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2012, Research In Motion Limited
+ * Copyright (C) 2012, Christian Halstrick <christian.halstrick at sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Contributors:
+ *    George Young - initial API and implementation
+ *    Christian Halstrick - initial API and implementation
+ */
+package org.eclipse.jgit.merge;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.NoMergeBaseException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+
+/**
+ * A three-way merger performing a content-merge if necessary across multiple
+ * bases using recursion
+ *
+ * This merger extends the resolve merger and does several things differently:
+ *
+ * - allow more than one merge base, up to a maximum
+ *
+ * - uses "Lists" instead of Arrays for chained types
+ *
+ * - recursively merges the merge bases together to compute a usable base
+ * @since 3.0
+ */
+public class RecursiveMerger extends ResolveMerger {
+	/**
+	 * The maximum number of merge bases. This merge will stop when the number
+	 * of merge bases exceeds this value
+	 */
+	public final int MAX_BASES = 200;
+
+	private PersonIdent ident = new PersonIdent(db);
+
+	/**
+	 * Normal recursive merge when you want a choice of DirCache placement
+	 * inCore
+	 *
+	 * @param local
+	 * @param inCore
+	 */
+	protected RecursiveMerger(Repository local, boolean inCore) {
+		super(local, inCore);
+	}
+
+	/**
+	 * Normal recursive merge, implies not inCore
+	 *
+	 * @param local
+	 */
+	protected RecursiveMerger(Repository local) {
+		this(local, false);
+	}
+
+	/**
+	 * Get a single base commit for two given commits. If the two source commits
+	 * have more than one base commit recursively merge the base commits
+	 * together until you end up with a single base commit.
+	 *
+	 * @throws IOException
+	 * @throws IncorrectObjectTypeException
+	 */
+	@Override
+	protected RevCommit getBaseCommit(RevCommit a, RevCommit b)
+			throws IncorrectObjectTypeException, IOException {
+		return getBaseCommit(a, b, 0);
+	}
+
+	/**
+	 * Get a single base commit for two given commits. If the two source commits
+	 * have more than one base commit recursively merge the base commits
+	 * together until a virtual common base commit has been found.
+	 *
+	 * @param a
+	 *            the first commit to be merged
+	 * @param b
+	 *            the second commit to be merged
+	 * @param callDepth
+	 *            the callDepth when this method is called recursively
+	 * @return the merge base of two commits
+	 * @throws IOException
+	 * @throws IncorrectObjectTypeException
+	 *             one of the input objects is not a commit.
+	 * @throws NoMergeBaseException
+	 *             too many merge bases are found or the computation of a common
+	 *             merge base failed (e.g. because of a conflict).
+	 */
+	protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
+			throws IOException {
+		ArrayList<RevCommit> baseCommits = new ArrayList<RevCommit>();
+		walk.reset();
+		walk.setRevFilter(RevFilter.MERGE_BASE);
+		walk.markStart(a);
+		walk.markStart(b);
+		RevCommit c;
+		while ((c = walk.next()) != null)
+			baseCommits.add(c);
+
+		if (baseCommits.isEmpty())
+			return null;
+		if (baseCommits.size() == 1)
+			return baseCommits.get(0);
+		if (baseCommits.size() >= MAX_BASES)
+			throw new NoMergeBaseException(NoMergeBaseException.MergeBaseFailureReason.TOO_MANY_MERGE_BASES, MessageFormat.format(
+					JGitText.get().mergeRecursiveTooManyMergeBasesFor,
+					Integer.valueOf(MAX_BASES), a.name(), b.name(),
+							Integer.valueOf(baseCommits.size())));
+
+		// We know we have more than one base commit. We have to do merges now
+		// to determine a single base commit. We don't want to spoil the current
+		// dircache and working tree with the results of this intermediate
+		// merges. Therefore set the dircache to a new in-memory dircache and
+		// disable that we update the working-tree. We set this back to the
+		// original values once a single base commit is created.
+		RevCommit currentBase = baseCommits.get(0);
+		DirCache oldDircache = dircache;
+		boolean oldIncore = inCore;
+		WorkingTreeIterator oldWTreeIt = workingTreeIterator;
+		workingTreeIterator = null;
+		try {
+			dircache = dircacheFromTree(currentBase.getTree());
+			inCore = true;
+
+			List<RevCommit> parents = new ArrayList<RevCommit>();
+			parents.add(currentBase);
+			for (int commitIdx = 1; commitIdx < baseCommits.size(); commitIdx++) {
+				RevCommit nextBase = baseCommits.get(commitIdx);
+				if (commitIdx >= MAX_BASES)
+					throw new NoMergeBaseException(
+							NoMergeBaseException.MergeBaseFailureReason.TOO_MANY_MERGE_BASES,
+							MessageFormat.format(
+							JGitText.get().mergeRecursiveTooManyMergeBasesFor,
+							Integer.valueOf(MAX_BASES), a.name(), b.name(),
+									Integer.valueOf(baseCommits.size())));
+				parents.add(nextBase);
+				if (mergeTrees(
+						openTree(getBaseCommit(currentBase, nextBase,
+								callDepth + 1).getTree()),
+						currentBase.getTree(),
+						nextBase.getTree()))
+					currentBase = createCommitForTree(resultTree, parents);
+				else
+					throw new NoMergeBaseException(
+							NoMergeBaseException.MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION,
+							MessageFormat.format(
+									JGitText.get().mergeRecursiveTooManyMergeBasesFor,
+									Integer.valueOf(MAX_BASES), a.name(),
+									b.name(),
+									Integer.valueOf(baseCommits.size())));
+			}
+		} finally {
+			inCore = oldIncore;
+			dircache = oldDircache;
+			workingTreeIterator = oldWTreeIt;
+		}
+		return currentBase;
+	}
+
+	/**
+	 * Create a new commit by explicitly specifying the content tree and the
+	 * parents. The commit message is not set and author/committer are set to
+	 * the current user.
+	 *
+	 * @param tree
+	 *            the tree this commit should capture
+	 * @param parents
+	 *            the list of parent commits
+	 * @return a new (persisted) commit
+	 * @throws IOException
+	 */
+	private RevCommit createCommitForTree(ObjectId tree, List<RevCommit> parents)
+			throws IOException {
+		CommitBuilder c = new CommitBuilder();
+		c.setParentIds(parents);
+		c.setTreeId(tree);
+		c.setAuthor(ident);
+		c.setCommitter(ident);
+		ObjectInserter odi = db.newObjectInserter();
+		ObjectId newCommitId = odi.insert(c);
+		odi.flush();
+		RevCommit ret = walk.lookupCommit(newCommitId);
+		walk.parseHeaders(ret);
+		return ret;
+	}
+
+	/**
+	 * Create a new in memory dircache which has the same content as a given
+	 * tree.
+	 *
+	 * @param treeId
+	 *            the tree which should be used to fill the dircache
+	 * @return a new in memory dircache
+	 * @throws IOException
+	 */
+	private DirCache dircacheFromTree(ObjectId treeId) throws IOException {
+		DirCache ret = DirCache.newInCore();
+		DirCacheBuilder builder = ret.builder();
+		TreeWalk tw = new TreeWalk(db);
+		tw.addTree(treeId);
+		tw.setRecursive(true);
+		while (tw.next()) {
+			DirCacheEntry e = new DirCacheEntry(tw.getRawPath());
+			e.setFileMode(tw.getFileMode(0));
+			e.setObjectId(tw.getObjectId(0));
+			builder.add(e);
+		}
+		builder.finish();
+		return ret;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 433458a..710996d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2010, Christian Halstrick <christian.halstrick at sap.com>,
  * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn at sap.com>
+ * Copyright (C) 2012, Research In Motion Limited
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -80,6 +81,8 @@ import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
@@ -104,7 +107,12 @@ public class ResolveMerger extends ThreeWayMerger {
 
 	private NameConflictTreeWalk tw;
 
-	private String commitNames[];
+	/**
+	 * string versions of a list of commit SHA1s
+	 *
+	 * @since 3.0
+	 */
+	protected String commitNames[];
 
 	private static final int T_BASE = 0;
 
@@ -118,7 +126,12 @@ public class ResolveMerger extends ThreeWayMerger {
 
 	private DirCacheBuilder builder;
 
-	private ObjectId resultTree;
+	/**
+	 * merge result as tree
+	 *
+	 * @since 3.0
+	 */
+	protected ObjectId resultTree;
 
 	private List<String> unmergedPaths = new ArrayList<String>();
 
@@ -134,13 +147,43 @@ public class ResolveMerger extends ThreeWayMerger {
 
 	private boolean enterSubtree;
 
-	private boolean inCore;
+	/**
+	 * Set to true if this merge should work in-memory. The repos dircache and
+	 * workingtree are not touched by this method. Eventually needed files are
+	 * created as temporary files and a new empty, in-memory dircache will be
+	 * used instead the repo's one. Often used for bare repos where the repo
+	 * doesn't even have a workingtree and dircache.
+	 * @since 3.0
+	 */
+	protected boolean inCore;
 
-	private DirCache dircache;
+	/**
+	 * Set to true if this merger should use the default dircache of the
+	 * repository and should handle locking and unlocking of the dircache. If
+	 * this merger should work in-core or if an explicit dircache was specified
+	 * during construction then this field is set to false.
+	 * @since 3.0
+	 */
+	protected boolean implicitDirCache;
 
-	private WorkingTreeIterator workingTreeIterator;
+	/**
+	 * Directory cache
+	 * @since 3.0
+	 */
+	protected DirCache dircache;
+
+	/**
+	 * The iterator to access the working tree. If set to <code>null</code> this
+	 * merger will not touch the working tree.
+	 * @since 3.0
+	 */
+	protected WorkingTreeIterator workingTreeIterator;
 
-	private MergeAlgorithm mergeAlgorithm;
+	/**
+	 * our merge algorithm
+	 * @since 3.0
+	 */
+	protected MergeAlgorithm mergeAlgorithm;
 
 	/**
 	 * @param local
@@ -153,11 +196,14 @@ public class ResolveMerger extends ThreeWayMerger {
 				ConfigConstants.CONFIG_KEY_ALGORITHM,
 				SupportedAlgorithm.HISTOGRAM);
 		mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
-		commitNames = new String[] { "BASE", "OURS", "THEIRS" };
+		commitNames = new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 		this.inCore = inCore;
 
 		if (inCore) {
+			implicitDirCache = false;
 			dircache = DirCache.newInCore();
+		} else {
+			implicitDirCache = true;
 		}
 	}
 
@@ -170,67 +216,11 @@ public class ResolveMerger extends ThreeWayMerger {
 
 	@Override
 	protected boolean mergeImpl() throws IOException {
-		boolean implicitDirCache = false;
-
-		if (dircache == null) {
+		if (implicitDirCache)
 			dircache = getRepository().lockDirCache();
-			implicitDirCache = true;
-		}
 
 		try {
-			builder = dircache.builder();
-			DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
-
-			tw = new NameConflictTreeWalk(db);
-			tw.addTree(mergeBase());
-			tw.addTree(sourceTrees[0]);
-			tw.addTree(sourceTrees[1]);
-			tw.addTree(buildIt);
-			if (workingTreeIterator != null)
-				tw.addTree(workingTreeIterator);
-
-			while (tw.next()) {
-				if (!processEntry(
-						tw.getTree(T_BASE, CanonicalTreeParser.class),
-						tw.getTree(T_OURS, CanonicalTreeParser.class),
-						tw.getTree(T_THEIRS, CanonicalTreeParser.class),
-						tw.getTree(T_INDEX, DirCacheBuildIterator.class),
-						(workingTreeIterator == null) ? null : tw.getTree(T_FILE, WorkingTreeIterator.class))) {
-					cleanUp();
-					return false;
-				}
-				if (tw.isSubtree() && enterSubtree)
-					tw.enterSubtree();
-			}
-
-			if (!inCore) {
-				// No problem found. The only thing left to be done is to
-				// checkout all files from "theirs" which have been selected to
-				// go into the new index.
-				checkout();
-
-				// All content-merges are successfully done. If we can now write the
-				// new index we are on quite safe ground. Even if the checkout of
-				// files coming from "theirs" fails the user can work around such
-				// failures by checking out the index again.
-				if (!builder.commit()) {
-					cleanUp();
-					throw new IndexWriteException();
-				}
-				builder = null;
-
-			} else {
-				builder.finish();
-				builder = null;
-			}
-
-			if (getUnmergedPaths().isEmpty() && !failed()) {
-				resultTree = dircache.writeTree(getObjectInserter());
-				return true;
-			} else {
-				resultTree = null;
-				return false;
-			}
+			return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1]);
 		} finally {
 			if (implicitDirCache)
 				dircache.unlock();
@@ -279,14 +269,15 @@ public class ResolveMerger extends ThreeWayMerger {
 	/**
 	 * Reverts the worktree after an unsuccessful merge. We know that for all
 	 * modified files the old content was in the old index and the index
-	 * contained only stage 0. In case if inCore operation just clear
-	 * the history of modified files.
+	 * contained only stage 0. In case if inCore operation just clear the
+	 * history of modified files.
 	 *
 	 * @throws IOException
 	 * @throws CorruptObjectException
 	 * @throws NoWorkTreeException
 	 */
-	private void cleanUp() throws NoWorkTreeException, CorruptObjectException, IOException {
+	private void cleanUp() throws NoWorkTreeException, CorruptObjectException,
+			IOException {
 		if (inCore) {
 			modifiedFiles.clear();
 			return;
@@ -298,7 +289,10 @@ public class ResolveMerger extends ThreeWayMerger {
 		while(mpathsIt.hasNext()) {
 			String mpath=mpathsIt.next();
 			DirCacheEntry entry = dc.getEntry(mpath);
-			FileOutputStream fos = new FileOutputStream(new File(db.getWorkTree(), mpath));
+			if (entry == null)
+				continue;
+			FileOutputStream fos = new FileOutputStream(new File(
+					db.getWorkTree(), mpath));
 			try {
 				or.open(entry.getObjectId()).copyTo(fos);
 			} finally {
@@ -494,7 +488,11 @@ public class ResolveMerger extends ThreeWayMerger {
 				return true;
 			} else if (modeT == 0 && modeB != 0) {
 				// we want THEIRS ... but THEIRS contains the deletion of the
-				// file
+				// file. Also, do not complain if the file is already deleted
+				// locally. This complements the test in isWorktreeDirty() for
+				// the same case.
+				if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0)
+					return true;
 				toBeDeleted.add(tw.getPathString());
 				return true;
 			}
@@ -610,6 +608,9 @@ public class ResolveMerger extends ThreeWayMerger {
 	}
 
 	private boolean isIndexDirty() {
+		if (inCore)
+			return false;
+
 		final int modeI = tw.getRawMode(T_INDEX);
 		final int modeO = tw.getRawMode(T_OURS);
 
@@ -623,7 +624,7 @@ public class ResolveMerger extends ThreeWayMerger {
 	}
 
 	private boolean isWorktreeDirty(WorkingTreeIterator work) {
-		if (inCore || work == null)
+		if (work == null)
 			return false;
 
 		final int modeF = tw.getRawMode(T_FILE);
@@ -633,7 +634,10 @@ public class ResolveMerger extends ThreeWayMerger {
 		boolean isDirty = work.isModeDifferent(modeO);
 		if (!isDirty && nonTree(modeF))
 			isDirty = !tw.idEqual(T_FILE, T_OURS);
-
+		// Ignore existing empty directories
+		if (isDirty && modeF == FileMode.TYPE_TREE
+				&& modeO == FileMode.TYPE_MISSING)
+			isDirty = false;
 		if (isDirty)
 			failingPaths.put(tw.getPathString(),
 					MergeFailureReason.DIRTY_WORKTREE);
@@ -862,19 +866,20 @@ public class ResolveMerger extends ThreeWayMerger {
 
 	/**
 	 * Sets the DirCache which shall be used by this merger. If the DirCache is
-	 * not set explicitly this merger will implicitly get and lock a default
-	 * DirCache. If the DirCache is explicitly set the caller is responsible to
-	 * lock it in advance. Finally the merger will call
-	 * {@link DirCache#commit()} which requires that the DirCache is locked. If
-	 * the {@link #mergeImpl()} returns without throwing an exception the lock
-	 * will be released. In case of exceptions the caller is responsible to
-	 * release the lock.
+	 * not set explicitly and if this merger doesn't work in-core, this merger
+	 * will implicitly get and lock a default DirCache. If the DirCache is
+	 * explicitly set the caller is responsible to lock it in advance. Finally
+	 * the merger will call {@link DirCache#commit()} which requires that the
+	 * DirCache is locked. If the {@link #mergeImpl()} returns without throwing
+	 * an exception the lock will be released. In case of exceptions the caller
+	 * is responsible to release the lock.
 	 *
 	 * @param dc
 	 *            the DirCache to set
 	 */
 	public void setDirCache(DirCache dc) {
 		this.dircache = dc;
+		implicitDirCache = false;
 	}
 
 	/**
@@ -891,4 +896,74 @@ public class ResolveMerger extends ThreeWayMerger {
 	public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
 		this.workingTreeIterator = workingTreeIterator;
 	}
+
+
+	/**
+	 * The resolve conflict way of three way merging
+	 *
+	 * @param baseTree
+	 * @param headTree
+	 * @param mergeTree
+	 * @return whether the trees merged cleanly
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	protected boolean mergeTrees(AbstractTreeIterator baseTree,
+			RevTree headTree, RevTree mergeTree) throws IOException {
+
+		builder = dircache.builder();
+		DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
+
+		tw = new NameConflictTreeWalk(db);
+		tw.addTree(baseTree);
+		tw.addTree(headTree);
+		tw.addTree(mergeTree);
+		tw.addTree(buildIt);
+		if (workingTreeIterator != null)
+			tw.addTree(workingTreeIterator);
+
+		while (tw.next()) {
+			if (!processEntry(
+					tw.getTree(T_BASE, CanonicalTreeParser.class),
+					tw.getTree(T_OURS, CanonicalTreeParser.class),
+					tw.getTree(T_THEIRS, CanonicalTreeParser.class),
+					tw.getTree(T_INDEX, DirCacheBuildIterator.class),
+					(workingTreeIterator == null) ? null : tw.getTree(T_FILE,
+							WorkingTreeIterator.class))) {
+				cleanUp();
+				return false;
+			}
+			if (tw.isSubtree() && enterSubtree)
+				tw.enterSubtree();
+		}
+
+		if (!inCore) {
+			// No problem found. The only thing left to be done is to
+			// checkout all files from "theirs" which have been selected to
+			// go into the new index.
+			checkout();
+
+			// All content-merges are successfully done. If we can now write the
+			// new index we are on quite safe ground. Even if the checkout of
+			// files coming from "theirs" fails the user can work around such
+			// failures by checking out the index again.
+			if (!builder.commit()) {
+				cleanUp();
+				throw new IndexWriteException();
+			}
+			builder = null;
+
+		} else {
+			builder.finish();
+			builder = null;
+		}
+
+		if (getUnmergedPaths().isEmpty() && !failed()) {
+			resultTree = dircache.writeTree(getObjectInserter());
+			return true;
+		} else {
+			resultTree = null;
+			return false;
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
similarity index 78%
rename from org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsConfig.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
index fa91b7c..58c1ed2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2012, Research In Motion Limited
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -41,21 +41,29 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.storage.dfs;
+package org.eclipse.jgit.merge;
 
-import java.io.IOException;
+import org.eclipse.jgit.lib.Repository;
 
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.StoredConfig;
+/**
+ * A three-way merge strategy performing a content-merge if necessary
+ *
+ * @since 3.0
+ */
+public class StrategyRecursive extends StrategyResolve {
+
+	@Override
+	public ThreeWayMerger newMerger(Repository db) {
+		return new RecursiveMerger(db, false);
+	}
 
-final class DfsConfig extends StoredConfig {
 	@Override
-	public void load() throws IOException, ConfigInvalidException {
-		clear();
+	public ThreeWayMerger newMerger(Repository db, boolean inCore) {
+		return new RecursiveMerger(db, inCore);
 	}
 
 	@Override
-	public void save() throws IOException {
-		// TODO actually store this configuration.
+	public String getName() {
+		return "recursive";
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
index 521f896..1ad791b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2012, Research In Motion Limited
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -118,6 +119,6 @@ public abstract class ThreeWayMerger extends Merger {
 	protected AbstractTreeIterator mergeBase() throws IOException {
 		if (baseTree != null)
 			return openTree(baseTree);
-		return mergeBase(0, 1);
+		return mergeBase(sourceCommits[0], sourceCommits[1]);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
index 35460a7..0802bfa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>,
+ * Copyright (C) 2013, Gustaf Lundh <gustaf.lundh at sonymobile.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -50,10 +51,22 @@ import org.eclipse.jgit.errors.MissingObjectException;
 
 /** A queue of commits sorted by commit time order. */
 public class DateRevQueue extends AbstractRevQueue {
+	private static final int REBUILD_INDEX_COUNT = 1000;
+
 	private Entry head;
 
 	private Entry free;
 
+	private int inQueue;
+
+	private int sinceLastIndex;
+
+	private Entry[] index;
+
+	private int first;
+
+	private int last = -1;
+
 	/** Create an empty date queue. */
 	public DateRevQueue() {
 		super();
@@ -70,10 +83,36 @@ public class DateRevQueue extends AbstractRevQueue {
 	}
 
 	public void add(final RevCommit c) {
+		sinceLastIndex++;
+		if (++inQueue > REBUILD_INDEX_COUNT
+				&& sinceLastIndex > REBUILD_INDEX_COUNT)
+			buildIndex();
+
 		Entry q = head;
 		final long when = c.commitTime;
+
+		if (first <= last && index[first].commit.commitTime > when) {
+			int low = first, high = last;
+			while (low <= high) {
+				int mid = (low + high) >>> 1;
+				int t = index[mid].commit.commitTime;
+				if (t < when)
+					high = mid - 1;
+				else if (t > when)
+					low = mid + 1;
+				else {
+					low = mid - 1;
+					break;
+				}
+			}
+			low = Math.min(low, high);
+			while (low > first && when == index[low].commit.commitTime)
+				--low;
+			q = index[low];
+		}
+
 		final Entry n = newEntry(c);
-		if (q == null || when > q.commit.commitTime) {
+		if (q == null || (q == head && when > q.commit.commitTime)) {
 			n.next = q;
 			head = n;
 		} else {
@@ -91,11 +130,28 @@ public class DateRevQueue extends AbstractRevQueue {
 		final Entry q = head;
 		if (q == null)
 			return null;
+
+		if (index != null && q == index[first])
+			index[first++] = null;
+		inQueue--;
+
 		head = q.next;
 		freeEntry(q);
 		return q.commit;
 	}
 
+	private void buildIndex() {
+		sinceLastIndex = 0;
+		first = 0;
+		index = new Entry[inQueue / 100 + 1];
+		int qi = 0, ii = 0;
+		for (Entry q = head; q != null; q = q.next) {
+			if (++qi % 100 == 0)
+				index[ii++] = q;
+		}
+		last = ii - 1;
+	}
+
 	/**
 	 * Peek at the next commit, without removing it.
 	 *
@@ -108,6 +164,10 @@ public class DateRevQueue extends AbstractRevQueue {
 	public void clear() {
 		head = null;
 		free = null;
+		index = null;
+		inQueue = 0;
+		sinceLastIndex = 0;
+		last = -1;
 	}
 
 	boolean everbodyHasFlag(final int f) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FollowFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FollowFilter.java
index ab2cf2b..9928286 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FollowFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FollowFilter.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.revwalk;
 
 import java.io.IOException;
 
+import org.eclipse.jgit.diff.DiffConfig;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.treewalk.TreeWalk;
@@ -77,20 +78,25 @@ public class FollowFilter extends TreeFilter {
 	 *            the path to filter on. Must not be the empty string. All
 	 *            trailing '/' characters will be trimmed before string's length
 	 *            is checked or is used as part of the constructed filter.
+	 * @param cfg
+	 *            diff config specifying rename detection options.
 	 * @return a new filter for the requested path.
 	 * @throws IllegalArgumentException
 	 *             the path supplied was the empty string.
+	 * @since 3.0
 	 */
-	public static FollowFilter create(String path) {
-		return new FollowFilter(PathFilter.create(path));
+	public static FollowFilter create(String path, DiffConfig cfg) {
+		return new FollowFilter(PathFilter.create(path), cfg);
 	}
 
 	private final PathFilter path;
+	final DiffConfig cfg;
 
 	private RenameCallback renameCallback;
 
-	FollowFilter(final PathFilter path) {
+	FollowFilter(final PathFilter path, final DiffConfig cfg) {
 		this.path = path;
+		this.cfg = cfg;
 	}
 
 	/** @return the path this filter matches. */
@@ -112,7 +118,7 @@ public class FollowFilter extends TreeFilter {
 
 	@Override
 	public TreeFilter clone() {
-		return new FollowFilter(path.clone());
+		return new FollowFilter(path.clone(), cfg);
 	}
 
 	@SuppressWarnings("nls")
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
index 85982c7..8068ba6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
@@ -66,6 +66,18 @@ public class RevFlag {
 	public static final RevFlag UNINTERESTING = new StaticRevFlag(
 			"UNINTERESTING", RevWalk.UNINTERESTING); //$NON-NLS-1$
 
+	/**
+	 * Set on RevCommit instances added to {@link RevWalk#pending} queue.
+	 * <p>
+	 * We use this flag to avoid adding the same commit instance twice to our
+	 * queue, especially if we reached it by more than one path.
+	 * <p>
+	 * This is a static flag. Its RevWalk is not available.
+	 *
+	 * @since 3.0
+	 */
+	public static final RevFlag SEEN = new StaticRevFlag("SEEN", RevWalk.SEEN);
+
 	final RevWalk walker;
 
 	final String name;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index 269b6c1..12693a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -240,8 +240,16 @@ public class RevTag extends RevObject {
 
 	/**
 	 * Get a reference to the object this tag was placed on.
+	 * <p>
+	 * Note that the returned object has only been looked up (see
+	 * {@link RevWalk#lookupAny(AnyObjectId, int)}. To access the contents it
+	 * needs to be parsed, see {@link RevWalk#parseHeaders(RevObject)} and
+	 * {@link RevWalk#parseBody(RevObject)}.
+	 * <p>
+	 * As an alternative, use {@link RevWalk#peel(RevObject)} and pass this
+	 * {@link RevTag} to peel it until the first non-tag object.
 	 *
-	 * @return object this tag refers to.
+	 * @return object this tag refers to (only looked up, not parsed)
 	 */
 	public final RevObject getObject() {
 		return object;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 8e07048..ca5f4fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -163,9 +163,6 @@ public class RevWalk implements Iterable<RevCommit> {
 
 	private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
 
-	/** Exists <b>ONLY</b> to support legacy Tag and Commit objects. */
-	final Repository repository;
-
 	final ObjectReader reader;
 
 	final MutableObjectId idBuffer;
@@ -203,7 +200,7 @@ public class RevWalk implements Iterable<RevCommit> {
 	 *            released by the caller.
 	 */
 	public RevWalk(final Repository repo) {
-		this(repo, repo.newObjectReader());
+		this(repo.newObjectReader());
 	}
 
 	/**
@@ -215,11 +212,6 @@ public class RevWalk implements Iterable<RevCommit> {
 	 *            required.
 	 */
 	public RevWalk(ObjectReader or) {
-		this(null, or);
-	}
-
-	private RevWalk(final Repository repo, final ObjectReader or) {
-		repository = repo;
 		reader = or;
 		idBuffer = new MutableObjectId();
 		objects = new ObjectIdOwnerMap<RevObject>();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
index 9e1f021..a84e80e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteTreeFilter.java
@@ -46,15 +46,15 @@ package org.eclipse.jgit.revwalk;
 import java.io.IOException;
 import java.util.List;
 
+import org.eclipse.jgit.diff.DiffConfig;
 import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.RenameDetector;
 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
+import org.eclipse.jgit.diff.RenameDetector;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.StopWalkException;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.filter.RevFilter;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -82,10 +82,7 @@ class RewriteTreeFilter extends RevFilter {
 
 	private final TreeWalk pathFilter;
 
-	private final Repository repository;
-
 	RewriteTreeFilter(final RevWalk walker, final TreeFilter t) {
-		repository = walker.repository;
 		pathFilter = new TreeWalk(walker.reader);
 		pathFilter.setFilter(t);
 		pathFilter.setRecursive(t.shouldBeRecursive());
@@ -142,7 +139,7 @@ class RewriteTreeFilter extends RevFilter {
 					// commit. We need to update our filter to its older
 					// name, if we can discover it. Find out what that is.
 					//
-					updateFollowFilter(trees);
+					updateFollowFilter(trees, ((FollowFilter) tw.getFilter()).cfg);
 				}
 				return true;
 			}
@@ -235,7 +232,7 @@ class RewriteTreeFilter extends RevFilter {
 		return false;
 	}
 
-	private void updateFollowFilter(ObjectId[] trees)
+	private void updateFollowFilter(ObjectId[] trees, DiffConfig cfg)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			CorruptObjectException, IOException {
 		TreeWalk tw = pathFilter;
@@ -244,14 +241,14 @@ class RewriteTreeFilter extends RevFilter {
 		tw.reset(trees);
 
 		List<DiffEntry> files = DiffEntry.scan(tw);
-		RenameDetector rd = new RenameDetector(repository);
+		RenameDetector rd = new RenameDetector(tw.getObjectReader(), cfg);
 		rd.addAll(files);
 		files = rd.compute();
 
 		TreeFilter newFilter = oldFilter;
 		for (DiffEntry ent : files) {
 			if (isRename(ent) && ent.getNewPath().equals(oldFilter.getPath())) {
-				newFilter = FollowFilter.create(ent.getOldPath());
+				newFilter = FollowFilter.create(ent.getOldPath(), cfg);
 				RenameCallback callback = oldFilter.getRenameCallback();
 				if (callback != null) {
 					callback.renamed(ent);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjectRepresentation.java
deleted file mode 100644
index 2b45ffa..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsObjectRepresentation.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2011, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.storage.dfs;
-
-import static org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource.GC;
-import static org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
-
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.storage.dfs.DfsObjDatabase.PackSource;
-import org.eclipse.jgit.storage.pack.ObjectToPack;
-import org.eclipse.jgit.storage.pack.StoredObjectRepresentation;
-
-class DfsObjectRepresentation extends StoredObjectRepresentation {
-	final ObjectToPack object;
-
-	DfsPackFile pack;
-
-	/**
-	 * Position of {@link #pack} in the reader's pack list. Lower numbers are
-	 * newer/more recent packs and less likely to contain the best format for a
-	 * base object. Higher numbered packs are bigger, more stable, and favored
-	 * by PackWriter when selecting representations... but only if they come
-	 * last in the representation ordering.
-	 */
-	int packIndex;
-
-	long offset;
-
-	int format;
-
-	long length;
-
-	ObjectId baseId;
-
-	DfsObjectRepresentation(ObjectToPack object) {
-		this.object = object;
-	}
-
-	@Override
-	public int getFormat() {
-		return format;
-	}
-
-	@Override
-	public int getWeight() {
-		return (int) Math.min(length, Integer.MAX_VALUE);
-	}
-
-	@Override
-	public ObjectId getDeltaBase() {
-		return baseId;
-	}
-
-	@Override
-	public boolean wasDeltaAttempted() {
-		if (pack != null) {
-			PackSource source = pack.getPackDescription().getPackSource();
-			return source == GC || source == UNREACHABLE_GARBAGE;
-		}
-		return false;
-	}
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadAheadRejectedExecutionHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadAheadRejectedExecutionHandler.java
deleted file mode 100644
index 78c2cae..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadAheadRejectedExecutionHandler.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2011, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.storage.dfs;
-
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadPoolExecutor;
-
-/** This handler aborts a {@link ReadAheadTask} when the queue is full. */
-final class ReadAheadRejectedExecutionHandler implements
-		RejectedExecutionHandler {
-	static final ReadAheadRejectedExecutionHandler INSTANCE = new ReadAheadRejectedExecutionHandler();
-
-	private ReadAheadRejectedExecutionHandler() {
-		// Singleton, do not create more instances.
-	}
-
-	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
-		((ReadAheadTask.TaskFuture) r).task.abort();
-	}
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadAheadTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadAheadTask.java
deleted file mode 100644
index 35b7677..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/ReadAheadTask.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2011, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.storage.dfs;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jgit.util.IO;
-
-final class ReadAheadTask implements Callable<Void> {
-	private final DfsBlockCache cache;
-
-	private final ReadableChannel channel;
-
-	private final List<BlockFuture> futures;
-
-	private boolean running;
-
-	ReadAheadTask(DfsBlockCache cache, ReadableChannel channel,
-			List<BlockFuture> futures) {
-		this.cache = cache;
-		this.channel = channel;
-		this.futures = futures;
-	}
-
-	public Void call() {
-		int idx = 0;
-		try {
-			synchronized (this) {
-				if (channel.isOpen())
-					running = true;
-				else
-					return null;
-			}
-
-			long position = channel.position();
-			for (; idx < futures.size() && !Thread.interrupted(); idx++) {
-				BlockFuture f = futures.get(idx);
-				if (cache.contains(f.pack, f.start)) {
-					f.done();
-					continue;
-				}
-
-				if (position != f.start)
-					channel.position(f.start);
-
-				int size = (int) (f.end - f.start);
-				byte[] buf = new byte[size];
-				if (IO.read(channel, buf, 0, size) != size)
-					throw new EOFException();
-
-				cache.put(new DfsBlock(f.pack, f.start, buf));
-				f.done();
-				position = f.end;
-			}
-		} catch (IOException err) {
-			// Ignore read-ahead errors. These will be caught later on.
-		} finally {
-			for (; idx < futures.size(); idx++)
-				futures.get(idx).abort();
-			close();
-		}
-		return null;
-	}
-
-	void abort() {
-		for (BlockFuture f : futures)
-			f.abort();
-
-		synchronized (this) {
-			if (!running)
-				close();
-		}
-	}
-
-	private synchronized void close() {
-		try {
-			if (channel.isOpen())
-				channel.close();
-		} catch (IOException err) {
-			// Ignore close errors on a read-only channel.
-		}
-	}
-
-	static final class TaskFuture extends java.util.concurrent.FutureTask<Void> {
-		final ReadAheadTask task;
-
-		TaskFuture(ReadAheadTask task) {
-			super(task);
-			this.task = task;
-		}
-
-		@Override
-		public boolean cancel(boolean mayInterruptIfRunning) {
-			if (super.cancel(mayInterruptIfRunning)) {
-				task.abort();
-				return true;
-			}
-			return false;
-		}
-	}
-
-	/** A scheduled read-ahead block load. */
-	static final class BlockFuture implements Future<Void> {
-		private static enum State {
-			PENDING, DONE, CANCELLED;
-		}
-
-		private volatile State state;
-
-		private volatile Future<?> task;
-
-		private final CountDownLatch latch;
-
-		final DfsPackKey pack;
-
-		final long start;
-
-		final long end;
-
-		BlockFuture(DfsPackKey key, long start, long end) {
-			this.state = State.PENDING;
-			this.latch = new CountDownLatch(1);
-			this.pack = key;
-			this.start = start;
-			this.end = end;
-		}
-
-		synchronized void setTask(Future<?> task) {
-			if (state == State.PENDING)
-				this.task = task;
-		}
-
-		boolean contains(DfsPackKey want, long pos) {
-			return pack == want && start <= pos && pos < end;
-		}
-
-		synchronized void done() {
-			if (state == State.PENDING) {
-				latch.countDown();
-				state = State.DONE;
-				task = null;
-			}
-		}
-
-		synchronized void abort() {
-			if (state == State.PENDING) {
-				latch.countDown();
-				state = State.CANCELLED;
-				task = null;
-			}
-		}
-
-		public boolean cancel(boolean mayInterruptIfRunning) {
-			Future<?> t = task;
-			if (t == null)
-				return false;
-
-			boolean r = t.cancel(mayInterruptIfRunning);
-			abort();
-			return r;
-		}
-
-		public Void get() throws InterruptedException, ExecutionException {
-			latch.await();
-			return null;
-		}
-
-		public Void get(long timeout, TimeUnit unit)
-				throws InterruptedException, ExecutionException,
-				TimeoutException {
-			if (latch.await(timeout, unit))
-				return null;
-			else
-				throw new TimeoutException();
-		}
-
-		public boolean isCancelled() {
-			State s = state;
-			if (s == State.DONE)
-				return false;
-			if (s == State.CANCELLED)
-				return true;
-
-			Future<?> t = task;
-			return t != null ? t.isCancelled() : true;
-		}
-
-		public boolean isDone() {
-			return state == State.DONE;
-		}
-	}
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index a9a86dc..5509fc6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -58,6 +58,8 @@ import java.text.MessageFormat;
 import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileSnapshot;
+import org.eclipse.jgit.internal.storage.file.LockFile;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java
index 377fb04..944fb4f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileRepositoryBuilder.java
@@ -47,7 +47,9 @@ import java.io.File;
 import java.io.IOException;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.lib.BaseRepositoryBuilder;
+import org.eclipse.jgit.lib.Repository;
 
 /**
  * Constructs a {@link FileRepository}.
@@ -69,7 +71,7 @@ import org.eclipse.jgit.lib.BaseRepositoryBuilder;
  * </pre>
  */
 public class FileRepositoryBuilder extends
-		BaseRepositoryBuilder<FileRepositoryBuilder, FileRepository> {
+		BaseRepositoryBuilder<FileRepositoryBuilder, Repository> {
 	/**
 	 * Create a repository matching the configuration in this builder.
 	 * <p>
@@ -83,12 +85,29 @@ public class FileRepositoryBuilder extends
 	 * @throws IOException
 	 *             the repository could not be accessed to configure the rest of
 	 *             the builder's parameters.
+	 * @since 3.0
 	 */
 	@Override
-	public FileRepository build() throws IOException {
+	public Repository build() throws IOException {
 		FileRepository repo = new FileRepository(setup());
 		if (isMustExist() && !repo.getObjectDatabase().exists())
 			throw new RepositoryNotFoundException(getGitDir());
 		return repo;
 	}
+
+	/**
+	 * Convenience factory method to construct a {@link FileRepository}.
+	 *
+	 * @param gitDir
+	 *            {@code GIT_DIR}, the repository meta directory.
+	 * @return a repository matching this configuration.
+	 * @throws IOException
+	 *             the repository could not be accessed to configure the rest of
+	 *             the builder's parameters.
+	 * @since 3.0
+	 */
+	public static Repository create(File gitDir) throws IOException {
+		return new FileRepositoryBuilder().setGitDir(gitDir).readEnvironment()
+				.build();
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index 8060af7..69abd0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -43,10 +43,11 @@
 
 package org.eclipse.jgit.storage.file;
 
+import org.eclipse.jgit.internal.storage.file.WindowCache;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.storage.pack.PackConfig;
 
-/** Configuration parameters for {@link WindowCache}. */
+/** Configuration parameters for JVM-wide buffer cache used by JGit. */
 public class WindowCacheConfig {
 	/** 1024 (number of bytes in one kibibyte/kilobyte) */
 	public static final int KB = 1024;
@@ -146,8 +147,8 @@ public class WindowCacheConfig {
 	}
 
 	/**
-	 * @return maximum number of bytes to cache in {@link DeltaBaseCache}
-	 *         for inflated, recently accessed objects, without delta chains.
+	 * @return maximum number of bytes to cache in delta base cache for
+	 *         inflated, recently accessed objects, without delta chains.
 	 *         <b>Default 10 MB.</b>
 	 */
 	public int getDeltaBaseCacheLimit() {
@@ -156,9 +157,8 @@ public class WindowCacheConfig {
 
 	/**
 	 * @param newLimit
-	 *            maximum number of bytes to cache in
-	 *            {@link DeltaBaseCache} for inflated, recently accessed
-	 *            objects, without delta chains.
+	 *            maximum number of bytes to cache in delta base cache for
+	 *            inflated, recently accessed objects, without delta chains.
 	 */
 	public void setDeltaBaseCacheLimit(final int newLimit) {
 		deltaBaseCacheLimit = newLimit;
@@ -186,9 +186,12 @@ public class WindowCacheConfig {
 	 * If a property is not defined in the configuration, then it is left
 	 * unmodified.
 	 *
-	 * @param rc configuration to read properties from.
+	 * @param rc
+	 *            configuration to read properties from.
+	 * @return {@code this}.
+	 * @since 3.0
 	 */
-	public void fromConfig(final Config rc) {
+	public WindowCacheConfig fromConfig(final Config rc) {
 		setPackedGitOpenFiles(rc.getInt(
 				"core", null, "packedgitopenfiles", getPackedGitOpenFiles())); //$NON-NLS-1$ //$NON-NLS-2$
 		setPackedGitLimit(rc.getLong(
@@ -206,5 +209,19 @@ public class WindowCacheConfig {
 		sft = Math.min(sft, maxMem / 4); // don't use more than 1/4 of the heap
 		sft = Math.min(sft, Integer.MAX_VALUE); // cannot exceed array length
 		setStreamFileThreshold((int) sft);
+		return this;
+	}
+
+	/**
+	 * Install this configuration as the live settings.
+	 * <p>
+	 * The new configuration is applied immediately. If the new limits are
+	 * smaller than what what is currently cached, older entries will be purged
+	 * as soon as possible to allow the cache to meet the new limit.
+	 *
+	 * @since 3.0
+	 */
+	public void install() {
+		WindowCache.reconfigure(this);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/package-info.java
deleted file mode 100644
index 9b3506b..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * File based repository storage.
- */
-package org.eclipse.jgit.storage.file;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
deleted file mode 100644
index aa03746..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/DeltaTask.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010, Google Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- *   names of its contributors may be used to endorse or promote
- *   products derived from this software without specific prior
- *   written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.storage.pack;
-
-import java.util.concurrent.Callable;
-
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
-
-final class DeltaTask implements Callable<Object> {
-	private final PackConfig config;
-
-	private final ObjectReader templateReader;
-
-	private final DeltaCache dc;
-
-	private final ThreadSafeProgressMonitor pm;
-
-	private final int batchSize;
-
-	private final int start;
-
-	private final ObjectToPack[] list;
-
-	DeltaTask(PackConfig config, ObjectReader reader, DeltaCache dc,
-			ThreadSafeProgressMonitor pm, int batchSize, int start,
-			ObjectToPack[] list) {
-		this.config = config;
-		this.templateReader = reader;
-		this.dc = dc;
-		this.pm = pm;
-		this.batchSize = batchSize;
-		this.start = start;
-		this.list = list;
-	}
-
-	public Object call() throws Exception {
-		final ObjectReader or = templateReader.newReader();
-		try {
-			DeltaWindow dw;
-			dw = new DeltaWindow(config, dc, or);
-			dw.search(pm, list, start, batchSize);
-		} finally {
-			or.release();
-			pm.endWorker();
-		}
-		return null;
-	}
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index 58ee985..e321953 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -47,12 +47,12 @@ package org.eclipse.jgit.storage.pack;
 import java.util.concurrent.Executor;
 import java.util.zip.Deflater;
 
+import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.file.PackIndexWriter;
 
 /**
- * Configuration used by a {@link PackWriter} when constructing the stream.
+ * Configuration used by a pack writer when constructing the stream.
  *
  * A configuration may be modified once created, but should not be modified
  * while it is being used by a PackWriter. If a configuration is not modified it
@@ -130,6 +130,14 @@ public class PackConfig {
 	 */
 	public static final int DEFAULT_INDEX_VERSION = 2;
 
+	/**
+	 * Default value of the build bitmaps option: {@value}
+	 *
+	 * @see #setBuildBitmaps(boolean)
+	 * @since 3.0
+	 */
+	public static final boolean DEFAULT_BUILD_BITMAPS = true;
+
 
 	private int compressionLevel = Deflater.DEFAULT_COMPRESSION;
 
@@ -159,6 +167,9 @@ public class PackConfig {
 
 	private int indexVersion = DEFAULT_INDEX_VERSION;
 
+	private boolean buildBitmaps = DEFAULT_BUILD_BITMAPS;
+
+	private boolean cutDeltaChains;
 
 	/** Create a default configuration. */
 	public PackConfig() {
@@ -210,6 +221,8 @@ public class PackConfig {
 		this.threads = cfg.threads;
 		this.executor = cfg.executor;
 		this.indexVersion = cfg.indexVersion;
+		this.buildBitmaps = cfg.buildBitmaps;
+		this.cutDeltaChains = cfg.cutDeltaChains;
 	}
 
 	/**
@@ -363,6 +376,33 @@ public class PackConfig {
 	}
 
 	/**
+	 * @return true if existing delta chains should be cut at
+	 *         {@link #getMaxDeltaDepth()}. Default is false, allowing existing
+	 *         chains to be of any length.
+	 * @since 3.0
+	 */
+	public boolean getCutDeltaChains() {
+		return cutDeltaChains;
+	}
+
+	/**
+	 * Enable cutting existing delta chains at {@link #getMaxDeltaDepth()}.
+	 *
+	 * By default this is disabled and existing chains are kept at whatever
+	 * length a prior packer was configured to create. This allows objects to be
+	 * packed one with a large depth (for example 250), and later to quickly
+	 * repack the repository with a shorter depth (such as 50), but reusing the
+	 * complete delta chains created by the earlier 250 depth.
+	 *
+	 * @param cut
+	 *            true to cut existing chains.
+	 * @since 3.0
+	 */
+	public void setCutDeltaChains(boolean cut) {
+		cutDeltaChains = cut;
+	}
+
+	/**
 	 * Get the number of objects to try when looking for a delta base.
 	 *
 	 * This limit is per thread, if 4 threads are used the actual memory used
@@ -615,6 +655,35 @@ public class PackConfig {
 	}
 
 	/**
+	 * True if writer is allowed to build bitmaps for indexes.
+	 *
+	 * Default setting: {@value #DEFAULT_BUILD_BITMAPS}
+	 *
+	 * @return true if delta base is the writer can choose to output an index
+	 *         with bitmaps.
+	 * @since 3.0
+	 */
+	public boolean isBuildBitmaps() {
+		return buildBitmaps;
+	}
+
+	/**
+	 * Set writer to allow building bitmaps for supported pack files.
+	 *
+	 * Index files can include bitmaps to speed up future ObjectWalks.
+	 *
+	 * Default setting: {@value #DEFAULT_BUILD_BITMAPS}
+	 *
+	 * @param buildBitmaps
+	 *            boolean indicating whether bitmaps may be included in the
+	 *            index.
+	 * @since 3.0
+	 */
+	public void setBuildBitmaps(boolean buildBitmaps) {
+		this.buildBitmaps = buildBitmaps;
+	}
+
+	/**
 	 * Update properties by setting fields from the configuration.
 	 *
 	 * If a property's corresponding variable is not defined in the supplied
@@ -646,5 +715,26 @@ public class PackConfig {
 		setReuseObjects(rc.getBoolean("pack", "reuseobjects", isReuseObjects())); //$NON-NLS-1$ //$NON-NLS-2$
 		setDeltaCompress(rc.getBoolean(
 				"pack", "deltacompression", isDeltaCompress())); //$NON-NLS-1$ //$NON-NLS-2$
+		setCutDeltaChains(rc.getBoolean(
+				"pack", "cutdeltachains", getCutDeltaChains())); //$NON-NLS-1$ //$NON-NLS-2$
+		setBuildBitmaps(rc.getBoolean("pack", "buildbitmaps", isBuildBitmaps())); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	public String toString() {
+		final StringBuilder b = new StringBuilder();
+		b.append("maxDeltaDepth=").append(getMaxDeltaDepth()); //$NON-NLS-1$
+		b.append(", deltaSearchWindowSize=").append(getDeltaSearchWindowSize()); //$NON-NLS-1$
+		b.append(", deltaSearchMemoryLimit=").append(getDeltaSearchMemoryLimit()); //$NON-NLS-1$
+		b.append(", deltaCacheSize=").append(getDeltaCacheSize()); //$NON-NLS-1$
+		b.append(", deltaCacheLimit=").append(getDeltaCacheLimit()); //$NON-NLS-1$
+		b.append(", compressionLevel=").append(getCompressionLevel()); //$NON-NLS-1$
+		b.append(", indexVersion=").append(getIndexVersion()); //$NON-NLS-1$
+		b.append(", bigFileThreshold=").append(getBigFileThreshold()); //$NON-NLS-1$
+		b.append(", threads=").append(getThreads()); //$NON-NLS-1$
+		b.append(", reuseDeltas=").append(isReuseDeltas()); //$NON-NLS-1$
+		b.append(", reuseObjects=").append(isReuseObjects()); //$NON-NLS-1$
+		b.append(", deltaCompress=").append(isDeltaCompress()); //$NON-NLS-1$
+		b.append(", buildBitmaps=").append(isBuildBitmaps()); //$NON-NLS-1$
+		return b.toString();
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/package-info.java
deleted file mode 100644
index ac7d115..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Reading/writing Git pack files.
- */
-package org.eclipse.jgit.storage.pack;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java
index b77e644..cc27707 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java
@@ -45,6 +45,7 @@
 
 package org.eclipse.jgit.transport;
 
+import java.io.OutputStream;
 import java.util.Collection;
 import java.util.Set;
 
@@ -66,6 +67,12 @@ abstract class BaseFetchConnection extends BaseConnection implements
 	public final void fetch(final ProgressMonitor monitor,
 			final Collection<Ref> want, final Set<ObjectId> have)
 			throws TransportException {
+		fetch(monitor, want, have, null);
+	}
+
+	public final void fetch(final ProgressMonitor monitor,
+			final Collection<Ref> want, final Set<ObjectId> have,
+			OutputStream out) throws TransportException {
 		markStartedOperation();
 		doFetch(monitor, want, have);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 8a61405..c82a0cd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -47,6 +47,7 @@ package org.eclipse.jgit.transport;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.text.MessageFormat;
 import java.util.Collection;
 import java.util.Collections;
@@ -56,15 +57,16 @@ import java.util.Set;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackLock;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Config.SectionParser;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Config.SectionParser;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevCommitList;
 import org.eclipse.jgit.revwalk.RevFlag;
@@ -73,7 +75,6 @@ import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
 import org.eclipse.jgit.revwalk.filter.RevFilter;
-import org.eclipse.jgit.storage.file.PackLock;
 import org.eclipse.jgit.transport.PacketLineIn.AckNackResult;
 import org.eclipse.jgit.util.TemporaryBuffer;
 
@@ -265,8 +266,17 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 	public final void fetch(final ProgressMonitor monitor,
 			final Collection<Ref> want, final Set<ObjectId> have)
 			throws TransportException {
+		fetch(monitor, want, have, null);
+	}
+
+	/**
+	 * @since 3.0
+	 */
+	public final void fetch(final ProgressMonitor monitor,
+			final Collection<Ref> want, final Set<ObjectId> have,
+			OutputStream outputStream) throws TransportException {
 		markStartedOperation();
-		doFetch(monitor, want, have);
+		doFetch(monitor, want, have, outputStream);
 	}
 
 	public boolean didFetchIncludeTags() {
@@ -298,12 +308,15 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 	 *            additional objects to assume that already exist locally. This
 	 *            will be added to the set of objects reachable from the
 	 *            destination repository's references.
+	 * @param outputStream
+	 *            ouputStream to write sideband messages to
 	 * @throws TransportException
 	 *             if any exception occurs.
+	 * @since 3.0
 	 */
 	protected void doFetch(final ProgressMonitor monitor,
-			final Collection<Ref> want, final Set<ObjectId> have)
-			throws TransportException {
+			final Collection<Ref> want, final Set<ObjectId> have,
+			OutputStream outputStream) throws TransportException {
 		try {
 			markRefsAdvertised();
 			markReachable(have, maxTimeWanted(want));
@@ -321,7 +334,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 				state = null;
 				pckState = null;
 
-				receivePack(monitor);
+				receivePack(monitor, outputStream);
 			}
 		} catch (CancelledException ce) {
 			close();
@@ -702,11 +715,13 @@ public abstract class BasePackFetchConnection extends BasePackConnection
 			((RevCommit) obj).carry(COMMON);
 	}
 
-	private void receivePack(final ProgressMonitor monitor) throws IOException {
+	private void receivePack(final ProgressMonitor monitor,
+			OutputStream outputStream) throws IOException {
 		onReceivePack();
 		InputStream input = in;
 		if (sideband)
-			input = new SideBandInputStream(input, monitor, getMessageWriter());
+			input = new SideBandInputStream(input, monitor, getMessageWriter(),
+					outputStream);
 
 		ObjectInserter ins = local.newObjectInserter();
 		try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index e97f03c..22b458c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -45,6 +45,7 @@
 package org.eclipse.jgit.transport;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.text.MessageFormat;
 import java.util.Collection;
 import java.util.HashSet;
@@ -56,10 +57,10 @@ import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 
 /**
@@ -138,8 +139,17 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates)
 			throws TransportException {
+		push(monitor, refUpdates, null);
+	}
+
+	/**
+	 * @since 3.0
+	 */
+	public void push(final ProgressMonitor monitor,
+			final Map<String, RemoteRefUpdate> refUpdates, OutputStream outputStream)
+			throws TransportException {
 		markStartedOperation();
-		doPush(monitor, refUpdates);
+		doPush(monitor, refUpdates, outputStream);
 	}
 
 	@Override
@@ -172,14 +182,17 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
 	 *            progress monitor to receive status updates.
 	 * @param refUpdates
 	 *            update commands to be applied to the remote repository.
+	 * @param outputStream
+	 *            output stream to write sideband messages to
 	 * @throws TransportException
 	 *             if any exception occurs.
+	 * @since 3.0
 	 */
 	protected void doPush(final ProgressMonitor monitor,
-			final Map<String, RemoteRefUpdate> refUpdates)
-			throws TransportException {
+			final Map<String, RemoteRefUpdate> refUpdates,
+			OutputStream outputStream) throws TransportException {
 		try {
-			writeCommands(refUpdates.values(), monitor);
+			writeCommands(refUpdates.values(), monitor, outputStream);
 			if (writePack)
 				writePack(refUpdates, monitor);
 			if (sentCommand) {
@@ -208,8 +221,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
 	}
 
 	private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
-			final ProgressMonitor monitor) throws IOException {
-		final String capabilities = enableCapabilities(monitor);
+			final ProgressMonitor monitor, OutputStream outputStream) throws IOException {
+		final String capabilities = enableCapabilities(monitor, outputStream);
 		for (final RemoteRefUpdate rru : refUpdates) {
 			if (!capableDeleteRefs && rru.isDelete()) {
 				rru.setStatus(Status.REJECTED_NODELETE);
@@ -242,7 +255,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
 		outNeedsEnd = false;
 	}
 
-	private String enableCapabilities(final ProgressMonitor monitor) {
+	private String enableCapabilities(final ProgressMonitor monitor,
+			OutputStream outputStream) {
 		final StringBuilder line = new StringBuilder();
 		capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
 		capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
@@ -250,7 +264,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
 
 		capableSideBand = wantCapability(line, CAPABILITY_SIDE_BAND_64K);
 		if (capableSideBand) {
-			in = new SideBandInputStream(in, monitor, getMessageWriter());
+			in = new SideBandInputStream(in, monitor, getMessageWriter(),
+					outputStream);
 			pckIn = new PacketLineIn(in);
 		}
 
@@ -276,7 +291,9 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
 					newObjects.add(r.getNewObjectId());
 			}
 
+			writer.setIndexDisabled(true);
 			writer.setUseCachedPacks(true);
+			writer.setUseBitmaps(true);
 			writer.setThin(thinPack);
 			writer.setReuseValidatingObjects(false);
 			writer.setDeltaBaseAsOffset(capableOfsDelta);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index 26cf903..1a87049 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -66,6 +66,7 @@ import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackLock;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Config.SectionParser;
@@ -86,7 +87,6 @@ import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.PackLock;
 import org.eclipse.jgit.transport.ReceiveCommand.Result;
 import org.eclipse.jgit.util.io.InterruptTimer;
 import org.eclipse.jgit.util.io.TimeoutInputStream;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index 10af055..f4bfd66 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
@@ -65,6 +65,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackLock;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
@@ -76,7 +77,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.PackLock;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.RawParseUtils;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index dc52b1d..4f5cda7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -54,6 +54,7 @@ import java.util.Set;
 import java.util.TreeMap;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -62,7 +63,6 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.storage.pack.PackConfig;
-import org.eclipse.jgit.storage.pack.PackWriter;
 
 /**
  * Creates a Git bundle file, for sneaker-net transport to another system.
@@ -200,6 +200,7 @@ public class BundleWriter {
 			inc.addAll(include.values());
 			for (final RevCommit r : assume)
 				exc.add(r.getId());
+			packWriter.setIndexDisabled(true);
 			packWriter.setDeltaBaseAsOffset(true);
 			packWriter.setThin(exc.size() > 0);
 			packWriter.setReuseValidatingObjects(false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
index 9dc54da..2d04240 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java
@@ -46,14 +46,15 @@
 
 package org.eclipse.jgit.transport;
 
+import java.io.OutputStream;
 import java.util.Collection;
 import java.util.Set;
 
 import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.storage.file.PackLock;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.storage.file.PackLock;
 
 /**
  * Lists known refs from the remote and copies objects of selected refs.
@@ -112,6 +113,47 @@ public interface FetchConnection extends Connection {
 			throws TransportException;
 
 	/**
+	 * Fetch objects we don't have but that are reachable from advertised refs.
+	 * <p>
+	 * Only one call per connection is allowed. Subsequent calls will result in
+	 * {@link TransportException}.
+	 * </p>
+	 * <p>
+	 * Implementations are free to use network connections as necessary to
+	 * efficiently (for both client and server) transfer objects from the remote
+	 * repository into this repository. When possible implementations should
+	 * avoid replacing/overwriting/duplicating an object already available in
+	 * the local destination repository. Locally available objects and packs
+	 * should always be preferred over remotely available objects and packs.
+	 * {@link Transport#isFetchThin()} should be honored if applicable.
+	 * </p>
+	 *
+	 * @param monitor
+	 *            progress monitor to inform the end-user about the amount of
+	 *            work completed, or to indicate cancellation. Implementations
+	 *            should poll the monitor at regular intervals to look for
+	 *            cancellation requests from the user.
+	 * @param want
+	 *            one or more refs advertised by this connection that the caller
+	 *            wants to store locally.
+	 * @param have
+	 *            additional objects known to exist in the destination
+	 *            repository, especially if they aren't yet reachable by the ref
+	 *            database. Connections should take this set as an addition to
+	 *            what is reachable through all Refs, not in replace of it.
+	 * @param out
+	 *            OutputStream to write sideband messages to
+	 * @throws TransportException
+	 *             objects could not be copied due to a network failure,
+	 *             protocol error, or error on remote side, or connection was
+	 *             already used for fetch.
+	 * @since 3.0
+	 */
+	public void fetch(final ProgressMonitor monitor,
+			final Collection<Ref> want, final Set<ObjectId> have,
+			OutputStream out) throws TransportException;
+
+	/**
 	 * Did the last {@link #fetch(ProgressMonitor, Collection, Set)} get tags?
 	 * <p>
 	 * Some Git aware transports are able to implicitly grab an annotated tag if
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index a50b0b9..52a9bab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -68,6 +68,8 @@ import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.internal.storage.file.PackLock;
 import org.eclipse.jgit.lib.BatchRefUpdate;
 import org.eclipse.jgit.lib.BatchingProgressMonitor;
 import org.eclipse.jgit.lib.Constants;
@@ -77,8 +79,6 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.revwalk.ObjectWalk;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.LockFile;
-import org.eclipse.jgit.storage.file.PackLock;
 
 class FetchProcess {
 	/** Transport we will fetch over. */
@@ -382,23 +382,16 @@ class FetchProcess {
 				continue;
 
 			Ref local = haveRefs.get(r.getName());
-			ObjectId obj = r.getObjectId();
-
-			if (r.getPeeledObjectId() == null) {
-				if (local != null && obj.equals(local.getObjectId()))
-					continue;
-				if (askFor.containsKey(obj) || transport.local.hasObject(obj))
-					wantTag(r);
-				else
-					additionalTags.add(r);
+			if (local != null)
+				// We already have a tag with this name, don't fetch it (even if
+				// the local is different).
 				continue;
-			}
 
-			if (local != null) {
-				if (!obj.equals(local.getObjectId()))
-					wantTag(r);
-			} else if (askFor.containsKey(r.getPeeledObjectId())
-					|| transport.local.hasObject(r.getPeeledObjectId()))
+			ObjectId obj = r.getPeeledObjectId();
+			if (obj == null)
+				obj = r.getObjectId();
+
+			if (askFor.containsKey(obj) || transport.local.hasObject(obj))
 				wantTag(r);
 			else
 				additionalTags.add(r);
@@ -419,7 +412,7 @@ class FetchProcess {
 
 	private void wantTag(final Ref r) throws TransportException {
 		want(r, new RefSpec().setSource(r.getName())
-				.setDestination(r.getName()));
+				.setDestination(r.getName()).setForceUpdate(true));
 	}
 
 	private void want(final Ref src, final RefSpec spec)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 7b55768..e16cce0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -62,6 +62,8 @@ import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.TooLargeObjectInPackException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackLock;
+import org.eclipse.jgit.internal.storage.pack.BinaryDelta;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.BatchingProgressMonitor;
 import org.eclipse.jgit.lib.Constants;
@@ -78,8 +80,6 @@ import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ObjectStream;
 import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.storage.file.PackLock;
-import org.eclipse.jgit.storage.pack.BinaryDelta;
 import org.eclipse.jgit.util.BlockList;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.NB;
@@ -428,6 +428,7 @@ public abstract class PackParser {
 	 *         {@link #setLockMessage(String)}.
 	 * @throws IOException
 	 *             the stream is malformed, or contains corrupt objects.
+	 * @since 3.0
 	 */
 	public final PackLock parse(ProgressMonitor progress) throws IOException {
 		return parse(progress, progress);
@@ -446,6 +447,7 @@ public abstract class PackParser {
 	 *         {@link #setLockMessage(String)}.
 	 * @throws IOException
 	 *             the stream is malformed, or contains corrupt objects.
+	 * @since 3.0
 	 */
 	public PackLock parse(ProgressMonitor receiving, ProgressMonitor resolving)
 			throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java
index 489ac78..c2a885f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java
@@ -44,6 +44,7 @@
 
 package org.eclipse.jgit.transport;
 
+import java.io.OutputStream;
 import java.util.Map;
 
 import org.eclipse.jgit.errors.TransportException;
@@ -111,4 +112,50 @@ public interface PushConnection extends Connection {
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates)
 			throws TransportException;
+
+	/**
+	 * Pushes to the remote repository basing on provided specification. This
+	 * possibly result in update/creation/deletion of refs on remote repository
+	 * and sending objects that remote repository need to have a consistent
+	 * objects graph from new refs.
+	 * <p>
+	 * <p>
+	 * Only one call per connection is allowed. Subsequent calls will result in
+	 * {@link TransportException}.
+	 * </p>
+	 * <p>
+	 * Implementation may use local repository to send a minimum set of objects
+	 * needed by remote repository in efficient way.
+	 * {@link Transport#isPushThin()} should be honored if applicable.
+	 * refUpdates should be filled with information about status of each update.
+	 * </p>
+	 *
+	 * @param monitor
+	 *            progress monitor to update the end-user about the amount of
+	 *            work completed, or to indicate cancellation. Implementors
+	 *            should poll the monitor at regular intervals to look for
+	 *            cancellation requests from the user.
+	 * @param refUpdates
+	 *            map of remote refnames to remote refs update
+	 *            specifications/statuses. Can't be empty. This indicate what
+	 *            refs caller want to update on remote side. Only refs updates
+	 *            with {@link Status#NOT_ATTEMPTED} should passed.
+	 *            Implementation must ensure that and appropriate status with
+	 *            optional message should be set during call. No refUpdate with
+	 *            {@link Status#AWAITING_REPORT} or {@link Status#NOT_ATTEMPTED}
+	 *            can be leaved by implementation after return from this call.
+	 * @param out
+	 *            output stream to write sideband messages to
+	 * @throws TransportException
+	 *             objects could not be copied due to a network failure,
+	 *             critical protocol error, or error on remote side, or
+	 *             connection was already used for push - new connection must be
+	 *             created. Non-critical errors concerning only isolated refs
+	 *             should be placed in refUpdates.
+	 * @since 3.0
+	 */
+	public void push(final ProgressMonitor monitor,
+			final Map<String, RemoteRefUpdate> refUpdates, OutputStream out)
+			throws TransportException;
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
index 3169bfc..53fba55 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.transport;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.text.MessageFormat;
 import java.util.Collection;
 import java.util.HashMap;
@@ -64,7 +65,7 @@ import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 /**
  * Class performing push operation on remote repository.
  *
- * @see Transport#push(ProgressMonitor, Collection)
+ * @see Transport#push(ProgressMonitor, Collection, OutputStream)
  */
 class PushProcess {
 	/** Task name for {@link ProgressMonitor} used during opening connection. */
@@ -82,6 +83,9 @@ class PushProcess {
 	/** Revision walker for checking some updates properties. */
 	private final RevWalk walker;
 
+	/** an outputstream to write messages to */
+	private final OutputStream out;
+
 	/**
 	 * Create process for specified transport and refs updates specification.
 	 *
@@ -90,13 +94,33 @@ class PushProcess {
 	 *            connection.
 	 * @param toPush
 	 *            specification of refs updates (and local tracking branches).
+	 *
 	 * @throws TransportException
 	 */
 	PushProcess(final Transport transport,
 			final Collection<RemoteRefUpdate> toPush) throws TransportException {
+		this(transport, toPush, null);
+	}
+
+	/**
+	 * Create process for specified transport and refs updates specification.
+	 * 
+	 * @param transport
+	 *            transport between remote and local repository, used to create
+	 *            connection.
+	 * @param toPush
+	 *            specification of refs updates (and local tracking branches).
+	 * @param out
+	 *            OutputStream to write messages to
+	 * @throws TransportException
+	 */
+	PushProcess(final Transport transport,
+			final Collection<RemoteRefUpdate> toPush, OutputStream out)
+			throws TransportException {
 		this.walker = new RevWalk(transport.local);
 		this.transport = transport;
 		this.toPush = new HashMap<String, RemoteRefUpdate>();
+		this.out = out;
 		for (final RemoteRefUpdate rru : toPush) {
 			if (this.toPush.put(rru.getRemoteName(), rru) != null)
 				throw new TransportException(MessageFormat.format(
@@ -138,7 +162,7 @@ class PushProcess {
 				if (transport.isDryRun())
 					modifyUpdatesForDryRun();
 				else if (!preprocessed.isEmpty())
-					connection.push(monitor, preprocessed);
+					connection.push(monitor, preprocessed, out);
 			} finally {
 				connection.close();
 				res.addMessages(connection.getMessages());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
index 53a7b02..66ffc3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2008, 2013 Shawn O. Pearce <spearce at spearce.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,8 +43,8 @@
 
 package org.eclipse.jgit.transport;
 
-import java.text.MessageFormat;
 import java.io.Serializable;
+import java.text.MessageFormat;
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
@@ -59,7 +59,7 @@ import org.eclipse.jgit.lib.Ref;
 public class RefSpec implements Serializable {
 	private static final long serialVersionUID = 1L;
 
-        /**
+	/**
 	 * Suffix for wildcard ref spec component, that indicate matching all refs
 	 * with specified prefix.
 	 */
@@ -73,7 +73,7 @@ public class RefSpec implements Serializable {
 	 * @return true if provided string is a wildcard ref spec component.
 	 */
 	public static boolean isWildcard(final String s) {
-		return s != null && s.endsWith(WILDCARD_SUFFIX);
+		return s != null && s.contains("*"); //$NON-NLS-1$
 	}
 
 	/** Does this specification ask for forced updated (rewind/reset)? */
@@ -106,13 +106,14 @@ public class RefSpec implements Serializable {
 	 * <p>
 	 * Specifications are typically one of the following forms:
 	 * <ul>
-	 * <li><code>refs/head/master</code></li>
-	 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
-	 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
-	 * <li><code>+refs/head/master</code></li>
-	 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
-	 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
-	 * <li><code>:refs/head/master</code></li>
+	 * <li><code>refs/heads/master</code></li>
+	 * <li><code>refs/heads/master:refs/remotes/origin/master</code></li>
+	 * <li><code>refs/heads/*:refs/remotes/origin/*</code></li>
+	 * <li><code>+refs/heads/master</code></li>
+	 * <li><code>+refs/heads/master:refs/remotes/origin/master</code></li>
+	 * <li><code>+refs/heads/*:refs/remotes/origin/*</code></li>
+	 * <li><code>+refs/pull/*/head:refs/remotes/origin/pr/*</code></li>
+	 * <li><code>:refs/heads/master</code></li>
 	 * </ul>
 	 *
 	 * @param spec
@@ -132,18 +133,24 @@ public class RefSpec implements Serializable {
 			s = s.substring(1);
 			if (isWildcard(s))
 				throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
-			dstName = s;
+			dstName = checkValid(s);
 		} else if (c > 0) {
-			srcName = s.substring(0, c);
-			dstName = s.substring(c + 1);
-			if (isWildcard(srcName) && isWildcard(dstName))
+			String src = s.substring(0, c);
+			String dst = s.substring(c + 1);
+			if (isWildcard(src) && isWildcard(dst)) {
+				// Both contain wildcard
 				wildcard = true;
-			else if (isWildcard(srcName) || isWildcard(dstName))
+			} else if (isWildcard(src) || isWildcard(dst)) {
+				// If either source or destination has wildcard, the other one
+				// must have as well.
 				throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
+			}
+			srcName = checkValid(src);
+			dstName = checkValid(dst);
 		} else {
 			if (isWildcard(s))
 				throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
-			srcName = s;
+			srcName = checkValid(s);
 		}
 	}
 
@@ -215,7 +222,7 @@ public class RefSpec implements Serializable {
 	 */
 	public RefSpec setSource(final String source) {
 		final RefSpec r = new RefSpec(this);
-		r.srcName = source;
+		r.srcName = checkValid(source);
 		if (isWildcard(r.srcName) && r.dstName == null)
 			throw new IllegalStateException(JGitText.get().destinationIsNotAWildcard);
 		if (isWildcard(r.srcName) != isWildcard(r.dstName))
@@ -254,7 +261,7 @@ public class RefSpec implements Serializable {
 	 */
 	public RefSpec setDestination(final String destination) {
 		final RefSpec r = new RefSpec(this);
-		r.dstName = destination;
+		r.dstName = checkValid(destination);
 		if (isWildcard(r.dstName) && r.srcName == null)
 			throw new IllegalStateException(JGitText.get().sourceIsNotAWildcard);
 		if (isWildcard(r.srcName) != isWildcard(r.dstName))
@@ -350,8 +357,7 @@ public class RefSpec implements Serializable {
 		final String psrc = srcName, pdst = dstName;
 		wildcard = false;
 		srcName = name;
-		dstName = pdst.substring(0, pdst.length() - 1)
-				+ name.substring(psrc.length() - 1);
+		dstName = expandWildcard(name, psrc, pdst);
 		return this;
 	}
 
@@ -392,8 +398,7 @@ public class RefSpec implements Serializable {
 	private RefSpec expandFromDstImp(final String name) {
 		final String psrc = srcName, pdst = dstName;
 		wildcard = false;
-		srcName = psrc.substring(0, psrc.length() - 1)
-				+ name.substring(pdst.length() - 1);
+		srcName = expandWildcard(name, pdst, psrc);
 		dstName = name;
 		return this;
 	}
@@ -414,12 +419,50 @@ public class RefSpec implements Serializable {
 		return expandFromDestination(r.getName());
 	}
 
-	private boolean match(final String refName, final String s) {
+	private boolean match(final String name, final String s) {
 		if (s == null)
 			return false;
-		if (isWildcard())
-			return refName.startsWith(s.substring(0, s.length() - 1));
-		return refName.equals(s);
+		if (isWildcard()) {
+			int wildcardIndex = s.indexOf('*');
+			String prefix = s.substring(0, wildcardIndex);
+			String suffix = s.substring(wildcardIndex + 1);
+			return name.length() > prefix.length() + suffix.length()
+					&& name.startsWith(prefix) && name.endsWith(suffix);
+		}
+		return name.equals(s);
+	}
+
+	private static String expandWildcard(String name, String patternA,
+			String patternB) {
+		int a = patternA.indexOf('*');
+		int trailingA = patternA.length() - (a + 1);
+		int b = patternB.indexOf('*');
+		String match = name.substring(a, name.length() - trailingA);
+		return patternB.substring(0, b) + match + patternB.substring(b + 1);
+	}
+
+	private static String checkValid(String spec) {
+		if (spec != null && !isValid(spec))
+			throw new IllegalArgumentException(MessageFormat.format(
+					JGitText.get().invalidRefSpec, spec));
+		return spec;
+	}
+
+	private static boolean isValid(final String s) {
+		if (s.startsWith("/")) //$NON-NLS-1$
+			return false;
+		if (s.contains("//")) //$NON-NLS-1$
+			return false;
+		int i = s.indexOf('*');
+		if (i != -1) {
+			if (s.indexOf('*', i + 1) > i)
+				return false;
+			if (i > 0 && s.charAt(i - 1) != '/')
+				return false;
+			if (i < s.length() - 1 && s.charAt(i + 1) != '/')
+				return false;
+		}
+		return true;
 	}
 
 	public int hashCode() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
index b48a8a5..cf388e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
@@ -48,6 +48,7 @@ import static org.eclipse.jgit.transport.SideBandOutputStream.HDR_SIZE;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.Writer;
 import java.text.MessageFormat;
 import java.util.regex.Matcher;
@@ -99,6 +100,8 @@ class SideBandInputStream extends InputStream {
 
 	private final Writer messages;
 
+	private final OutputStream out;
+
 	private String progressBuffer = ""; //$NON-NLS-1$
 
 	private String currentTask;
@@ -112,12 +115,13 @@ class SideBandInputStream extends InputStream {
 	private int available;
 
 	SideBandInputStream(final InputStream in, final ProgressMonitor progress,
-			final Writer messageStream) {
+			final Writer messageStream, OutputStream outputStream) {
 		rawIn = in;
 		pckIn = new PacketLineIn(rawIn);
 		monitor = progress;
 		messages = messageStream;
 		currentTask = ""; //$NON-NLS-1$
+		out = outputStream;
 	}
 
 	@Override
@@ -232,6 +236,8 @@ class SideBandInputStream extends InputStream {
 		}
 
 		messages.write(msg);
+		if (out != null)
+			out.write(msg.getBytes());
 	}
 
 	private void beginTask(final int totalWorkUnits) {
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 5a7c0a1..3c19610 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -50,6 +50,7 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -1134,6 +1135,8 @@ public abstract class Transport {
 	 *            converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
 	 *            more than 1 RemoteRefUpdate with the same remoteName is
 	 *            allowed. These objects are modified during this call.
+	 * @param out
+	 *            output stream to write messages to
 	 * @return information about results of remote refs updates, tracking refs
 	 *         updates and refs advertised by remote repository.
 	 * @throws NotSupportedException
@@ -1143,9 +1146,11 @@ public abstract class Transport {
 	 *             the remote connection could not be established or object
 	 *             copying (if necessary) failed at I/O or protocol level or
 	 *             update specification was incorrect.
+	 * @since 3.0
 	 */
 	public PushResult push(final ProgressMonitor monitor,
-			Collection<RemoteRefUpdate> toPush) throws NotSupportedException,
+			Collection<RemoteRefUpdate> toPush, OutputStream out)
+			throws NotSupportedException,
 			TransportException {
 		if (toPush == null || toPush.isEmpty()) {
 			// If the caller did not ask for anything use the defaults.
@@ -1158,11 +1163,57 @@ public abstract class Transport {
 			if (toPush.isEmpty())
 				throw new TransportException(JGitText.get().nothingToPush);
 		}
-		final PushProcess pushProcess = new PushProcess(this, toPush);
+		final PushProcess pushProcess = new PushProcess(this, toPush, out);
 		return pushProcess.execute(monitor);
 	}
 
 	/**
+	 * Push objects and refs from the local repository to the remote one.
+	 * <p>
+	 * This is a utility function providing standard push behavior. It updates
+	 * remote refs and sends necessary objects according to remote ref update
+	 * specification. After successful remote ref update, associated locally
+	 * stored tracking branch is updated if set up accordingly. Detailed
+	 * operation result is provided after execution.
+	 * <p>
+	 * For setting up remote ref update specification from ref spec, see helper
+	 * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs
+	 * ({@link #REFSPEC_TAGS}, {@link #REFSPEC_PUSH_ALL}) or consider using
+	 * directly {@link RemoteRefUpdate} for more possibilities.
+	 * <p>
+	 * When {@link #isDryRun()} is true, result of this operation is just
+	 * estimation of real operation result, no real action is performed.
+	 *
+	 * @see RemoteRefUpdate
+	 *
+	 * @param monitor
+	 *            progress monitor to inform the user about our processing
+	 *            activity. Must not be null. Use {@link NullProgressMonitor} if
+	 *            progress updates are not interesting or necessary.
+	 * @param toPush
+	 *            specification of refs to push. May be null or the empty
+	 *            collection to use the specifications from the RemoteConfig
+	 *            converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
+	 *            more than 1 RemoteRefUpdate with the same remoteName is
+	 *            allowed. These objects are modified during this call.
+	 *
+	 * @return information about results of remote refs updates, tracking refs
+	 *         updates and refs advertised by remote repository.
+	 * @throws NotSupportedException
+	 *             this transport implementation does not support pushing
+	 *             objects.
+	 * @throws TransportException
+	 *             the remote connection could not be established or object
+	 *             copying (if necessary) failed at I/O or protocol level or
+	 *             update specification was incorrect.
+	 */
+	public PushResult push(final ProgressMonitor monitor,
+			Collection<RemoteRefUpdate> toPush) throws NotSupportedException,
+			TransportException {
+		return push(monitor, toPush, null);
+	}
+
+	/**
 	 * Convert push remote refs update specification from {@link RefSpec} form
 	 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
 	 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 3f1ceba..baaf886 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008-2010, Google Inc.
  * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2013, Matthias Sohn <matthias.sohn at sap.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -83,8 +84,10 @@ import java.util.TreeMap;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
+import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
 
@@ -93,6 +96,7 @@ import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.RefDirectory;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Config.SectionParser;
 import org.eclipse.jgit.lib.Constants;
@@ -102,7 +106,6 @@ import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.SymbolicRef;
-import org.eclipse.jgit.storage.file.RefDirectory;
 import org.eclipse.jgit.util.HttpSupport;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.RawParseUtils;
@@ -532,6 +535,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
 			ctx.init(null, trustAllCerts, null);
 			final HttpsURLConnection sslConn = (HttpsURLConnection) conn;
 			sslConn.setSSLSocketFactory(ctx.getSocketFactory());
+			sslConn.setHostnameVerifier(new DummyHostnameVerifier());
 		} catch (KeyManagementException e) {
 			throw new IOException(e.getMessage());
 		} catch (NoSuchAlgorithmException e) {
@@ -735,12 +739,12 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
 
 		@Override
 		protected void doFetch(final ProgressMonitor monitor,
-				final Collection<Ref> want, final Set<ObjectId> have)
-				throws TransportException {
+				final Collection<Ref> want, final Set<ObjectId> have,
+				final OutputStream outputStream) throws TransportException {
 			try {
 				svc = new MultiRequestService(SVC_UPLOAD_PACK);
 				init(svc.getInputStream(), svc.getOutputStream());
-				super.doFetch(monitor, want, have);
+				super.doFetch(monitor, want, have, outputStream);
 			} finally {
 				svc = null;
 			}
@@ -764,11 +768,11 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
 		}
 
 		protected void doPush(final ProgressMonitor monitor,
-				final Map<String, RemoteRefUpdate> refUpdates)
-				throws TransportException {
+				final Map<String, RemoteRefUpdate> refUpdates,
+				OutputStream outputStream) throws TransportException {
 			final Service svc = new MultiRequestService(SVC_RECEIVE_PACK);
 			init(svc.getInputStream(), svc.getOutputStream());
-			super.doPush(monitor, refUpdates);
+			super.doPush(monitor, refUpdates, outputStream);
 		}
 	}
 
@@ -980,4 +984,11 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
 			// no check
 		}
 	}
+
+	private static class DummyHostnameVerifier implements HostnameVerifier {
+		public boolean verify(String hostname, SSLSession session) {
+			// always accept
+			return true;
+		}
+	}
 }
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 c783898..2f615e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -63,8 +63,8 @@ import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryBuilder;
 import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.storage.file.FileRepository;
 import org.eclipse.jgit.util.io.MessageWriter;
 import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
 import org.eclipse.jgit.util.io.StreamCopyThread;
@@ -201,7 +201,7 @@ class TransportLocal extends Transport implements PackTransport {
 
 			final Repository dst;
 			try {
-				dst = new FileRepository(remoteGitDir);
+				dst = new RepositoryBuilder().setGitDir(remoteGitDir).build();
 			} catch (IOException err) {
 				throw new TransportException(uri, JGitText.get().notAGitDirectory);
 			}
@@ -341,7 +341,7 @@ class TransportLocal extends Transport implements PackTransport {
 
 			final Repository dst;
 			try {
-				dst = new FileRepository(remoteGitDir);
+				dst = new RepositoryBuilder().setGitDir(remoteGitDir).build();
 			} catch (IOException err) {
 				throw new TransportException(uri, JGitText.get().notAGitDirectory);
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 9befb92..deebe66 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -3,6 +3,7 @@
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg at dewire.com>
  * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
  * Copyright (C) 2010, Christian Halstrick <christian.halstrick at sap.com>
+ * Copyright (C) 2013, Robin Stocker <robin at nibor.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -87,7 +88,7 @@ public class URIish implements Serializable {
 	 * Part of a pattern which matches the host part of URIs. Defines one
 	 * capturing group containing the host name.
 	 */
-	private static final String HOST_P = "([^\\\\/:]+)"; //$NON-NLS-1$
+	private static final String HOST_P = "((?:[^\\\\/:]+)|(?:\\[[0-9a-f:]+\\]))";
 
 	/**
 	 * Part of a pattern which matches the optional port part of URIs. Defines
@@ -111,7 +112,7 @@ public class URIish implements Serializable {
 	 * Part of a pattern which matches a relative path. Relative paths don't
 	 * start with slash or drive letters. Defines no capturing group.
 	 */
-	private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/])*[^\\\\/]+[\\\\/]?)"; //$NON-NLS-1$
+	private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*[^\\\\/]+[\\\\/]*)"; //$NON-NLS-1$
 
 	/**
 	 * Part of a pattern which matches a relative or absolute path. Defines no
@@ -698,7 +699,7 @@ public class URIish implements Serializable {
 		if ("file".equals(scheme) || LOCAL_FILE.matcher(s).matches()) //$NON-NLS-1$
 			elements = s.split("[\\" + File.separatorChar + "/]"); //$NON-NLS-1$ //$NON-NLS-2$
 		else
-			elements = s.split("/"); //$NON-NLS-1$
+			elements = s.split("/+"); //$NON-NLS-1$
 		if (elements.length == 0)
 			throw new IllegalArgumentException();
 		String result = elements[elements.length - 1];
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 74801fe..5347eb7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.PackProtocolException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
@@ -77,7 +78,6 @@ import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
 import org.eclipse.jgit.storage.pack.PackConfig;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.transport.BasePackFetchConnection.MultiAck;
 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
 import org.eclipse.jgit.util.io.InterruptTimer;
@@ -537,6 +537,7 @@ public class UploadPack {
 	 * @return statistics about pack output, if a pack was sent. Null if no pack
 	 *         was sent, such as during the negotation phase of a smart HTTP
 	 *         connection, or if the client was already up-to-date.
+	 * @since 3.0
 	 */
 	public PackWriter.Statistics getPackStatistics() {
 		return statistics;
@@ -787,74 +788,24 @@ public class UploadPack {
 		preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
 		if (peerHas.isEmpty())
 			return last;
+		if (wantAll.isEmpty() && !wantIds.isEmpty())
+			parseWants();
 
-		List<ObjectId> toParse = peerHas;
-		HashSet<ObjectId> peerHasSet = null;
-		boolean needMissing = false;
 		sentReady = false;
-
-		if (wantAll.isEmpty() && !wantIds.isEmpty()) {
-			// We have not yet parsed the want list. Parse it now.
-			peerHasSet = new HashSet<ObjectId>(peerHas);
-			int cnt = wantIds.size() + peerHasSet.size();
-			toParse = new ArrayList<ObjectId>(cnt);
-			toParse.addAll(wantIds);
-			toParse.addAll(peerHasSet);
-			needMissing = true;
-		}
-
-		Set<RevObject> notAdvertisedWants = null;
 		int haveCnt = 0;
-		AsyncRevObjectQueue q = walk.parseAny(toParse, needMissing);
+		walk.getObjectReader().setAvoidUnreachableObjects(true);
+		AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
 		try {
 			for (;;) {
 				RevObject obj;
 				try {
 					obj = q.next();
 				} catch (MissingObjectException notFound) {
-					ObjectId id = notFound.getObjectId();
-					if (wantIds.contains(id)) {
-						String msg = MessageFormat.format(
-								JGitText.get().wantNotValid, id.name());
-						throw new PackProtocolException(msg, notFound);
-					}
 					continue;
 				}
 				if (obj == null)
 					break;
 
-				// If the object is still found in wantIds, the want
-				// list wasn't parsed earlier, and was done in this batch.
-				//
-				if (wantIds.remove(obj)) {
-					if (!advertised.contains(obj) && requestPolicy != RequestPolicy.ANY) {
-						if (notAdvertisedWants == null)
-							notAdvertisedWants = new HashSet<RevObject>();
-						notAdvertisedWants.add(obj);
-					}
-
-					if (!obj.has(WANT)) {
-						obj.add(WANT);
-						wantAll.add(obj);
-					}
-
-					if (!(obj instanceof RevCommit))
-						obj.add(SATISFIED);
-
-					if (obj instanceof RevTag) {
-						RevObject target = walk.peel(obj);
-						if (target instanceof RevCommit) {
-							if (!target.has(WANT)) {
-								target.add(WANT);
-								wantAll.add(target);
-							}
-						}
-					}
-
-					if (!peerHasSet.contains(obj))
-						continue;
-				}
-
 				last = obj;
 				haveCnt++;
 
@@ -889,25 +840,7 @@ public class UploadPack {
 			}
 		} finally {
 			q.release();
-		}
-
-		// If the client asked for non advertised object, check our policy.
-		if (notAdvertisedWants != null && !notAdvertisedWants.isEmpty()) {
-			switch (requestPolicy) {
-			case ADVERTISED:
-			default:
-				throw new PackProtocolException(MessageFormat.format(
-						JGitText.get().wantNotValid,
-						notAdvertisedWants.iterator().next().name()));
-
-			case REACHABLE_COMMIT:
-				checkNotAdvertisedWants(notAdvertisedWants);
-				break;
-
-			case ANY:
-				// Allow whatever was asked for.
-				break;
-			}
+			walk.getObjectReader().setAvoidUnreachableObjects(false);
 		}
 
 		int missCnt = peerHas.size() - haveCnt;
@@ -952,7 +885,61 @@ public class UploadPack {
 		return last;
 	}
 
-	private void checkNotAdvertisedWants(Set<RevObject> notAdvertisedWants)
+	private void parseWants() throws IOException {
+		AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
+		try {
+			List<RevCommit> checkReachable = null;
+			RevObject obj;
+			while ((obj = q.next()) != null) {
+				if (!advertised.contains(obj)) {
+					switch (requestPolicy) {
+					case ADVERTISED:
+					default:
+						throw new PackProtocolException(MessageFormat.format(
+								JGitText.get().wantNotValid, obj));
+					case REACHABLE_COMMIT:
+						if (!(obj instanceof RevCommit)) {
+							throw new PackProtocolException(MessageFormat.format(
+								JGitText.get().wantNotValid, obj));
+						}
+						if (checkReachable == null)
+							checkReachable = new ArrayList<RevCommit>();
+						checkReachable.add((RevCommit) obj);
+						break;
+					case ANY:
+						break;
+					}
+				}
+				want(obj);
+
+				if (!(obj instanceof RevCommit))
+					obj.add(SATISFIED);
+				if (obj instanceof RevTag) {
+					obj = walk.peel(obj);
+					if (obj instanceof RevCommit)
+						want(obj);
+				}
+			}
+			if (checkReachable != null)
+				checkNotAdvertisedWants(checkReachable);
+			wantIds.clear();
+		} catch (MissingObjectException notFound) {
+			ObjectId id = notFound.getObjectId();
+			throw new PackProtocolException(MessageFormat.format(
+					JGitText.get().wantNotValid, id.name()), notFound);
+		} finally {
+			q.release();
+		}
+	}
+
+	private void want(RevObject obj) {
+		if (!obj.has(WANT)) {
+			obj.add(WANT);
+			wantAll.add(obj);
+		}
+	}
+
+	private void checkNotAdvertisedWants(List<RevCommit> notAdvertisedWants)
 			throws MissingObjectException, IncorrectObjectTypeException, IOException {
 		// Walk the requested commits back to the advertised commits.
 		// If any commit exists, a branch was deleted or rewound and
@@ -960,15 +947,8 @@ public class UploadPack {
 		// If the requested commit is merged into an advertised branch
 		// it will be marked UNINTERESTING and no commits return.
 
-		for (RevObject o : notAdvertisedWants) {
-			if (!(o instanceof RevCommit)) {
-				throw new PackProtocolException(MessageFormat.format(
-						JGitText.get().wantNotValid,
-						notAdvertisedWants.iterator().next().name()));
-			}
-			walk.markStart((RevCommit) o);
-		}
-
+		for (RevCommit c : notAdvertisedWants)
+			walk.markStart(c);
 		for (ObjectId id : advertised) {
 			try {
 				walk.markUninteresting(walk.parseCommit(id));
@@ -1136,7 +1116,9 @@ public class UploadPack {
 			cfg = new PackConfig(db);
 		final PackWriter pw = new PackWriter(cfg, walk.getObjectReader());
 		try {
+			pw.setIndexDisabled(true);
 			pw.setUseCachedPacks(true);
+			pw.setUseBitmaps(true);
 			pw.setReuseDeltaCommits(true);
 			pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
 			pw.setThin(options.contains(OPTION_THIN_PACK));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
index 45f91ed..99fa6e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
@@ -43,7 +43,7 @@
 
 package org.eclipse.jgit.transport;
 
-import org.eclipse.jgit.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 
 /**
  * Logs activity that occurred within {@link UploadPack}.
@@ -66,6 +66,7 @@ public interface UploadPackLogger {
 	 *
 	 * @param stats
 	 *            the statistics after sending a pack to the client.
+	 * @since 3.0
 	 */
 	public void onPackStatistics(PackWriter.Statistics stats);
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
index a6a2dad..3f14cc6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
@@ -45,7 +45,7 @@ package org.eclipse.jgit.transport;
 
 import java.util.List;
 
-import org.eclipse.jgit.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 
 /**
  * {@link UploadPackLogger} that delegates to a list of other loggers.
@@ -78,6 +78,9 @@ public class UploadPackLoggerChain implements UploadPackLogger {
 			return new UploadPackLoggerChain(newLoggers, i);
 	}
 
+	/**
+	 * @since 3.0
+	 */
 	public void onPackStatistics(PackWriter.Statistics stats) {
 		for (int i = 0; i < count; i++)
 			loggers[i].onPackStatistics(stats);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index e1ce5d6..972ee28 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -63,6 +63,10 @@ import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackLock;
+import org.eclipse.jgit.internal.storage.file.UnpackedObject;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
@@ -82,10 +86,6 @@ import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.ObjectDirectory;
-import org.eclipse.jgit.storage.file.PackIndex;
-import org.eclipse.jgit.storage.file.PackLock;
-import org.eclipse.jgit.storage.file.UnpackedObject;
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.util.FileUtils;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
index 78fe30a..02960bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
@@ -58,6 +58,7 @@ import java.util.TreeMap;
 
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -67,7 +68,6 @@ import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
 import org.eclipse.jgit.lib.RefWriter;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.storage.pack.PackWriter;
 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
 
@@ -137,6 +137,12 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates)
 			throws TransportException {
+		push(monitor, refUpdates, null);
+	}
+
+	public void push(final ProgressMonitor monitor,
+			final Map<String, RemoteRefUpdate> refUpdates, OutputStream out)
+			throws TransportException {
 		markStartedOperation();
 		packNames = null;
 		newRefs = new TreeMap<String, Ref>(getRefsMap());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
index 59a9f56..e47913e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
@@ -57,12 +57,12 @@ import java.util.Map;
 
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.RefDirectory;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.storage.file.RefDirectory;
 import org.eclipse.jgit.util.IO;
 
 /**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 4cf79c0..2dd3c3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -425,7 +425,14 @@ public abstract class AbstractTreeIterator {
 		return TreeWalk.pathOf(this);
 	}
 
-	/** @return the internal buffer holding the current path. */
+	/**
+	 * Get the current entry path buffer.
+	 * <p>
+	 * Note that the returned byte[] has to be used together with
+	 * {@link #getEntryPathLength()} (only use bytes up to this length).
+	 *
+	 * @return the internal buffer holding the current path.
+	 */
 	public byte[] getEntryPathBuffer() {
 		return path;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 1858626..07ba9d7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -2,6 +2,7 @@
  * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
  * Copyright (C) 2010, Christian Halstrick <christian.halstrick at sap.com>
  * Copyright (C) 2010, Matthias Sohn <matthias.sohn at sap.com>
+ * Copyright (C) 2012-2013, Robin Rosenberg
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -75,6 +76,7 @@ import org.eclipse.jgit.lib.CoreConfig;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.CoreConfig.CheckStat;
 import org.eclipse.jgit.submodule.SubmoduleWalk;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.IO;
@@ -754,15 +756,23 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
 
 		// Git under windows only stores seconds so we round the timestamp
 		// Java gives us if it looks like the timestamp in index is seconds
-		// only. Otherwise we compare the timestamp at millisecond precision.
+		// only. Otherwise we compare the timestamp at millisecond precision,
+		// unless core.checkstat is set to "minimal", in which case we only
+		// compare the whole second part.
 		long cacheLastModified = entry.getLastModified();
 		long fileLastModified = getEntryLastModified();
-		if (cacheLastModified % 1000 == 0)
-			fileLastModified = fileLastModified - fileLastModified % 1000;
+		long lastModifiedMillis = fileLastModified % 1000;
+		long cacheMillis = cacheLastModified % 1000;
+		if (getOptions().getCheckStat() == CheckStat.MINIMAL) {
+			fileLastModified = fileLastModified - lastModifiedMillis;
+			cacheLastModified = cacheLastModified - cacheMillis;
+		} else if (cacheMillis == 0)
+			fileLastModified = fileLastModified - lastModifiedMillis;
 		// Some Java version on Linux return whole seconds only even when
 		// the file systems supports more precision.
-		else if (fileLastModified % 1000 == 0)
-			cacheLastModified = cacheLastModified - cacheLastModified % 1000;
+		else if (lastModifiedMillis == 0)
+			cacheLastModified = cacheLastModified - cacheMillis;
+
 		if (fileLastModified != cacheLastModified)
 			return MetadataDiff.DIFFER_BY_TIMESTAMP;
 		else if (!entry.isSmudged())
@@ -1064,8 +1074,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
 				loadRulesFromFile(r, excludesfile);
 			}
 
-			File exclude = fs
-					.resolve(repository.getDirectory(), "info/exclude"); //$NON-NLS-1$
+			File exclude = fs.resolve(repository.getDirectory(),
+					Constants.INFO_EXCLUDE);
 			loadRulesFromFile(r, exclude);
 
 			return r.getRules().isEmpty() ? null : r;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
index dafb3a1..15bfb91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010, Marc Strapetz <marc.strapetz at syntevo.com>
+ * Copyright (C) 2012-2013, Robin Rosenberg
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -46,6 +47,7 @@ import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Config.SectionParser;
 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
+import org.eclipse.jgit.lib.CoreConfig.CheckStat;
 
 /** Options used by the {@link WorkingTreeIterator}. */
 public class WorkingTreeOptions {
@@ -60,11 +62,15 @@ public class WorkingTreeOptions {
 
 	private final AutoCRLF autoCRLF;
 
+	private final CheckStat checkStat;
+
 	private WorkingTreeOptions(final Config rc) {
 		fileMode = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
 				ConfigConstants.CONFIG_KEY_FILEMODE, true);
 		autoCRLF = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.FALSE);
+		checkStat = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_CHECKSTAT, CheckStat.DEFAULT);
 	}
 
 	/** @return true if the execute bit on working files should be trusted. */
@@ -76,4 +82,12 @@ public class WorkingTreeOptions {
 	public AutoCRLF getAutoCRLF() {
 		return autoCRLF;
 	}
+
+	/**
+	 * @return how stat data is compared
+	 * @since 3.0
+	 */
+	public CheckStat getCheckStat() {
+		return checkStat;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
index 5a35359..91251e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
@@ -53,6 +53,10 @@ import org.eclipse.jgit.util.RawParseUtils;
  * {@link PathFilterGroup.Group}. Most methods assume the hash is already know
  * and therefore requires the caller to supply it beforehand. The implementation
  * is a loose derivative of ObjectIdSubclassMap.
+ * <p>
+ * The class is only intended for use by PathFilterGroup.
+ * <p>
+ * The arrays stored may not be changed after adding.
  */
 class ByteArraySet {
 
@@ -87,11 +91,11 @@ class ByteArraySet {
 		return null;
 	}
 
-	private static boolean equals(byte[] a, byte[] b, int length) {
-		if (a.length != length || b.length < length)
+	private static boolean equals(byte[] storedObj, byte[] toFind, int length) {
+		if (storedObj.length != length || toFind.length < length)
 			return false;
 		for (int i = 0; i < length; ++i) {
-			if (a[i] != b[i])
+			if (storedObj[i] != toFind[i])
 				return false;
 		}
 		return true;
@@ -125,7 +129,8 @@ class ByteArraySet {
 	 * </pre>
 	 *
 	 * @param newValue
-	 *            the array to store.
+	 *            the array to store by reference if the length is the same as
+	 *            the length parameter
 	 * @param length
 	 *            The number of bytes in newValue that are used
 	 * @param hash
@@ -246,6 +251,9 @@ class ByteArraySet {
 		return -1;
 	}
 
+	/**
+	 * An incremental hash function.
+	 */
 	static class Hasher {
 		private int hash;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
index 66d9f87..bdfde0b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
@@ -208,6 +208,19 @@ public class PathFilterGroup {
 				if (compare(max, pf.pathRaw) < 0)
 					max = pf.pathRaw;
 			}
+			// Adjust max for the git sort order. A path we compare
+			// with may end with a slash at any position (but the
+			// first, but we ignore that here since it's not relevant).
+			// Such paths must be included in the processing
+			// before we can give up and throw a StopWalkException.
+			byte[] newMax = new byte[max.length + 1];
+			for (int i = 0; i < max.length; ++i)
+				if ((max[i] & 0xFF) < '/')
+					newMax[i] = '/';
+				else
+					newMax[i] = max[i];
+			newMax[newMax.length - 1] = '/';
+			max = newMax;
 		}
 
 		private static int compare(byte[] a, byte[] b) {
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 ae61978..1b68801 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -53,11 +53,51 @@ import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.eclipse.jgit.errors.SymlinksNotSupportedException;
+import org.eclipse.jgit.internal.JGitText;
+
 /** Abstraction to support various file system operations not in Java. */
 public abstract class FS {
+	/**
+	 * This class creates FS instances. It will be overridden by a Java7 variant
+	 * if such can be detected in {@link #detect(Boolean)}.
+	 *
+	 * @since 3.0
+	 */
+	public static class FSFactory {
+		/**
+		 * Constructor
+		 */
+		protected FSFactory() {
+			// empty
+		}
+
+		/**
+		 * Detect the file system
+		 *
+		 * @param cygwinUsed
+		 * @return FS instance
+		 */
+		public FS detect(Boolean cygwinUsed) {
+			if (SystemReader.getInstance().isWindows()) {
+				if (cygwinUsed == null)
+					cygwinUsed = Boolean.valueOf(FS_Win32_Cygwin.isCygwin());
+				if (cygwinUsed.booleanValue())
+					return new FS_Win32_Cygwin();
+				else
+					return new FS_Win32();
+			} else if (FS_POSIX_Java6.hasExecute())
+				return new FS_POSIX_Java6();
+			else
+				return new FS_POSIX_Java5();
+		}
+	}
+
 	/** The auto-detected implementation selected for this operating system and JRE. */
 	public static final FS DETECTED = detect();
 
+	private static FSFactory factory;
+
 	/**
 	 * Auto-detect the appropriate file system abstraction.
 	 *
@@ -89,17 +129,25 @@ public abstract class FS {
 	 * @return detected file system abstraction
 	 */
 	public static FS detect(Boolean cygwinUsed) {
-		if (SystemReader.getInstance().isWindows()) {
-			if (cygwinUsed == null)
-				cygwinUsed = Boolean.valueOf(FS_Win32_Cygwin.isCygwin());
-			if (cygwinUsed.booleanValue())
-				return new FS_Win32_Cygwin();
-			else
-				return new FS_Win32();
-		} else if (FS_POSIX_Java6.hasExecute())
-			return new FS_POSIX_Java6();
-		else
-			return new FS_POSIX_Java5();
+		if (factory == null) {
+			try {
+				Class<?> activatorClass = Class
+						.forName("org.eclipse.jgit.util.Java7FSFactory"); //$NON-NLS-1$
+				// found Java7
+				factory = (FSFactory) activatorClass.newInstance();
+			} catch (ClassNotFoundException e) {
+				// Java7 module not found
+				factory = new FS.FSFactory();
+				// Silently ignore failure to find Java7 FS factory
+			} catch (UnsupportedClassVersionError e) {
+				// Java7 module not accessible
+				factory = new FS.FSFactory();
+			} catch (Exception e) {
+				factory = new FS.FSFactory();
+				throw new Error(e);
+			}
+		}
+		return factory.detect(cygwinUsed);
 	}
 
 	private volatile Holder<File> userHome;
@@ -136,6 +184,17 @@ public abstract class FS {
 	public abstract boolean supportsExecute();
 
 	/**
+	 * Does this operating system and JRE supports symbolic links. The
+	 * capability to handle symbolic links is detected at runtime.
+	 *
+	 * @return true if symbolic links may be used
+	 * @since 3.0
+	 */
+	public boolean supportsSymlinks() {
+		return false;
+	}
+
+	/**
 	 * Is this file system case sensitive
 	 *
 	 * @return true if this implementation is case sensitive
@@ -147,6 +206,10 @@ public abstract class FS {
 	 * <p>
 	 * Not all platforms and JREs support executable flags on files. If the
 	 * feature is unsupported this method will always return false.
+	 * <p>
+	 * <em>If the platform supports symbolic links and <code>f</code> is a symbolic link
+	 * this method returns false, rather than the state of the executable flags
+	 * on the target file.</em>
 	 *
 	 * @param f
 	 *            abstract path to test.
@@ -170,6 +233,46 @@ public abstract class FS {
 	public abstract boolean setExecute(File f, boolean canExec);
 
 	/**
+	 * Get the last modified time of a file system object. If the OS/JRE support
+	 * symbolic links, the modification time of the link is returned, rather
+	 * than that of the link target.
+	 *
+	 * @param f
+	 * @return last modified time of f
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public long lastModified(File f) throws IOException {
+		return f.lastModified();
+	}
+
+	/**
+	 * Set the last modified time of a file system object. If the OS/JRE support
+	 * symbolic links, the link is modified, not the target,
+	 *
+	 * @param f
+	 * @param time
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public void setLastModified(File f, long time) throws IOException {
+		f.setLastModified(time);
+	}
+
+	/**
+	 * Get the length of a file or link, If the OS/JRE supports symbolic links
+	 * it's the length of the link, else the length of the target.
+	 *
+	 * @param path
+	 * @return length of a file
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public long length(File path) throws IOException {
+		return path.length();
+	}
+
+	/**
 	 * Resolve this file to its actual path name that the JRE can use.
 	 * <p>
 	 * This method can be relatively expensive. Computing a translation may
@@ -260,8 +363,9 @@ public abstract class FS {
 	 * @param lookFor
 	 *            Files to search for in the given path
 	 * @return the first match found, or null
+	 * @since 3.0
 	 **/
-	static File searchPath(final String path, final String... lookFor) {
+	protected static File searchPath(final String path, final String... lookFor) {
 		if (path == null)
 			return null;
 
@@ -398,6 +502,103 @@ public abstract class FS {
 	}
 
 	/**
+	 * Check if a file is a symbolic link and read it
+	 *
+	 * @param path
+	 * @return target of link or null
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public String readSymLink(File path) throws IOException {
+		throw new SymlinksNotSupportedException(
+				JGitText.get().errorSymlinksNotSupported);
+	}
+
+	/**
+	 * @param path
+	 * @return true if the path is a symbolic link (and we support these)
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public boolean isSymLink(File path) throws IOException {
+		return false;
+	}
+
+	/**
+	 * Tests if the path exists, in case of a symbolic link, true even if the
+	 * target does not exist
+	 *
+	 * @param path
+	 * @return true if path exists
+	 * @since 3.0
+	 */
+	public boolean exists(File path) {
+		return path.exists();
+	}
+
+	/**
+	 * Check if path is a directory. If the OS/JRE supports symbolic links and
+	 * path is a symbolic link to a directory, this method returns false.
+	 *
+	 * @param path
+	 * @return true if file is a directory,
+	 * @since 3.0
+	 */
+	public boolean isDirectory(File path) {
+		return path.isDirectory();
+	}
+
+	/**
+	 * Examine if path represents a regular file. If the OS/JRE supports
+	 * symbolic links the test returns false if path represents a symbolic link.
+	 *
+	 * @param path
+	 * @return true if path represents a regular file
+	 * @since 3.0
+	 */
+	public boolean isFile(File path) {
+		return path.isFile();
+	}
+
+	/**
+	 * @param path
+	 * @return true if path is hidden, either starts with . on unix or has the
+	 *         hidden attribute in windows
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public boolean isHidden(File path) throws IOException {
+		return path.isHidden();
+	}
+
+	/**
+	 * Set the hidden attribute for file whose name starts with a period.
+	 *
+	 * @param path
+	 * @param hidden
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public void setHidden(File path, boolean hidden) throws IOException {
+		if (!path.getName().startsWith(".")) //$NON-NLS-1$
+			throw new IllegalArgumentException(
+					"Hiding only allowed for names that start with a period");
+	}
+
+	/**
+	 * Create a symbolic link
+	 *
+	 * @param path
+	 * @param target
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public void createSymLink(File path, String target) throws IOException {
+		throw new SymlinksNotSupportedException(
+				JGitText.get().errorSymlinksNotSupported);
+	}
+
+	/**
 	 * Initialize a ProcesssBuilder to run a command using the system shell.
 	 *
 	 * @param cmd
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index b6b5595..b7de056 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -43,12 +43,19 @@
 package org.eclipse.jgit.util;
 
 import java.io.File;
+import java.io.IOException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-abstract class FS_POSIX extends FS {
+
+/**
+ * Base FS for POSIX based systems
+ *
+ * @since 3.0
+ */
+public abstract class FS_POSIX extends FS {
 	@Override
 	protected File discoverGitPrefix() {
 		String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$
@@ -75,11 +82,20 @@ abstract class FS_POSIX extends FS {
 		return null;
 	}
 
-	FS_POSIX() {
+	/**
+	 * Default constructor
+	 */
+	protected FS_POSIX() {
 		super();
 	}
 
-	FS_POSIX(FS src) {
+	/**
+	 * Constructor
+	 *
+	 * @param src
+	 *            FS to copy some settings from
+	 */
+	protected FS_POSIX(FS src) {
 		super(src);
 	}
 
@@ -89,6 +105,11 @@ abstract class FS_POSIX extends FS {
 	}
 
 	@Override
+	public void setHidden(File path, boolean hidden) throws IOException {
+		// Do nothing
+	}
+
+	@Override
 	public ProcessBuilder runInShell(String cmd, String[] args) {
 		List<String> argv = new ArrayList<String>(4 + args.length);
 		argv.add("sh"); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
index c79bbe6..46ea2aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
@@ -45,12 +45,27 @@ package org.eclipse.jgit.util;
 
 import java.io.File;
 
-class FS_POSIX_Java5 extends FS_POSIX {
-	FS_POSIX_Java5() {
+
+/**
+ * FS implementaton for Java5
+ *
+ * @since 3.0
+ */
+public class FS_POSIX_Java5 extends FS_POSIX {
+	/**
+	 * Constructor
+	 */
+	public FS_POSIX_Java5() {
 		super();
 	}
 
-	FS_POSIX_Java5(FS src) {
+	/**
+	 * Constructor
+	 *
+	 * @param src
+	 *            instance whose attributes to copy
+	 */
+	public FS_POSIX_Java5(FS src) {
 		super(src);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
index 0502547..47a7a68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
@@ -49,7 +49,13 @@ import java.io.File;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
-class FS_POSIX_Java6 extends FS_POSIX {
+
+/**
+ * FS implementation for POSIX systems using Java6
+ *
+ * @since 3.0
+ */
+public class FS_POSIX_Java6 extends FS_POSIX {
 	private static final Method canExecute;
 
 	private static final Method setExecute;
@@ -59,7 +65,11 @@ class FS_POSIX_Java6 extends FS_POSIX {
 		setExecute = needMethod(File.class, "setExecutable", Boolean.TYPE); //$NON-NLS-1$
 	}
 
-	static boolean hasExecute() {
+	/**
+	 * @return true if Java has the ability to set and get the executable flag
+	 *         on files
+	 */
+	public static boolean hasExecute() {
 		return canExecute != null && setExecute != null;
 	}
 
@@ -74,11 +84,20 @@ class FS_POSIX_Java6 extends FS_POSIX {
 		}
 	}
 
-	FS_POSIX_Java6() {
+	/**
+	 * Constructor
+	 */
+	public FS_POSIX_Java6() {
 		super();
 	}
 
-	FS_POSIX_Java6(FS src) {
+	/**
+	 * Constructor
+	 *
+	 * @param src
+	 *            instance whose attributes to copy
+	 */
+	public FS_POSIX_Java6(FS src) {
 		super(src);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index 73f9161..5822dcf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -50,12 +50,27 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-class FS_Win32 extends FS {
-	FS_Win32() {
+
+/**
+ * FS implementation for Windows
+ *
+ * @since 3.0
+ */
+public class FS_Win32 extends FS {
+	/**
+	 * Constructor
+	 */
+	public FS_Win32() {
 		super();
 	}
 
-	FS_Win32(FS src) {
+	/**
+	 * Constructor
+	 *
+	 * @param src
+	 *            instance whose attributes to copy
+	 */
+	protected FS_Win32(FS src) {
 		super(src);
 	}
 
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 af2d5a9..81f2001 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
@@ -50,10 +50,19 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-class FS_Win32_Cygwin extends FS_Win32 {
+
+/**
+ * FS implementation for Cygwin on Windows
+ *
+ * @since 3.0
+ */
+public class FS_Win32_Cygwin extends FS_Win32 {
 	private static String cygpath;
 
-	static boolean isCygwin() {
+	/**
+	 * @return true if cygwin is found
+	 */
+	public static boolean isCygwin() {
 		final String path = AccessController
 				.doPrivileged(new PrivilegedAction<String>() {
 					public String run() {
@@ -68,11 +77,20 @@ class FS_Win32_Cygwin extends FS_Win32 {
 		return cygpath != null;
 	}
 
-	FS_Win32_Cygwin() {
+	/**
+	 * Constructor
+	 */
+	public FS_Win32_Cygwin() {
 		super();
 	}
 
-	FS_Win32_Cygwin(FS src) {
+	/**
+	 * Constructor
+	 *
+	 * @param src
+	 *            instance whose attributes to copy
+	 */
+	protected FS_Win32_Cygwin(FS src) {
 		super(src);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 11215c8..834cc85 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -49,6 +49,8 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.channels.FileLock;
 import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.eclipse.jgit.internal.JGitText;
 
@@ -84,6 +86,14 @@ public class FileUtils {
 	public static final int IGNORE_ERRORS = 8;
 
 	/**
+	 * Option to only delete empty directories. This option can be combined with
+	 * {@link #RECURSIVE}
+	 *
+	 * @since 3.0
+	 */
+	public static final int EMPTY_DIRECTORIES_ONLY = 16;
+
+	/**
 	 * Delete file or empty folder
 	 *
 	 * @param f
@@ -116,18 +126,46 @@ public class FileUtils {
 	 *             exception is not thrown when IGNORE_ERRORS is set.
 	 */
 	public static void delete(final File f, int options) throws IOException {
-		if ((options & SKIP_MISSING) != 0 && !f.exists())
+		FS fs = FS.DETECTED;
+		if ((options & SKIP_MISSING) != 0 && !fs.exists(f))
 			return;
 
-		if ((options & RECURSIVE) != 0 && f.isDirectory()) {
+		if ((options & RECURSIVE) != 0 && fs.isDirectory(f)) {
 			final File[] items = f.listFiles();
 			if (items != null) {
+				List<File> files = new ArrayList<File>();
+				List<File> dirs = new ArrayList<File>();
 				for (File c : items)
-					delete(c, options);
+					if (c.isFile())
+						files.add(c);
+					else
+						dirs.add(c);
+				// Try to delete files first, otherwise options
+				// EMPTY_DIRECTORIES_ONLY|RECURSIVE will delete empty
+				// directories before aborting, depending on order.
+				for (File file : files)
+					delete(file, options);
+				for (File d : dirs)
+					delete(d, options);
 			}
 		}
-		if (!f.delete()) {
-			if ((options & RETRY) != 0 && f.exists()) {
+
+		boolean delete = false;
+		if ((options & EMPTY_DIRECTORIES_ONLY) != 0) {
+			if (f.isDirectory()) {
+				delete = true;
+			} else {
+				if ((options & IGNORE_ERRORS) == 0)
+					throw new IOException(MessageFormat.format(
+							JGitText.get().deleteFileFailed,
+							f.getAbsolutePath()));
+			}
+		} else {
+			delete = true;
+		}
+
+		if (delete && !f.delete()) {
+			if ((options & RETRY) != 0 && fs.exists(f)) {
 				for (int i = 1; i < 10; i++) {
 					try {
 						Thread.sleep(100);
@@ -145,6 +183,56 @@ public class FileUtils {
 	}
 
 	/**
+	 * Rename a file or folder. If the rename fails and if we are running on a
+	 * filesystem where it makes sense to repeat a failing rename then repeat
+	 * the rename operation up to 9 times with 100ms sleep time between two
+	 * calls. Furthermore if the destination exists and is directory hierarchy
+	 * with only directories in it, the whole directory hierarchy will be
+	 * deleted. If the target represents a non-empty directory structure, empty
+	 * subdirectories within that structure may or may not be deleted even if
+	 * the method fails. Furthermore if the destination exists and is a file
+	 * then the file will be deleted and then the rename is retried.
+	 * <p>
+	 * This operation is <em>not</me> atomic.
+	 *
+	 * @see FS#retryFailedLockFileCommit()
+	 * @param src
+	 *            the old {@code File}
+	 * @param dst
+	 *            the new {@code File}
+	 * @throws IOException
+	 *             if the rename has failed
+	 * @since 3.0
+	 */
+	public static void rename(final File src, final File dst)
+			throws IOException {
+		int attempts = FS.DETECTED.retryFailedLockFileCommit() ? 10 : 1;
+		while (--attempts >= 0) {
+			if (src.renameTo(dst))
+				return;
+			try {
+				if (!dst.delete())
+					delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
+				// On *nix there is no try, you do or do not
+				if (src.renameTo(dst))
+					return;
+			} catch (IOException e) {
+				// ignore and continue retry
+			}
+			try {
+				Thread.sleep(100);
+			} catch (InterruptedException e) {
+				throw new IOException(MessageFormat.format(
+						JGitText.get().renameFileFailed, src.getAbsolutePath(),
+						dst.getAbsolutePath()));
+			}
+		}
+		throw new IOException(MessageFormat.format(
+				JGitText.get().renameFileFailed, src.getAbsolutePath(),
+				dst.getAbsolutePath()));
+	}
+
+	/**
 	 * Creates the directory named by this abstract pathname.
 	 *
 	 * @param d
@@ -250,4 +338,28 @@ public class FileUtils {
 			throw new IOException(MessageFormat.format(
 					JGitText.get().createNewFileFailed, f));
 	}
+
+	/**
+	 * Create a symbolic link
+	 *
+	 * @param path
+	 * @param target
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public static void createSymLink(File path, String target)
+			throws IOException {
+		FS.DETECTED.createSymLink(path, target);
+	}
+
+	/**
+	 * @param path
+	 * @return the target of the symbolic link, or null if it is not a symbolic
+	 *         link
+	 * @throws IOException
+	 * @since 3.0
+	 */
+	public static String readSymLink(File path) throws IOException {
+		return FS.DETECTED.readSymLink(path);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
index 07f7990..e110c11 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
@@ -103,6 +103,40 @@ public final class NB {
 	}
 
 	/**
+	 * Convert sequence of 8 bytes (network byte order) into signed value.
+	 *
+	 * @param intbuf
+	 *            buffer to acquire the 8 bytes of data from.
+	 * @param offset
+	 *            position within the buffer to begin reading from. This
+	 *            position and the next 7 bytes after it (for a total of 8
+	 *            bytes) will be read.
+	 * @return signed integer value that matches the 64 bits read.
+	 * @since 3.0
+	 */
+	public static long decodeInt64(final byte[] intbuf, final int offset) {
+		long r = intbuf[offset] << 8;
+
+		r |= intbuf[offset + 1] & 0xff;
+		r <<= 8;
+
+		r |= intbuf[offset + 2] & 0xff;
+		r <<= 8;
+
+		r |= intbuf[offset + 3] & 0xff;
+		r <<= 8;
+
+		r |= intbuf[offset + 4] & 0xff;
+		r <<= 8;
+
+		r |= intbuf[offset + 5] & 0xff;
+		r <<= 8;
+
+		r |= intbuf[offset + 6] & 0xff;
+		return (r << 8) | (intbuf[offset + 7] & 0xff);
+	}
+
+	/**
 	 * Convert sequence of 4 bytes (network byte order) into unsigned value.
 	 *
 	 * @param intbuf
@@ -186,7 +220,7 @@ public final class NB {
 	 * Write a 64 bit integer as a sequence of 8 bytes (network byte order).
 	 *
 	 * @param intbuf
-	 *            buffer to write the 48bytes of data into.
+	 *            buffer to write the 8 bytes of data into.
 	 * @param offset
 	 *            position within the buffer to begin writing to. This position
 	 *            and the next 7 bytes after it (for a total of 8 bytes) will be
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java
index 2f7f486..bc101bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java
@@ -95,21 +95,21 @@ public class RawSubStringPattern {
 		int matchPos = rcs.startPtr;
 		final int maxPos = rcs.endPtr - needleLen;
 
-		OUTER: for (; matchPos < maxPos; matchPos++) {
+		OUTER: for (; matchPos <= maxPos; matchPos++) {
 			if (neq(first, text[matchPos])) {
-				while (++matchPos < maxPos && neq(first, text[matchPos])) {
+				while (++matchPos <= maxPos && neq(first, text[matchPos])) {
 					/* skip */
 				}
-				if (matchPos == maxPos)
+				if (matchPos > maxPos)
 					return -1;
 			}
 
-			int si = ++matchPos;
+			int si = matchPos + 1;
 			for (int j = 1; j < needleLen; j++, si++) {
 				if (neq(needle[j], text[si]))
 					continue OUTER;
 			}
-			return matchPos - 1;
+			return matchPos;
 		}
 		return -1;
 	}
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 f0db580..cfd6fea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -56,8 +56,8 @@ import java.text.SimpleDateFormat;
 import java.util.Locale;
 import java.util.TimeZone;
 
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.lib.Config;
 
 /**
  * Interface to read values from the system.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
index 2a353a5..98c5477 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012, Robin Rosenberg
- * Copyright (C) 2010, Marc Strapetz <marc.strapetz at syntevo.com>
+ * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz at syntevo.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -58,9 +58,12 @@ import org.eclipse.jgit.diff.RawText;
  * of binary files, canonicalization is turned off (for the complete file).
  */
 public class AutoCRLFInputStream extends InputStream {
+
+	static final int BUFFER_SIZE = 8096;
+
 	private final byte[] single = new byte[1];
 
-	private final byte[] buf = new byte[8096];
+	private final byte[] buf = new byte[BUFFER_SIZE];
 
 	private final InputStream in;
 
@@ -95,40 +98,40 @@ public class AutoCRLFInputStream extends InputStream {
 	}
 
 	@Override
-	public int read(byte[] bs, int off, int len) throws IOException {
+	public int read(byte[] bs, final int off, final int len) throws IOException {
 		if (len == 0)
 			return 0;
 
 		if (cnt == -1)
 			return -1;
 
-		final int startOff = off;
+		int i = off;
 		final int end = off + len;
 
-		while (off < end) {
+		while (i < end) {
 			if (ptr == cnt && !fillBuffer())
 				break;
 
 			byte b = buf[ptr++];
 			if (isBinary || b != '\n') {
 				// Logic for binary files ends here
-				bs[off++] = last = b;
+				bs[i++] = last = b;
 				continue;
 			}
 
 			if (b == '\n') {
 				if (last == '\r') {
-					bs[off++] = last = b;
+					bs[i++] = last = b;
 					continue;
 				}
-				bs[off++] = last = '\r';
+				bs[i++] = last = '\r';
 				ptr--;
 			} else
-				bs[off++] = last = b;
+				bs[i++] = last = b;
 		}
-		int n = startOff == off ? -1 : off - startOff;
+		int n = i == off ? -1 : i - off;
 		if (n > 0)
-			last = bs[off - 1];
+			last = bs[i - 1];
 		return n;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
index fe073d8..f05da1c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, Robin Rosenberg
+ * Copyright (C) 2011, 2013 Robin Rosenberg
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -55,11 +55,15 @@ import org.eclipse.jgit.diff.RawText;
  */
 public class AutoCRLFOutputStream extends OutputStream {
 
+	static final int BUFFER_SIZE = 8000;
+
 	private final OutputStream out;
 
 	private int buf = -1;
 
-	private byte[] binbuf = new byte[8000];
+	private byte[] binbuf = new byte[BUFFER_SIZE];
+
+	private byte[] onebytebuf = new byte[1];
 
 	private int binbufcnt = 0;
 
@@ -74,29 +78,8 @@ public class AutoCRLFOutputStream extends OutputStream {
 
 	@Override
 	public void write(int b) throws IOException {
-		int overflow = buffer((byte) b);
-		if (overflow >= 0)
-			return;
-		if (isBinary) {
-			out.write(b);
-			return;
-		}
-		if (b == '\n') {
-			if (buf == '\r') {
-				out.write('\n');
-				buf = -1;
-			} else if (buf == -1) {
-				out.write('\r');
-				out.write('\n');
-				buf = -1;
-			}
-		} else if (b == '\r') {
-			out.write(b);
-			buf = '\r';
-		} else {
-			out.write(b);
-			buf = -1;
-		}
+		onebytebuf[0] = (byte) b;
+		write(onebytebuf, 0, 1);
 	}
 
 	@Override
@@ -107,12 +90,13 @@ public class AutoCRLFOutputStream extends OutputStream {
 	}
 
 	@Override
-	public void write(byte[] b, int off, int len) throws IOException {
-		int overflow = buffer(b, off, len);
+	public void write(byte[] b, final int startOff, final int startLen)
+			throws IOException {
+		final int overflow = buffer(b, startOff, startLen);
 		if (overflow < 0)
 			return;
-		off = off + len - overflow;
-		len = overflow;
+		final int off = startOff + startLen - overflow;
+		final int len = overflow;
 		if (len == 0)
 			return;
 		int lastw = off;
@@ -121,7 +105,7 @@ public class AutoCRLFOutputStream extends OutputStream {
 			return;
 		}
 		for (int i = off; i < off + len; ++i) {
-			byte c = b[i];
+			final byte c = b[i];
 			if (c == '\r') {
 				buf = '\r';
 			} else if (c == '\n') {
@@ -144,15 +128,6 @@ public class AutoCRLFOutputStream extends OutputStream {
 			buf = '\r';
 	}
 
-	private int buffer(byte b) throws IOException {
-		if (binbufcnt > binbuf.length)
-			return 1;
-		binbuf[binbufcnt++] = b;
-		if (binbufcnt == binbuf.length)
-			decideMode();
-		return 0;
-	}
-
 	private int buffer(byte[] b, int off, int len) throws IOException {
 		if (binbufcnt > binbuf.length)
 			return len;
@@ -174,7 +149,7 @@ public class AutoCRLFOutputStream extends OutputStream {
 
 	@Override
 	public void flush() throws IOException {
-		if (binbufcnt < binbuf.length)
+		if (binbufcnt <= binbuf.length)
 			decideMode();
 		buf = -1;
 		out.flush();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
index 5921836..f87ab68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Marc Strapetz <marc.strapetz at syntevo.com>
+ * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz at syntevo.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -91,17 +91,17 @@ public class EolCanonicalizingInputStream extends InputStream {
 	}
 
 	@Override
-	public int read(byte[] bs, int off, int len) throws IOException {
+	public int read(byte[] bs, final int off, final int len) throws IOException {
 		if (len == 0)
 			return 0;
 
 		if (cnt == -1)
 			return -1;
 
-		final int startOff = off;
+		int i = off;
 		final int end = off + len;
 
-		while (off < end) {
+		while (i < end) {
 			if (ptr == cnt && !fillBuffer()) {
 				break;
 			}
@@ -109,23 +109,23 @@ public class EolCanonicalizingInputStream extends InputStream {
 			byte b = buf[ptr++];
 			if (isBinary || b != '\r') {
 				// Logic for binary files ends here
-				bs[off++] = b;
+				bs[i++] = b;
 				continue;
 			}
 
 			if (ptr == cnt && !fillBuffer()) {
-				bs[off++] = '\r';
+				bs[i++] = '\r';
 				break;
 			}
 
 			if (buf[ptr] == '\n') {
-				bs[off++] = '\n';
+				bs[i++] = '\n';
 				ptr++;
 			} else
-				bs[off++] = '\r';
+				bs[i++] = '\r';
 		}
 
-		return startOff == off ? -1 : off - startOff;
+		return i == off ? -1 : i - off;
 	}
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java
index 16ed9c6..6adadbb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2010, 2013 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -89,11 +89,12 @@ public class TeeInputStream extends InputStream {
 	}
 
 	@Override
-	public long skip(long cnt) throws IOException {
+	public long skip(final long count) throws IOException {
 		long skipped = 0;
-		byte[] b = skipBuffer();
+		long cnt = count;
+		final byte[] b = skipBuffer();
 		while (0 < cnt) {
-			int n = src.read(b, 0, (int) Math.min(b.length, cnt));
+			final int n = src.read(b, 0, (int) Math.min(b.length, cnt));
 			if (n <= 0)
 				break;
 			dst.write(b, 0, n);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java
index cda2b59..fe452c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009, 2013 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -137,7 +137,8 @@ public class TimeoutInputStream extends FilterInputStream {
 		myTimer.end();
 	}
 
-	private static InterruptedIOException readTimedOut() {
-		return new InterruptedIOException(JGitText.get().readTimedOut);
+	private InterruptedIOException readTimedOut() {
+		return new InterruptedIOException(MessageFormat.format(
+				JGitText.get().readTimedOut, Integer.valueOf(timeout)));
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutOutputStream.java
index b074396..7ca11ca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TimeoutOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009, 2013 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -150,7 +150,8 @@ public class TimeoutOutputStream extends OutputStream {
 		myTimer.end();
 	}
 
-	private static InterruptedIOException writeTimedOut() {
-		return new InterruptedIOException(JGitText.get().writeTimedOut);
+	private InterruptedIOException writeTimedOut() {
+		return new InterruptedIOException(MessageFormat.format(
+				JGitText.get().writeTimedOut, Integer.valueOf(timeout)));
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
index 20dcd46..0319afd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009, 2013 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -158,17 +158,18 @@ public class UnionInputStream extends InputStream {
 	}
 
 	@Override
-	public long skip(long len) throws IOException {
-		long cnt = 0;
-		while (0 < len) {
+	public long skip(final long count) throws IOException {
+		long skipped = 0;
+		long cnt = count;
+		while (0 < cnt) {
 			final InputStream in = head();
-			final long n = in.skip(len);
+			final long n = in.skip(cnt);
 			if (0 < n) {
-				cnt += n;
-				len -= n;
+				skipped += n;
+				cnt -= n;
 
 			} else if (in == EOF) {
-				return cnt;
+				return skipped;
 
 			} else {
 				// Is this stream at EOF? We can't tell from skip alone.
@@ -178,15 +179,15 @@ public class UnionInputStream extends InputStream {
 				final int r = in.read();
 				if (r < 0) {
 					pop();
-					if (0 < cnt)
+					if (0 < skipped)
 						break;
 				} else {
-					cnt += 1;
-					len -= 1;
+					skipped += 1;
+					cnt -= 1;
 				}
 			}
 		}
-		return cnt;
+		return skipped;
 	}
 
 	@Override
diff --git a/pom.xml b/pom.xml
index cbf6aa1..4c27438 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,7 @@
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>org.eclipse.jgit-parent</artifactId>
   <packaging>pom</packaging>
-  <version>2.3.1.201302201838-r</version>
+  <version>3.0.0.201306101825-r</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -175,8 +175,9 @@
     <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>2.2.0.201212191850-r</jgit-last-release-version>
+    <jgit-last-release-version>2.3.1.201302201838-r</jgit-last-release-version>
     <jsch-version>0.1.46</jsch-version>
+    <javaewah-version>0.5.6</javaewah-version>
     <junit-version>4.5</junit-version>
     <!-- TODO: update Maven dependency for args4j to 2.0.21 as soon as available on Maven Central -->
     <args4j-version>2.0.12</args4j-version>
@@ -406,6 +407,12 @@
       </dependency>
 
       <dependency>
+        <groupId>com.googlecode.javaewah</groupId>
+        <artifactId>JavaEWAH</artifactId>
+        <version>${javaewah-version}</version>
+      </dependency>
+
+      <dependency>
         <groupId>args4j</groupId>
         <artifactId>args4j</artifactId>
         <version>${args4j-version}</version>
@@ -438,25 +445,38 @@
   </dependencyManagement>
 
   <distributionManagement>
+    <repository>
+      <id>repo.eclipse.org</id>
+      <name>JGit Maven Repository - Releases</name>
+      <url>https://repo.eclipse.org/content/repositories/jgit-releases/</url>
+    </repository>
     <snapshotRepository>
-      <id>jgit-maven-snapshot</id>
-      <name>JGit Maven Repository</name>
-      <url>dav:https://egit.googlecode.com/svn/maven/</url>
+      <id>repo.eclipse.org</id>
+      <name>JGit Maven Repository - Snapshots</name>
+      <url>https://repo.eclipse.org/content/repositories/jgit-snapshots/</url>
       <uniqueVersion>true</uniqueVersion>
     </snapshotRepository>
   </distributionManagement>
 
   <profiles>
-    <!-- Set -Djgit.java6.skip=true to compile with only Java 5 -->
     <profile>
       <id>jgit.java6</id>
       <activation>
-        <property>
-          <name>!jgit.java6.skip</name>
-        </property>
+        <jdk>1.6</jdk>
+      </activation>
+      <modules>
+        <module>org.eclipse.jgit.console</module>
+      </modules>
+    </profile>
+    <profile>
+      <id>jgit.java7</id>
+      <activation>
+        <jdk>1.7</jdk>
       </activation>
       <modules>
         <module>org.eclipse.jgit.console</module>
+        <module>org.eclipse.jgit.java7</module>
+        <module>org.eclipse.jgit.java7.test</module>
       </modules>
     </profile>
     <profile>
diff --git a/tools/version.sh b/tools/version.sh
index eaff9a6..52a02c5 100755
--- a/tools/version.sh
+++ b/tools/version.sh
@@ -133,7 +133,14 @@ perl -pi~ -e '
 	' org.eclipse.jgit.packaging/org.*.feature/feature.xml
 
 perl -pi~ -e '
-	s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
+	if ($ARGV ne $old_argv) {
+		$seen_version = 0;
+		$old_argv = $ARGV;
+	}
+	if (!$seen_version) {
+		$seen_version = 1 if
+		s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
+	}
 	' org.eclipse.jgit.packaging/org.*.feature/pom.xml
 
 perl -pi~ -e '
@@ -152,8 +159,8 @@ perl -pi~ -e '
 		$seen_version = 0;
 		$old_argv = $ARGV;
 	}
-	if ($seen_version < 2) {
-		$seen_version++ if
+	if (!$seen_version) {
+		$seen_version = 1 if
 		s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
 	}
 	' org.eclipse.jgit.packaging/pom.xml

-- 
UNNAMED PROJECT



More information about the pkg-java-commits mailing list