Merge branch 'stable-4.7' into stable-4.8

* stable-4.7:
  Prepare 4.7.9-SNAPSHOT builds
  JGit v4.7.8.201903121755-r
  Prepare 4.5.7-SNAPSHOT builds
  JGit v4.5.6.201903121547-r
  Check for packfile validity and fd before reading
  Move throw of PackInvalidException outside the catch
  Use FileSnapshot to get lastModified on PackFile
  Include size when comparing FileSnapshot
  Do not reuse packfiles when changed on filesystem
  Silence API warnings for new API introduced for fixes

Change-Id: I1136eaa1590b225ceaf16ba7552af0374253a74e
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/WORKSPACE b/WORKSPACE
index c5eae1d..f2fecb5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -105,46 +105,46 @@
     sha1 = "a60a5e993c98c864010053cb901b7eab25306568",
 )
 
-JETTY_VER = "9.3.17.v20170317"
+JETTY_VER = "9.4.5.v20170502"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
-    sha1 = "ed6986b0d0ca7b9b0f9015c9efb80442e3043a8e",
-    src_sha1 = "ee6b4784a00a92e5c1b6111033b7ae41ac6052a3",
+    sha1 = "394a535b76ca7399b25be3266f06f614e020517e",
+    src_sha1 = "4e85803c8d539aa0a8389e113095ef86032ac425",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
-    sha1 = "ca52535569445682d42aaa97c7039442719a0507",
-    src_sha1 = "2ff9f4fb18b320fd5a0272a427bacc4d5fe7bc86",
+    sha1 = "4f4fc4cbe3504b6c91143ee37b38a1f3de2dcc72",
+    src_sha1 = "2124a757c87eacea7ad6507be6a415b5b51139b5",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
-    sha1 = "194e9a02e6ba249ef4a3f4bd56b4993087992299",
-    src_sha1 = "0c9bd572f530c411592aefb71781ecca0b3719a9",
+    sha1 = "b4d30340213c3d2a5f908860ba170c5a697829be",
+    src_sha1 = "295d873f609a0e2863f33b5dbc8906ca348f1107",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
-    sha1 = "6c02d728e15d4868486254039c867a1ac3e4a52e",
-    src_sha1 = "3c0a2a82792f268631b4fefd77be9f126ec974b1",
+    sha1 = "c51b8a6a67d64672889249dd958edd77bff8fc0c",
+    src_sha1 = "c1bee39aeb565a4f26852b1851192d98ab611dbc",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
-    sha1 = "756a8cd2a1cbfb84a94973b6332dd3eccd47c0cd",
-    src_sha1 = "a9afa99cccb19b441364fa805d027f457cbbb136",
+    sha1 = "76086f955d4e943396b8f340fd5bae3ce4da19d9",
+    src_sha1 = "8d41e410b2f0dd284a6e199ed08f45ef7ab2acf1",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
-    sha1 = "b8512ab02819de01f0f5a5c6026163041f579beb",
-    src_sha1 = "96f8e3dcdc3660a5c91f19c46695daa70ac95625",
+    sha1 = "5fd36dfcf39110b809bd9b20cec62706ab694711",
+    src_sha1 = "629fcda1e4eecfd795e24cc07715ab9797970980",
 )
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 83c8a8d..5889109 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.junit;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
+ org.eclipse.jgit.ant.tasks;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.junit;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.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.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index 4c7ccc8..386ecd2 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 8c14401..be009d1 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -2,11 +2,11 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[4.7.9,4.8.0)"
+  org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="4.7.9";
+Export-Package: org.eclipse.jgit.ant.tasks;version="4.8.1";
  uses:="org.apache.tools.ant.types,org.apache.tools.ant"
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index ecb9c2c..5712d29 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>4.7.9-SNAPSHOT</version>
+		<version>4.8.1-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/BUILD b/org.eclipse.jgit.archive/BUILD
index dfdbfdc..8c65fc0 100644
--- a/org.eclipse.jgit.archive/BUILD
+++ b/org.eclipse.jgit.archive/BUILD
@@ -3,7 +3,7 @@
 java_library(
     name = "jgit-archive",
     srcs = glob(
-        ["src/**"],
+        ["src/**/*.java"],
         exclude = ["src/org/eclipse/jgit/archive/FormatActivator.java"],
     ),
     resource_strip_prefix = "org.eclipse.jgit.archive/resources",
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index cfa5195..4080946 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -12,15 +12,15 @@
  org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
  org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
  org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
+ org.eclipse.jgit.api;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
  org.osgi.framework;version="[1.3.0,2.0.0)"
 Bundle-ActivationPolicy: lazy
 Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="4.7.9";
+Export-Package: org.eclipse.jgit.archive;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.api,
    org.apache.commons.compress.archivers,
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index 5a2aaf3..c03191a 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.archive - Sources
 Bundle-SymbolicName: org.eclipse.jgit.archive.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 4.7.9.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.7.9.qualifier";roots="."
+Bundle-Version: 4.8.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.8.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index e3263ed..eea1d80 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.http.apache/BUILD b/org.eclipse.jgit.http.apache/BUILD
index c1538ab..5ea118c 100644
--- a/org.eclipse.jgit.http.apache/BUILD
+++ b/org.eclipse.jgit.http.apache/BUILD
@@ -2,7 +2,7 @@
 
 java_library(
     name = "http-apache",
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.http.apache/resources",
     resources = glob(["resources/**"]),
     deps = [
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index c780687..2fc4543 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
@@ -22,10 +22,10 @@
  org.apache.http.impl.client;version="[4.3.0,5.0.0)",
  org.apache.http.impl.conn;version="[4.3.0,5.0.0)",
  org.apache.http.params;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="4.7.9";
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.8.1";
   uses:="org.apache.http.client,
    org.eclipse.jgit.transport.http,
    org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 70a5795..068e367 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -48,7 +48,7 @@
 	<parent>
 		<groupId>org.eclipse.jgit</groupId>
 		<artifactId>org.eclipse.jgit-parent</artifactId>
-		<version>4.7.9-SNAPSHOT</version>
+		<version>4.8.1-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/BUILD b/org.eclipse.jgit.http.server/BUILD
index 876c5fa..9d5673b 100644
--- a/org.eclipse.jgit.http.server/BUILD
+++ b/org.eclipse.jgit.http.server/BUILD
@@ -2,7 +2,7 @@
 
 java_library(
     name = "jgit-servlet",
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.http.server/resources",
     resources = glob(["resources/**"]),
     deps = [
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 1141cbf..2b51b4b 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -2,13 +2,13 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="4.7.9",
- org.eclipse.jgit.http.server.glue;version="4.7.9";
+Export-Package: org.eclipse.jgit.http.server;version="4.8.1",
+ org.eclipse.jgit.http.server.glue;version="4.8.1";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="4.7.9";
+ org.eclipse.jgit.http.server.resolver;version="4.8.1";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -17,12 +17,12 @@
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
  javax.servlet.http;version="[2.5.0,3.2.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.resolver;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)"
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index e3b0011..a011594 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 094c3b9..811585e 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -2,44 +2,44 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
  javax.servlet.http;version="[2.5.0,3.2.0)",
- org.eclipse.jetty.continuation;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.http;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.io;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security.authentication;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.handler;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.nio;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.servlet;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.component;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.log;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.thread;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.http.server;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.http.server.glue;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.http.server.resolver;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.junit;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.junit.http;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.resolver;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
+ org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.http;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.io;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.http.server;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.junit;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.junit.http;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 24e0206..1538601 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/BUILD b/org.eclipse.jgit.junit.http/BUILD
index be6e1ae..2a29acc 100644
--- a/org.eclipse.jgit.junit.http/BUILD
+++ b/org.eclipse.jgit.junit.http/BUILD
@@ -3,7 +3,7 @@
 java_library(
     name = "junit-http",
     testonly = 1,
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resources = glob(["resources/**"]),
     # TODO(davido): we want here provided deps
     deps = [
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index c306a06..00a7a65 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -10,26 +10,26 @@
 Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
  javax.servlet.http;version="[2.5.0,3.2.0)",
  org.apache.commons.logging;version="[1.1.1,2.0.0)",
- org.eclipse.jetty.http;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security.authentication;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.handler;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.nio;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.servlet;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.component;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.log;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.http.server;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.junit;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.resolver;version="[4.7.9,4.8.0)",
+ org.eclipse.jetty.http;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.http.server;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.junit;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.8.1,4.9.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="4.7.9";
+Export-Package: org.eclipse.jgit.junit.http;version="4.8.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.junit,
    javax.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 9b4995f..0c1acf9 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
index 6b7853d..0154a7f 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
@@ -102,7 +102,7 @@
 
 	@SuppressWarnings("unchecked")
 	private static Map<String, String[]> clone(Map parameterMap) {
-		return new TreeMap<String, String[]>(parameterMap);
+		return new TreeMap<>(parameterMap);
 	}
 
 	/** @return {@code "GET"} or {@code "POST"} */
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
index a663484..28c0f21 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
@@ -46,25 +46,25 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
+import org.eclipse.jetty.security.AbstractLoginService;
 import org.eclipse.jetty.security.Authenticator;
 import org.eclipse.jetty.security.ConstraintMapping;
 import org.eclipse.jetty.security.ConstraintSecurityHandler;
-import org.eclipse.jetty.security.MappedLoginService;
 import org.eclipse.jetty.security.authentication.BasicAuthenticator;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.util.security.Constraint;
@@ -168,36 +168,41 @@
 		return ctx;
 	}
 
-	static class TestMappedLoginService extends MappedLoginService {
+	static class TestMappedLoginService extends AbstractLoginService {
 		private String role;
 
+		protected final ConcurrentMap<String, UserPrincipal> users = new ConcurrentHashMap<>();
+
 		TestMappedLoginService(String role) {
 			this.role = role;
 		}
 
 		@Override
-		protected UserIdentity loadUser(String who) {
-			return null;
+		protected void doStart() throws Exception {
+			UserPrincipal p = new UserPrincipal(username,
+					new Password(password));
+			users.put(username, p);
+			super.doStart();
 		}
 
 		@Override
-		protected void loadUsers() throws IOException {
-			putUser(username, new Password(password), new String[] { role });
+		protected String[] loadRoleInfo(UserPrincipal user) {
+			if (users.get(user.getName()) == null)
+				return null;
+			else
+				return new String[] { role };
 		}
 
-		protected String[] loadRoleInfo(KnownUser user) {
-			return null;
-		}
-
-		protected KnownUser loadUserInfo(String usrname) {
-			return null;
+		@Override
+		protected UserPrincipal loadUserInfo(String user) {
+			return users.get(user);
 		}
 	}
 
 	private void auth(ServletContextHandler ctx, Authenticator authType) {
 		final String role = "can-access";
 
-		MappedLoginService users = new TestMappedLoginService(role);
+		AbstractLoginService users = new TestMappedLoginService(role);
 		ConstraintMapping cm = new ConstraintMapping();
 		cm.setConstraint(new Constraint());
 		cm.getConstraint().setAuthenticate(true);
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
index 415398d..4e35ff6 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
@@ -161,6 +161,12 @@
 	@Override
 	public void warn(String msg, Object... args) {
 		synchronized (warnings) {
+			int i = 0;
+			int index = msg.indexOf("{}");
+			while (index >= 0) {
+				msg = msg.replaceFirst("\\{\\}", "{" + i++ + "}");
+				index = msg.indexOf("{}");
+			}
 			warnings.add(new Warning(MessageFormat.format(msg, args)));
 		}
 	}
diff --git a/org.eclipse.jgit.junit/BUILD b/org.eclipse.jgit.junit/BUILD
index 350b25f..74498fd 100644
--- a/org.eclipse.jgit.junit/BUILD
+++ b/org.eclipse.jgit.junit/BUILD
@@ -3,7 +3,7 @@
 java_library(
     name = "junit",
     testonly = 1,
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.junit/resources",
     resources = glob(["resources/**"]),
     deps = [
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 6d9d7e1..c864c5c 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -2,31 +2,31 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.api.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.dircache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.merge;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util.io;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util.time;version="[4.7.9,4.8.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.api.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.dircache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.merge;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util.io;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util.time;version="[4.8.1,4.9.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.rules;version="[4.9.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
  org.junit.runners.model;version="[4.5.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="4.7.9";
+Export-Package: org.eclipse.jgit.junit;version="4.8.1";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -35,4 +35,4 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.storage.file,
    org.eclipse.jgit.api",
- org.eclipse.jgit.junit.time;version="4.7.9"
+ org.eclipse.jgit.junit.time;version="4.8.1"
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index fc7df0c..3f843e6 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index 825dc65..f7960c7 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -13,25 +13,25 @@
  org.apache.http.client.methods;version="[4.3.0,5.0.0)",
  org.apache.http.entity;version="[4.3.0,5.0.0)",
  org.apache.http.impl.client;version="[4.3.0,5.0.0)",
- org.eclipse.jetty.continuation;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.http;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.io;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security.authentication;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.handler;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.nio;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.servlet;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.component;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.log;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.thread;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.junit.http;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.test;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
+ org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.http;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.io;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
+ org.eclipse.jgit.junit.http;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.test;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index 61b6f53..9fd7080 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/BUILD b/org.eclipse.jgit.lfs.server/BUILD
index fa14e8a..f3b9005 100644
--- a/org.eclipse.jgit.lfs.server/BUILD
+++ b/org.eclipse.jgit.lfs.server/BUILD
@@ -2,7 +2,7 @@
 
 java_library(
     name = "jgit-lfs-server",
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.lfs.server/resources",
     resources = glob(["resources/**"]),
     deps = [
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index 450dda2..3366e1f 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -2,19 +2,19 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.server;version="4.7.9";
+Export-Package: org.eclipse.jgit.lfs.server;version="4.8.1";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="4.7.9";
+ org.eclipse.jgit.lfs.server.fs;version="4.8.1";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="4.7.9";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="4.7.9";
+ org.eclipse.jgit.lfs.server.internal;version="4.8.1";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="4.8.1";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -24,14 +24,14 @@
  javax.servlet.http;version="[3.1.0,4.0.0)",
  org.apache.http;version="[4.3.0,5.0.0)",
  org.apache.http.client;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
+ org.eclipse.jgit.annotations;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index ca63b0a..f247cae 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
@@ -140,25 +140,6 @@
           </archive>
         </configuration>
       </plugin>
-
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>clirr-maven-plugin</artifactId>
-      </plugin>
     </plugins>
   </build>
-
-  <reporting>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>clirr-maven-plugin</artifactId>
-        <version>${clirr-version}</version>
-        <configuration>
-          <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
-          <minSeverity>info</minSeverity>
-        </configuration>
-      </plugin>
-    </plugins>
-  </reporting>
 </project>
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index ce10854..2bdbb59 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -2,23 +2,23 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.junit;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.junit;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
  org.junit.runners;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="4.7.9";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="4.8.1";x-friends:="org.eclipse.jgit.lfs.server.test"
 
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index ea34db4..f599ca0 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs/BUILD b/org.eclipse.jgit.lfs/BUILD
index c4c9f8a..0c7b1b2 100644
--- a/org.eclipse.jgit.lfs/BUILD
+++ b/org.eclipse.jgit.lfs/BUILD
@@ -2,7 +2,7 @@
 
 java_library(
     name = "jgit-lfs",
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.lfs/resources",
     resources = glob(["resources/**"]),
     deps = [
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index a68495b..489fd12 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -2,20 +2,20 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs;version="4.7.9",
- org.eclipse.jgit.lfs.errors;version="4.7.9",
- org.eclipse.jgit.lfs.internal;version="4.7.9";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
- org.eclipse.jgit.lfs.lib;version="4.7.9"
+Export-Package: org.eclipse.jgit.lfs;version="4.8.1",
+ org.eclipse.jgit.lfs.errors;version="4.8.1",
+ org.eclipse.jgit.lfs.internal;version="4.8.1";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="4.8.1"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[4.7.9,4.8.0)";resolution:=optional,
- org.eclipse.jgit.attributes;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)"
+Import-Package: org.eclipse.jgit.annotations;version="[4.8.1,4.9.0)";resolution:=optional,
+ org.eclipse.jgit.attributes;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)"
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 71f7185..98216fb 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
@@ -116,25 +116,6 @@
           </archive>
         </configuration>
       </plugin>
-
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>clirr-maven-plugin</artifactId>
-      </plugin>
     </plugins>
   </build>
-
-  <reporting>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>clirr-maven-plugin</artifactId>
-        <version>${clirr-version}</version>
-        <configuration>
-          <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
-          <minSeverity>info</minSeverity>
-        </configuration>
-      </plugin>
-    </plugins>
-  </reporting>
 </project>
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 8d05bef..0d9c4b2 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="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
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 91708f2..c6ae404 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index 9007365..c2662fc 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.http.apache"
       label="%featureName"
-      version="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index 0e3b04b..4fa4fca 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
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 fcb38a9..408b1d8 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
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.junit"
       label="%featureName"
-      version="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
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 5a1ac9a..bdaa809 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index 0604b83..7113e7b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.lfs"
       label="%featureName"
-      version="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 7aa31fd..b39af8c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
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 c82aa4c..1129615 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="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -31,8 +31,8 @@
          version="0.0.0"/>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="4.7.8" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="4.7.8" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="4.8.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="4.8.1" match="equivalent"/>
    </requires>
 
    <plugin
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 9469b6d..b8cb2c8 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
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 54c0e8d..0556fb5 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="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       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 6359c6d..70813bc 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
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 c6164b4..432c5d4 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
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 d00a825..5834a0c 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="4.7.9.qualifier"
+      version="4.8.1.qualifier"
       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 9ad6842..ec57876 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
index bf411b2..a43734b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
@@ -2,4 +2,4 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: JGit Target Platform Bundle
 Bundle-SymbolicName: org.eclipse.jgit.target
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
index be02826..8051080 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.5" sequenceNumber="1502747250">
+<target name="jgit-4.5" sequenceNumber="1502749391">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.http" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.io" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.security" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.server" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.util" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.3.17.v20170317"/>
-      <repository id="jetty-9.3.17" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.3.17.v20170317/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.5.v20170502"/>
+      <repository id="jetty-9.4.5" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.5.v20170502/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.apache.ant" version="1.9.4.v201504302020"/>
@@ -98,7 +98,7 @@
       <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
       <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
-      <repository location="http://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20170516192513/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
index efc1f44..f9653b2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
@@ -1,6 +1,6 @@
 target "jgit-4.5" with source configurePhase
 
-include "projects/jetty-9.3.17.tpd"
+include "projects/jetty-9.4.5.tpd"
 include "orbit/R20160221192158-Mars.tpd"
 include "orbit/R20170516192513-Oxygen.tpd"
 
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index bcef50b..b6bbcda 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.6" sequenceNumber="1502747233">
+<target name="jgit-4.6" sequenceNumber="1502749371">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.http" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.io" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.security" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.server" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.util" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.3.17.v20170317"/>
-      <repository id="jetty-9.3.17" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.3.17.v20170317/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.5.v20170502"/>
+      <repository id="jetty-9.4.5" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.5.v20170502/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
@@ -60,7 +60,7 @@
       <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
       <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
-      <repository location="http://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20170516192513/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
index 90f62ae..9ddba2d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
@@ -1,6 +1,6 @@
 target "jgit-4.6" with source configurePhase
 
-include "projects/jetty-9.3.17.tpd"
+include "projects/jetty-9.4.5.tpd"
 include "orbit/R20170516192513-Oxygen.tpd"
 
 location "http://download.eclipse.org/releases/neon/" {
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index 52ea6f8..6071c8f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.7" sequenceNumber="1502747215">
+<target name="jgit-4.7" sequenceNumber="1502749365">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.http" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.io" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.security" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.server" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.util" version="9.3.17.v20170317"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.3.17.v20170317"/>
-      <repository id="jetty-9.3.17" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.3.17.v20170317/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.5.v20170502"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.5.v20170502"/>
+      <repository id="jetty-9.4.5" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.5.v20170502/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.apache.ant" version="1.9.6.v201510161327"/>
@@ -60,7 +60,7 @@
       <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
       <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
-      <repository location="http://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20170516192513/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
index 1d0e693..4185079 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd
@@ -1,8 +1,8 @@
 target "jgit-4.7" with source configurePhase
 
-include "projects/jetty-9.3.17.tpd"
+include "projects/jetty-9.4.5.tpd"
 include "orbit/R20170516192513-Oxygen.tpd"
 
 location "http://download.eclipse.org/releases/oxygen/" {
 	org.eclipse.osgi lazy
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170516192513-Oxygen.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170516192513-Oxygen.tpd
index 3600628..ef19fa6 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170516192513-Oxygen.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170516192513-Oxygen.tpd
@@ -1,7 +1,7 @@
 target "R20170516192513-Oxygen" with source configurePhase
 // see http://download.eclipse.org/tools/orbit/downloads/
 
-location "http://download.eclipse.org/tools/orbit/R-builds/R20170516192513/repository" {
+location "http://download.eclipse.org/tools/orbit/downloads/drops/R20170516192513/repository" {
 	org.apache.ant [1.9.6.v201510161327,1.9.6.v201510161327]
 	org.apache.ant.source [1.9.6.v201510161327,1.9.6.v201510161327]
 	org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
index 2a53c9a..d1934e7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
@@ -49,7 +49,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.17.tpd
deleted file mode 100644
index 662df09..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.17.tpd
+++ /dev/null
@@ -1,20 +0,0 @@
-target "jetty-9.3.17" with source configurePhase
-
-location jetty-9.3.17 "http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.3.17.v20170317/" {
-	org.eclipse.jetty.client [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.client.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.continuation [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.continuation.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.http [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.http.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.io [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.io.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.security [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.security.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.server [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.server.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.servlet [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.servlet.source [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.util [9.3.17.v20170317,9.3.17.v20170317]
-	org.eclipse.jetty.util.source [9.3.17.v20170317,9.3.17.v20170317]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.9.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.9.tpd
deleted file mode 100644
index d5621a0..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.9.tpd
+++ /dev/null
@@ -1,20 +0,0 @@
-target "jetty-9.4.3" with source configurePhase
-
-location jetty-9.4.3 "http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.3.9.v20160517/" {
-	org.eclipse.jetty.client [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.client.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.continuation [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.continuation.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.http [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.http.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.io [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.io.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.security [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.security.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.server [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.server.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.servlet [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.servlet.source [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.util [9.3.9.v20160517,9.3.9.v20160517]
-	org.eclipse.jetty.util.source [9.3.9.v20160517,9.3.9.v20160517]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.5.tpd
new file mode 100644
index 0000000..363c600
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.5.tpd
@@ -0,0 +1,20 @@
+target "jetty-9.4.5" with source configurePhase
+
+location jetty-9.4.5 "http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.5.v20170502/" {
+	org.eclipse.jetty.client [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.client.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.continuation [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.continuation.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.http [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.http.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.io [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.io.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.security [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.security.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.server [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.server.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.servlet [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.servlet.source [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.util [9.4.5.v20170502,9.4.5.v20170502]
+	org.eclipse.jetty.util.source [9.4.5.v20170502,9.4.5.v20170502]
+}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 1e1fde6..2613686 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,7 +53,7 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>4.7.9-SNAPSHOT</version>
+  <version>4.8.1-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
@@ -216,7 +216,7 @@
         <plugin>
           <groupId>org.eclipse.cbi.maven.plugins</groupId>
           <artifactId>eclipse-jarsigner-plugin</artifactId>
-          <version>1.1.3</version>
+          <version>1.1.4</version>
         </plugin>
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 1f05db7..37bef75 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -2,28 +2,28 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.api.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.diff;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.dircache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="4.7.9",
- org.eclipse.jgit.junit;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.merge;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.pgm;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.pgm.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.pgm.opt;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util.io;version="[4.7.9,4.8.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.api.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.diff;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.dircache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.8.1",
+ org.eclipse.jgit.junit;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.merge;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.pgm;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util.io;version="[4.8.1,4.9.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.11.0,5.0.0)",
  org.junit.rules;version="[4.11.0,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 6927c45..ee234d8 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm/BUILD b/org.eclipse.jgit.pgm/BUILD
index 6d32790..0792268 100644
--- a/org.eclipse.jgit.pgm/BUILD
+++ b/org.eclipse.jgit.pgm/BUILD
@@ -1,6 +1,6 @@
 java_library(
     name = "pgm",
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.pgm/resources",
     resources = glob(["resources/**"]),
     visibility = ["//visibility:public"],
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index a493080..5bde6a3 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-Localization: plugin
@@ -13,60 +13,60 @@
  org.apache.commons.compress.archivers.tar;version="[1.3,2.0)",
  org.apache.commons.compress.archivers.zip;version="[1.3,2.0)",
  org.apache.commons.logging;version="1.1.1",
- org.eclipse.jetty.continuation;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.http;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.io;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.security.authentication;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.handler;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.server.nio;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.servlet;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.component;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.log;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
- org.eclipse.jetty.util.thread;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.api;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.api.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.archive;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.awtui;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.blame;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.diff;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.dircache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.gitrepo;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.ketch;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.server;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs.server.s3;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.merge;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.notes;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revplot;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.pack;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.resolver;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util.io;version="[4.7.9,4.8.0)",
+ org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.http;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.io;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)",
+ org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)",
+ org.eclipse.jgit.api;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.api.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.archive;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.awtui;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.blame;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.diff;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.dircache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.gitrepo;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.ketch;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.server;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.merge;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.notes;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revplot;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.pack;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util.io;version="[4.8.1,4.9.0)",
  org.kohsuke.args4j;version="[2.0.12,2.1.0)",
  org.kohsuke.args4j.spi;version="[2.0.15,2.1.0)"
-Export-Package: org.eclipse.jgit.console;version="4.7.9";
+Export-Package: org.eclipse.jgit.console;version="4.8.1";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="4.7.9";
+ org.eclipse.jgit.pgm;version="4.8.1";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.pgm.opt,
@@ -77,11 +77,11 @@
    org.eclipse.jgit.treewalk,
    javax.swing,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="4.7.9";
+ org.eclipse.jgit.pgm.debug;version="4.8.1";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="4.7.9";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="4.7.9";
+ org.eclipse.jgit.pgm.internal;version="4.8.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.kohsuke.args4j.spi,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index d302593..d5d71ee 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.pgm - Sources
 Bundle-SymbolicName: org.eclipse.jgit.pgm.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 4.7.9.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.7.9.qualifier";roots="."
+Bundle-Version: 4.8.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.8.1.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index ab9ace5..7d6ec1d 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 06e4d94..c3d7c68 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -44,6 +44,7 @@
 cantWrite=Can''t write {0}
 changesNotStagedForCommit=Changes not staged for commit:
 changesToBeCommitted=Changes to be committed:
+checkingOut=Submodule path ''{0}'': checked out ''{1}''
 checkoutConflict=error: Your local changes to the following files would be overwritten by checkout:
 checkoutConflictPathLine=\t{0}
 cleanRequireForce=clean.requireForce defaults to true and neither -n nor -f given; refusing to clean
@@ -66,12 +67,14 @@
 failedToLockTag=Failed to lock tag {0}: {1}
 fatalError=fatal: {0}
 fatalThisProgramWillDestroyTheRepository=fatal: This program will destroy the repository\nfatal:\nfatal:\nfatal:    {0}\nfatal:\nfatal: To continue, add {1} to the command line\nfatal:
+fetchingSubmodule=Fetching submodule {0}
 fileIsRequired=argument file is required
 ffNotPossibleAborting=Not possible to fast-forward, aborting.
 forcedUpdate=forced update
 fromURI=From {0}
 initializedEmptyGitRepositoryIn=Initialized empty Git repository in {0}
 invalidHttpProxyOnlyHttpSupported=Invalid http_proxy: {0}: Only http supported.
+invalidRecurseSubmodulesMode=Invalid recurse submodules mode: {0}
 jgitVersion=jgit version {0}
 lineFormat={0}
 listeningOn=Listening on {0}
@@ -197,6 +200,7 @@
 statusDeletedByUs=deleted by us:
 statusBothAdded=both added:
 statusBothModified=both modified:
+submoduleRegistered=Submodule {0} registered
 switchedToNewBranch=Switched to a new branch ''{0}''
 switchedToBranch=Switched to branch ''{0}''
 tagAlreadyExists=tag ''{0}'' already exists
@@ -283,7 +287,7 @@
 usage_addFileContentsToTheIndex=Add file contents to the index
 usage_alterTheDetailShown=alter the detail shown
 usage_approveDestructionOfRepository=approve destruction of repository
-usage_archive=zip up files from the named tree
+usage_archive=Zip up files from the named tree
 usage_archiveFormat=archive format. Currently supported formats: 'tar', 'zip', 'tgz', 'tbz2', 'txz'
 usage_archiveOutput=output file to write the archive to
 usage_archivePrefix=string to prepend to each pathname in the archive
@@ -357,6 +361,7 @@
 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_noRecurseSubmodules=Disable recursive fetching of submodules (this has the same effect as using the --recurse-submodules=no option)
 usage_noShowStandardNotes=Disable showing notes from the standard /refs/notes/commits branch
 usage_onlyMatchAgainstAlreadyTrackedFiles=Only match <filepattern> against already tracked files in the index rather than the working tree
 usage_outputFile=Output file
@@ -371,6 +376,7 @@
 usage_quiet=don't show progress messages
 usage_recordChangesToRepository=Record changes to the repository
 usage_recurseIntoSubtrees=recurse into subtrees
+usage_recurseSubmodules=recurse into submodules
 usage_removeUntrackedDirectories=remove untracked directories
 usage_renameLimit=limit size of rename matrix
 usage_reset=Reset current HEAD to the specified state
@@ -381,7 +387,7 @@
 usage_runLfsStore=Run LFS Store in a given directory
 usage_S3NoSslVerify=Skip verification of Amazon server certificate and hostname
 usage_setTheGitRepositoryToOperateOn=set the git repository to operate on
-usage_show=display one commit
+usage_show=Display one commit
 usage_showRefNamesMatchingCommits=Show ref names matching commits
 usage_showPatch=display patch
 usage_showNotes=Add this ref to the list of note branches from which notes are displayed
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 2cee2cb..08ec096 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
@@ -90,6 +90,9 @@
 			}
 		}
 		showRemoteMessages(errw, r.getMessages());
+		for (FetchResult submoduleResult : r.submoduleResults().values()) {
+			showFetchResult(submoduleResult);
+		}
 	}
 
 	static void showRemoteMessages(ThrowingPrintWriter writer, String pkt) throws IOException {
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 0407828..ca5205a 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
@@ -44,11 +44,14 @@
 package org.eclipse.jgit.pgm;
 
 import java.io.File;
+import java.io.IOException;
 import java.text.MessageFormat;
+import java.util.Collection;
 
 import org.eclipse.jgit.api.CloneCommand;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.TextProgressMonitor;
 import org.eclipse.jgit.pgm.internal.CLIText;
@@ -58,7 +61,7 @@
 import org.kohsuke.args4j.Option;
 
 @Command(common = true, usage = "usage_cloneRepositoryIntoNewDir")
-class Clone extends AbstractFetchCommand {
+class Clone extends AbstractFetchCommand implements CloneCommand.Callback {
 	@Option(name = "--origin", aliases = { "-o" }, metaVar = "metaVar_remoteName", usage = "usage_useNameInsteadOfOriginToTrackUpstream")
 	private String remoteName = Constants.DEFAULT_REMOTE_NAME;
 
@@ -74,6 +77,9 @@
 	@Option(name = "--quiet", usage = "usage_quiet")
 	private Boolean quiet;
 
+	@Option(name = "--recurse-submodules", usage = "usage_recurseSubmodules")
+	private boolean cloneSubmodules;
+
 	@Argument(index = 0, required = true, metaVar = "metaVar_uriish")
 	private String sourceUri;
 
@@ -109,13 +115,15 @@
 
 		CloneCommand command = Git.cloneRepository();
 		command.setURI(sourceUri).setRemote(remoteName).setBare(isBare)
-				.setNoCheckout(noCheckout).setBranch(branch);
+				.setNoCheckout(noCheckout).setBranch(branch)
+				.setCloneSubmodules(cloneSubmodules);
 
 		command.setGitDir(gitdir == null ? null : new File(gitdir));
 		command.setDirectory(localNameF);
 		boolean msgs = quiet == null || !quiet.booleanValue();
 		if (msgs) {
-			command.setProgressMonitor(new TextProgressMonitor(errw));
+			command.setProgressMonitor(new TextProgressMonitor(errw))
+					.setCallback(this);
 			outw.println(MessageFormat.format(
 					CLIText.get().cloningInto, localName));
 			outw.flush();
@@ -136,4 +144,39 @@
 			outw.flush();
 		}
 	}
+
+	@Override
+	public void initializedSubmodules(Collection<String> submodules) {
+		try {
+			for (String submodule : submodules) {
+				outw.println(MessageFormat
+						.format(CLIText.get().submoduleRegistered, submodule));
+			}
+			outw.flush();
+		} catch (IOException e) {
+			// ignore
+		}
+	}
+
+	@Override
+	public void cloningSubmodule(String path) {
+		try {
+			outw.println(MessageFormat.format(
+					CLIText.get().cloningInto, path));
+			outw.flush();
+		} catch (IOException e) {
+			// ignore
+		}
+	}
+
+	@Override
+	public void checkingOut(AnyObjectId commit, String path) {
+		try {
+			outw.println(MessageFormat.format(CLIText.get().checkingOut,
+					path, commit.getName()));
+			outw.flush();
+		} catch (IOException e) {
+			// ignore
+		}
+	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
index ed06733..5ed23b9 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
@@ -45,11 +45,15 @@
 
 package org.eclipse.jgit.pgm;
 
+import java.io.IOException;
+import java.text.MessageFormat;
 import java.util.List;
 
 import org.eclipse.jgit.api.FetchCommand;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
+import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.lib.TextProgressMonitor;
 import org.eclipse.jgit.transport.FetchResult;
 import org.eclipse.jgit.transport.RefSpec;
@@ -58,7 +62,7 @@
 import org.kohsuke.args4j.Option;
 
 @Command(common = true, usage = "usage_updateRemoteRefsFromAnotherRepository")
-class Fetch extends AbstractFetchCommand {
+class Fetch extends AbstractFetchCommand implements FetchCommand.Callback {
 	@Option(name = "--timeout", metaVar = "metaVar_seconds", usage = "usage_abortConnectionIfNoActivity")
 	int timeout = -1;
 
@@ -96,6 +100,31 @@
 		tags = Boolean.FALSE;
 	}
 
+	private FetchRecurseSubmodulesMode recurseSubmodules;
+
+	@Option(name = "--recurse-submodules", usage = "usage_recurseSubmodules")
+	void recurseSubmodules(String mode) {
+		if (mode == null || mode.isEmpty()) {
+			recurseSubmodules = FetchRecurseSubmodulesMode.YES;
+		} else {
+			for (FetchRecurseSubmodulesMode m : FetchRecurseSubmodulesMode
+					.values()) {
+				if (m.matchConfigValue(mode)) {
+					recurseSubmodules = m;
+					return;
+				}
+			}
+			throw die(MessageFormat
+					.format(CLIText.get().invalidRecurseSubmodulesMode, mode));
+		}
+	}
+
+	@Option(name = "--no-recurse-submodules", usage = "usage_noRecurseSubmodules")
+	void noRecurseSubmodules(@SuppressWarnings("unused")
+	final boolean ignored) {
+		recurseSubmodules = FetchRecurseSubmodulesMode.NO;
+	}
+
 	@Argument(index = 0, metaVar = "metaVar_uriish")
 	private String remote = Constants.DEFAULT_REMOTE_NAME;
 
@@ -124,12 +153,25 @@
 				fetch.setThin(thin.booleanValue());
 			if (quiet == null || !quiet.booleanValue())
 				fetch.setProgressMonitor(new TextProgressMonitor(errw));
+			fetch.setRecurseSubmodules(recurseSubmodules).setCallback(this);
 
 			FetchResult result = fetch.call();
-			if (result.getTrackingRefUpdates().isEmpty())
+			if (result.getTrackingRefUpdates().isEmpty()
+					&& result.submoduleResults().isEmpty())
 				return;
 
 			showFetchResult(result);
 		}
 	}
+
+	@Override
+	public void fetchingSubmodule(String name) {
+		try {
+			outw.println(MessageFormat.format(CLIText.get().fetchingSubmodule,
+					name));
+			outw.flush();
+		} catch (IOException e) {
+			// ignore
+		}
+	}
 }
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 3addecb..c94ba0b 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
@@ -44,8 +44,11 @@
 
 package org.eclipse.jgit.pgm;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.net.MalformedURLException;
@@ -54,6 +57,10 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jgit.awtui.AwtAuthenticator;
 import org.eclipse.jgit.awtui.AwtCredentialsProvider;
@@ -95,6 +102,8 @@
 
 	PrintWriter writer;
 
+	private ExecutorService gcExecutor;
+
 	/**
 	 *
 	 */
@@ -102,6 +111,17 @@
 		HttpTransport.setConnectionFactory(new HttpClientConnectionFactory());
 		CleanFilter.register();
 		SmudgeFilter.register();
+		gcExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+			private final ThreadFactory baseFactory = Executors
+					.defaultThreadFactory();
+
+			@Override
+			public Thread newThread(Runnable taskBody) {
+				Thread thr = baseFactory.newThread(taskBody);
+				thr.setName("JGit-autoGc"); //$NON-NLS-1$
+				return thr;
+			}
+		});
 	}
 
 	/**
@@ -189,10 +209,12 @@
 			// broken pipe
 			exit(1, null);
 		}
+		gcExecutor.shutdown();
+		gcExecutor.awaitTermination(10, TimeUnit.MINUTES);
 	}
 
 	PrintWriter createErrorWriter() {
-		return new PrintWriter(System.err);
+		return new PrintWriter(new OutputStreamWriter(System.err, UTF_8));
 	}
 
 	private void execute(final String[] argv) throws Exception {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 4842b98..e012372 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -121,6 +121,7 @@
 	/***/ public String cantWrite;
 	/***/ public String changesNotStagedForCommit;
 	/***/ public String changesToBeCommitted;
+	/***/ public String checkingOut;
 	/***/ public String checkoutConflict;
 	/***/ public String checkoutConflictPathLine;
 	/***/ public String cleanRequireForce;
@@ -142,12 +143,14 @@
 	/***/ public String failedToLockTag;
 	/***/ public String fatalError;
 	/***/ public String fatalThisProgramWillDestroyTheRepository;
+	/***/ public String fetchingSubmodule;
 	/***/ public String fileIsRequired;
 	/***/ public String ffNotPossibleAborting;
 	/***/ public String forcedUpdate;
 	/***/ public String fromURI;
 	/***/ public String initializedEmptyGitRepositoryIn;
 	/***/ public String invalidHttpProxyOnlyHttpSupported;
+	/***/ public String invalidRecurseSubmodulesMode;
 	/***/ public String jgitVersion;
 	/***/ public String lfsNoAccessKey;
 	/***/ public String lfsNoSecretKey;
@@ -263,6 +266,7 @@
 	/***/ public String statusDeletedByUs;
 	/***/ public String statusBothAdded;
 	/***/ public String statusBothModified;
+	/***/ public String submoduleRegistered;
 	/***/ public String switchedToNewBranch;
 	/***/ public String switchedToBranch;
 	/***/ public String tagAlreadyExists;
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index a328049..7bacb05 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -2,54 +2,54 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
- org.eclipse.jgit.api;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.api.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.attributes;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.awtui;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.blame;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.diff;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.dircache;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.events;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.fnmatch;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.gitrepo;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.hooks;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.ignore;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.ignore.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.junit;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lfs;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.merge;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.notes;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.patch;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.pgm;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.pgm.internal;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revplot;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.file;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.storage.pack;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.submodule;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.http;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport.resolver;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util.io;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util.sha1;version="[4.7.9,4.8.0)",
+ org.eclipse.jgit.api;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.api.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.attributes;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.awtui;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.blame;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.diff;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.dircache;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.events;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.fnmatch;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.gitrepo;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.hooks;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.ignore;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.junit;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lfs;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.merge;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.notes;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.patch;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.pgm;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revplot;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.file;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.storage.pack;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.submodule;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.http;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util.io;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util.sha1;version="[4.8.1,4.9.0)",
  org.junit;version="[4.4.0,5.0.0)",
  org.junit.experimental.theories;version="[4.4.0,5.0.0)",
  org.junit.rules;version="[4.11.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index c2c21c0..c373112 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
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 b405f6a..6f6b115 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
@@ -61,6 +61,7 @@
 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.Repository;
 import org.eclipse.jgit.lib.RepositoryState;
 import org.eclipse.jgit.lib.StoredConfig;
@@ -148,6 +149,57 @@
 	}
 
 	@Test
+	public void testPullFastForwardDetachedHead() throws Exception {
+		Repository repository = source.getRepository();
+		writeToFile(sourceFile, "2nd commit");
+		source.add().addFilepattern("SomeFile.txt").call();
+		source.commit().setMessage("2nd commit").call();
+
+		try (RevWalk revWalk = new RevWalk(repository)) {
+			// git checkout HEAD^
+			String initialBranch = repository.getBranch();
+			Ref initialRef = repository.findRef(Constants.HEAD);
+			RevCommit initialCommit = revWalk
+					.parseCommit(initialRef.getObjectId());
+			assertEquals("this test need linear history", 1,
+					initialCommit.getParentCount());
+			source.checkout().setName(initialCommit.getParent(0).getName())
+					.call();
+			assertFalse("expected detached HEAD",
+					repository.getFullBranch().startsWith(Constants.R_HEADS));
+
+			// change and commit another file
+			File otherFile = new File(sourceFile.getParentFile(),
+					System.currentTimeMillis() + ".tst");
+			writeToFile(otherFile, "other 2nd commit");
+			source.add().addFilepattern(otherFile.getName()).call();
+			RevCommit newCommit = source.commit().setMessage("other 2nd commit")
+					.call();
+
+			// git pull --rebase initialBranch
+			source.pull().setRebase(true).setRemote(".")
+					.setRemoteBranchName(initialBranch)
+					.call();
+
+			assertEquals(RepositoryState.SAFE,
+					source.getRepository().getRepositoryState());
+			Ref head = source.getRepository().findRef(Constants.HEAD);
+			RevCommit headCommit = revWalk.parseCommit(head.getObjectId());
+
+			// HEAD^ == initialCommit, no merge commit
+			assertEquals(1, headCommit.getParentCount());
+			assertEquals(initialCommit, headCommit.getParent(0));
+
+			// both contributions for both commits are available
+			assertFileContentsEqual(sourceFile, "2nd commit");
+			assertFileContentsEqual(otherFile, "other 2nd commit");
+			// HEAD has same message as rebased commit
+			assertEquals(newCommit.getShortMessage(),
+					headCommit.getShortMessage());
+		}
+	}
+
+	@Test
 	public void testPullConflict() throws Exception {
 		PullResult res = target.pull().call();
 		// nothing to update since we don't have different data yet
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractDiffTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractDiffTestCase.java
index b8f8dcb..4130b7e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractDiffTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/AbstractDiffTestCase.java
@@ -196,6 +196,32 @@
 	}
 
 	@Test
+	public void testEdit_DeleteNearCommonTail() {
+		EditList r = diff(t("aCq}nD}nb"), t("aq}nb"));
+		assertEquals(new Edit(1, 2, 1, 1), r.get(0));
+		assertEquals(new Edit(5, 8, 4, 4), r.get(1));
+		assertEquals(2, r.size());
+	}
+
+	@Test
+	public void testEdit_DeleteNearCommonCenter() {
+		EditList r = diff(t("abcd123123uvwxpq"), t("aBcd123uvwxPq"));
+		assertEquals(new Edit(1, 2, 1, 2), r.get(0));
+		assertEquals(new Edit(7, 10, 7, 7), r.get(1));
+		assertEquals(new Edit(14, 15, 11, 12), r.get(2));
+		assertEquals(3, r.size());
+	}
+
+	@Test
+	public void testEdit_InsertNearCommonCenter() {
+		EditList r = diff(t("aBcd123uvwxPq"), t("abcd123123uvwxpq"));
+		assertEquals(new Edit(1, 2, 1, 2), r.get(0));
+		assertEquals(new Edit(7, 7, 7, 10), r.get(1));
+		assertEquals(new Edit(11, 12, 14, 15), r.get(2));
+		assertEquals(3, r.size());
+	}
+
+	@Test
 	public void testEdit_LinuxBug() {
 		EditList r = diff(t("a{bcdE}z"), t("a{0bcdEE}z"));
 		assertEquals(new Edit(2, 2, 2, 3), r.get(0));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandSymlinkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandSymlinkTest.java
new file mode 100644
index 0000000..12f4dcc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandSymlinkTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016, 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.gitrepo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RepoCommandSymlinkTest extends RepositoryTestCase {
+	@Before
+	public void beforeMethod() {
+		// If this assumption fails the tests are skipped. When running on a
+		// filesystem not supporting symlinks I don't want this tests
+		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+	}
+
+	private Repository defaultDb;
+
+	private String rootUri;
+	private String defaultUri;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+
+		defaultDb = createWorkRepository();
+		try (Git git = new Git(defaultDb)) {
+			JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "hello world");
+			git.add().addFilepattern("hello.txt").call();
+			git.commit().setMessage("Initial commit").call();
+			addRepoToClose(defaultDb);
+		}
+
+		defaultUri = defaultDb.getDirectory().toURI().toString();
+		int root = defaultUri.lastIndexOf("/",
+				defaultUri.lastIndexOf("/.git") - 1)
+				+ 1;
+		rootUri = defaultUri.substring(0, root)
+				+ "manifest";
+		defaultUri = defaultUri.substring(root);
+	}
+
+	@Test
+	public void testLinkFileBare() throws Exception {
+		try (
+				Repository remoteDb = createBareRepository();
+				Repository tempDb = createWorkRepository()) {
+			StringBuilder xmlContent = new StringBuilder();
+			xmlContent
+					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+					.append("<manifest>")
+					.append("<remote name=\"remote1\" fetch=\".\" />")
+					.append("<default revision=\"master\" remote=\"remote1\" />")
+					.append("<project path=\"foo\" name=\"").append(defaultUri)
+					.append("\" revision=\"master\" >")
+					.append("<linkfile src=\"hello.txt\" dest=\"LinkedHello\" />")
+					.append("<linkfile src=\"hello.txt\" dest=\"foo/LinkedHello\" />")
+					.append("<linkfile src=\"hello.txt\" dest=\"subdir/LinkedHello\" />")
+					.append("</project>")
+					.append("<project path=\"bar/baz\" name=\"")
+					.append(defaultUri).append("\" revision=\"master\" >")
+					.append("<linkfile src=\"hello.txt\" dest=\"bar/foo/LinkedHello\" />")
+					.append("</project>").append("</manifest>");
+			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+					xmlContent.toString());
+			RepoCommand command = new RepoCommand(remoteDb);
+			command.setPath(
+					tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+					.setURI(rootUri).call();
+			// Clone it
+			File directory = createTempDirectory("testCopyFileBare");
+			Repository localDb = Git.cloneRepository().setDirectory(directory)
+					.setURI(remoteDb.getDirectory().toURI().toString()).call()
+					.getRepository();
+
+			// The LinkedHello symlink should exist.
+			File linkedhello = new File(localDb.getWorkTree(), "LinkedHello");
+			assertTrue("The LinkedHello file should exist",
+					localDb.getFS().exists(linkedhello));
+			assertTrue("The LinkedHello file should be a symlink",
+					localDb.getFS().isSymLink(linkedhello));
+			assertEquals("foo/hello.txt",
+					localDb.getFS().readSymLink(linkedhello));
+
+			// The foo/LinkedHello file should be skipped.
+			File linkedfoohello = new File(localDb.getWorkTree(), "foo/LinkedHello");
+			assertFalse("The foo/LinkedHello file should be skipped",
+					localDb.getFS().exists(linkedfoohello));
+
+			// The subdir/LinkedHello file should use a relative ../
+			File linkedsubdirhello = new File(localDb.getWorkTree(),
+					"subdir/LinkedHello");
+			assertTrue("The subdir/LinkedHello file should exist",
+					localDb.getFS().exists(linkedsubdirhello));
+			assertTrue("The subdir/LinkedHello file should be a symlink",
+					localDb.getFS().isSymLink(linkedsubdirhello));
+			assertEquals("../foo/hello.txt",
+					localDb.getFS().readSymLink(linkedsubdirhello));
+
+			// The bar/foo/LinkedHello file should use a single relative ../
+			File linkedbarfoohello = new File(localDb.getWorkTree(),
+					"bar/foo/LinkedHello");
+			assertTrue("The bar/foo/LinkedHello file should exist",
+					localDb.getFS().exists(linkedbarfoohello));
+			assertTrue("The bar/foo/LinkedHello file should be a symlink",
+					localDb.getFS().isSymLink(linkedbarfoohello));
+			assertEquals("../baz/hello.txt",
+					localDb.getFS().readSymLink(linkedbarfoohello));
+
+			localDb.close();
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index 9cf4569..6ed2c21 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -46,12 +46,14 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
@@ -184,6 +186,107 @@
 	}
 
 	@Test
+	public void androidSetup() throws Exception {
+		Repository child = Git.cloneRepository()
+				.setURI(groupADb.getDirectory().toURI().toString())
+				.setDirectory(createUniqueTestGitDir(true)).setBare(true).call()
+				.getRepository();
+
+		Repository dest = Git.cloneRepository()
+				.setURI(db.getDirectory().toURI().toString())
+				.setDirectory(createUniqueTestGitDir(true)).setBare(true).call()
+				.getRepository();
+
+		assertTrue(dest.isBare());
+		assertTrue(child.isBare());
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+			.append("<manifest>")
+			.append("<remote name=\"remote1\" fetch=\"..\" />")
+			.append("<default revision=\"master\" remote=\"remote1\" />")
+			.append("<project path=\"base\" name=\"platform/base\" />")
+			.append("</manifest>");
+		RepoCommand cmd = new RepoCommand(dest);
+
+		IndexedRepos repos = new IndexedRepos();
+		repos.put("platform/base", child);
+
+		RevCommit commit = cmd
+			.setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(StandardCharsets.UTF_8)))
+			.setRemoteReader(repos)
+			.setURI("platform/")
+			.setTargetURI("platform/superproject")
+			.setRecordRemoteBranch(true)
+			.setRecordSubmoduleLabels(true)
+			.call();
+
+		String idStr = commit.getId().name() + ":" + ".gitmodules";
+		ObjectId modId = dest.resolve(idStr);
+
+		try (ObjectReader reader = dest.newObjectReader()) {
+			byte[] bytes = reader.open(modId).getCachedBytes(Integer.MAX_VALUE);
+			Config base = new Config();
+			BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
+			String subUrl = cfg.getString("submodule", "base", "url");
+			assertEquals(subUrl, "../base");
+		}
+
+		child.close();
+		dest.close();
+	}
+
+	@Test
+	public void gerritSetup() throws Exception {
+		Repository child =
+			Git.cloneRepository().setURI(groupADb.getDirectory().toURI().toString())
+				.setDirectory(createUniqueTestGitDir(true))
+				.setBare(true).call().getRepository();
+
+		Repository dest = Git.cloneRepository()
+			.setURI(db.getDirectory().toURI().toString()).setDirectory(createUniqueTestGitDir(true))
+			.setBare(true).call().getRepository();
+
+		assertTrue(dest.isBare());
+		assertTrue(child.isBare());
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+			.append("<manifest>")
+			.append("<remote name=\"remote1\" fetch=\".\" />")
+			.append("<default revision=\"master\" remote=\"remote1\" />")
+			.append("<project path=\"plugins/cookbook\" name=\"plugins/cookbook\" />")
+			.append("</manifest>");
+		RepoCommand cmd = new RepoCommand(dest);
+
+		IndexedRepos repos = new IndexedRepos();
+		repos.put("plugins/cookbook", child);
+
+		RevCommit commit = cmd
+			.setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(StandardCharsets.UTF_8)))
+			.setRemoteReader(repos)
+			.setURI("")
+			.setTargetURI("gerrit")
+			.setRecordRemoteBranch(true)
+			.setRecordSubmoduleLabels(true)
+			.call();
+
+		String idStr = commit.getId().name() + ":" + ".gitmodules";
+		ObjectId modId = dest.resolve(idStr);
+
+		try (ObjectReader reader = dest.newObjectReader()) {
+			byte[] bytes = reader.open(modId).getCachedBytes(Integer.MAX_VALUE);
+			Config base = new Config();
+			BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
+			String subUrl = cfg.getString("submodule", "plugins/cookbook", "url");
+			assertEquals(subUrl, "../plugins/cookbook");
+		}
+
+		child.close();
+		dest.close();
+	}
+
+	@Test
 	public void absoluteRemoteURL() throws Exception {
 		Repository child =
 			Git.cloneRepository().setURI(groupADb.getDirectory().toURI().toString())
@@ -217,6 +320,7 @@
 					.setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(StandardCharsets.UTF_8)))
 					.setRemoteReader(repos)
 					.setURI(baseUrl)
+					.setTargetURI("gerrit")
 					.setRecordRemoteBranch(true)
 					.setRecordSubmoduleLabels(true)
 					.call();
@@ -997,4 +1101,28 @@
 			start = newStart;
 		}
 	}
+
+	void testRelative(String a, String b, String want) {
+		String got = RepoCommand.relativize(URI.create(a), URI.create(b)).toString();
+
+		if (!got.equals(want)) {
+			fail(String.format("relative('%s', '%s') = '%s', want '%s'", a, b, got, want));
+		}
+	}
+
+	@Test
+	public void relative() {
+		testRelative("a/b/", "a/", "../");
+		// Normalization:
+		testRelative("a/p/..//b/", "a/", "../");
+		testRelative("a/b", "a/", "");
+		testRelative("a/", "a/b/", "b/");
+		testRelative("a/", "a/b", "b");
+		testRelative("/a/b/c", "/b/c", "../../b/c");
+		testRelative("/abc", "bcd", "bcd");
+		testRelative("abc", "def", "def");
+		testRelative("abc", "/bcd", "/bcd");
+		testRelative("http://a", "a/b", "a/b");
+		testRelative("http://base.com/a/", "http://child.com/a/b", "http://child.com/a/b");
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
index c70b6f2..17c1835 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
@@ -7,7 +7,6 @@
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -180,41 +179,133 @@
 		RevCommit commit1 = commit().message("1").parent(commit0).create();
 		git.update("master", commit0);
 
-		gcNoTtl();
 		gcWithTtl();
-
-		// The repository has an UNREACHABLE_GARBAGE pack that could have
-		// expired, but since we never purge the most recent UNREACHABLE_GARBAGE
-		// pack, it must have survived the GC.
-		boolean commit1Found = false;
+		// The repository should have a GC pack with commit0 and an
+		// UNREACHABLE_GARBAGE pack with commit1.
+		assertEquals(2, odb.getPacks().length);
+		boolean gcPackFound = false;
+		boolean garbagePackFound = false;
 		for (DfsPackFile pack : odb.getPacks()) {
 			DfsPackDescription d = pack.getPackDescription();
 			if (d.getPackSource() == GC) {
+				gcPackFound = true;
 				assertTrue("has commit0", isObjectInPack(commit0, pack));
 				assertFalse("no commit1", isObjectInPack(commit1, pack));
 			} else if (d.getPackSource() == UNREACHABLE_GARBAGE) {
-				commit1Found |= isObjectInPack(commit1, pack);
+				garbagePackFound = true;
+				assertFalse("no commit0", isObjectInPack(commit0, pack));
+				assertTrue("has commit1", isObjectInPack(commit1, pack));
 			} else {
 				fail("unexpected " + d.getPackSource());
 			}
 		}
-		assertTrue("garbage commit1 still readable", commit1Found);
-
-		// Find oldest UNREACHABLE_GARBAGE; it will be pruned by next GC.
-		DfsPackDescription oldestGarbagePack = null;
-		for (DfsPackFile pack : odb.getPacks()) {
-			DfsPackDescription d = pack.getPackDescription();
-			if (d.getPackSource() == UNREACHABLE_GARBAGE) {
-				oldestGarbagePack = oldestPack(oldestGarbagePack, d);
-			}
-		}
-		assertNotNull("has UNREACHABLE_GARBAGE", oldestGarbagePack);
+		assertTrue("gc pack found", gcPackFound);
+		assertTrue("garbage pack found", garbagePackFound);
 
 		gcWithTtl();
-		assertTrue("has packs", odb.getPacks().length > 0);
+		// The gc operation should have removed UNREACHABLE_GARBAGE pack along with commit1.
+		DfsPackFile[] packs = odb.getPacks();
+		assertEquals(1, packs.length);
+
+		assertEquals(GC, packs[0].getPackDescription().getPackSource());
+		assertTrue("has commit0", isObjectInPack(commit0, packs[0]));
+		assertFalse("no commit1", isObjectInPack(commit1, packs[0]));
+	}
+
+	@Test
+	public void testCollectionWithGarbageAndRereferencingGarbage()
+			throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit0);
+
+		gcWithTtl();
+		// The repository should have a GC pack with commit0 and an
+		// UNREACHABLE_GARBAGE pack with commit1.
+		assertEquals(2, odb.getPacks().length);
+		boolean gcPackFound = false;
+		boolean garbagePackFound = false;
 		for (DfsPackFile pack : odb.getPacks()) {
-			assertNotEquals(oldestGarbagePack, pack.getPackDescription());
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC) {
+				gcPackFound = true;
+				assertTrue("has commit0", isObjectInPack(commit0, pack));
+				assertFalse("no commit1", isObjectInPack(commit1, pack));
+			} else if (d.getPackSource() == UNREACHABLE_GARBAGE) {
+				garbagePackFound = true;
+				assertFalse("no commit0", isObjectInPack(commit0, pack));
+				assertTrue("has commit1", isObjectInPack(commit1, pack));
+			} else {
+				fail("unexpected " + d.getPackSource());
+			}
 		}
+		assertTrue("gc pack found", gcPackFound);
+		assertTrue("garbage pack found", garbagePackFound);
+
+		git.update("master", commit1);
+
+		gcWithTtl();
+		// The gc operation should have removed the UNREACHABLE_GARBAGE pack and
+		// moved commit1 into GC pack.
+		DfsPackFile[] packs = odb.getPacks();
+		assertEquals(1, packs.length);
+
+		assertEquals(GC, packs[0].getPackDescription().getPackSource());
+		assertTrue("has commit0", isObjectInPack(commit0, packs[0]));
+		assertTrue("has commit1", isObjectInPack(commit1, packs[0]));
+	}
+
+	@Test
+	public void testCollectionWithPureGarbageAndGarbagePacksPurged()
+			throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+
+		gcWithTtl();
+		// The repository should have a single UNREACHABLE_GARBAGE pack with commit0
+		// and commit1.
+		DfsPackFile[] packs = odb.getPacks();
+		assertEquals(1, packs.length);
+
+		assertEquals(UNREACHABLE_GARBAGE, packs[0].getPackDescription().getPackSource());
+		assertTrue("has commit0", isObjectInPack(commit0, packs[0]));
+		assertTrue("has commit1", isObjectInPack(commit1, packs[0]));
+
+		gcWithTtl();
+		// The gc operation should have removed UNREACHABLE_GARBAGE pack along
+		// with commit0 and commit1.
+		assertEquals(0, odb.getPacks().length);
+	}
+
+	@Test
+	public void testCollectionWithPureGarbageAndRereferencingGarbage()
+			throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+
+		gcWithTtl();
+		// The repository should have a single UNREACHABLE_GARBAGE pack with commit0
+		// and commit1.
+		DfsPackFile[] packs = odb.getPacks();
+		assertEquals(1, packs.length);
+
+		DfsPackDescription pack = packs[0].getPackDescription();
+		assertEquals(UNREACHABLE_GARBAGE, pack.getPackSource());
+		assertTrue("has commit0", isObjectInPack(commit0, packs[0]));
+		assertTrue("has commit1", isObjectInPack(commit1, packs[0]));
+
+		git.update("master", commit0);
+
+		gcWithTtl();
+		// The gc operation should have moved commit0 into the GC pack and
+		// removed UNREACHABLE_GARBAGE along with commit1.
+		packs = odb.getPacks();
+		assertEquals(1, packs.length);
+
+		pack = packs[0].getPackDescription();
+		assertEquals(GC, pack.getPackSource());
+		assertTrue("has commit0", isObjectInPack(commit0, packs[0]));
+		assertFalse("no commit1", isObjectInPack(commit1, packs[0]));
 	}
 
 	@Test
@@ -583,19 +674,11 @@
 
 	private boolean isObjectInPack(AnyObjectId id, DfsPackFile pack)
 			throws IOException {
-		try (DfsReader reader = new DfsReader(odb)) {
+		try (DfsReader reader = odb.newReader()) {
 			return pack.hasObject(reader, id);
 		}
 	}
 
-	private static DfsPackDescription oldestPack(DfsPackDescription a,
-			DfsPackDescription b) {
-		if (a != null && a.getLastModified() < b.getLastModified()) {
-			return a;
-		}
-		return b;
-	}
-
 	private int countPacks(PackSource source) throws IOException {
 		int cnt = 0;
 		for (DfsPackFile pack : odb.getPacks()) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AlternatesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AlternatesTest.java
new file mode 100644
index 0000000..b09db03
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AlternatesTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017, Matthias Sohn <matthias.sohn@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.internal.storage.file;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.AbortedByHookException;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
+import org.eclipse.jgit.api.errors.NoHeadException;
+import org.eclipse.jgit.api.errors.NoMessageException;
+import org.eclipse.jgit.api.errors.UnmergedPathsException;
+import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
+import org.junit.Test;
+
+public class AlternatesTest extends SampleDataRepositoryTestCase {
+
+	private FileRepository db2;
+
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		db2 = createWorkRepository();
+	}
+
+	private void setAlternate(FileRepository from, FileRepository to)
+			throws IOException {
+		File alt = new File(from.getObjectDatabase().getDirectory(),
+				"info/alternates");
+		alt.getParentFile().mkdirs();
+		File fromDir = from.getObjectDatabase().getDirectory();
+		File toDir = to.getObjectDatabase().getDirectory();
+		Path relative = fromDir.toPath().relativize(toDir.toPath());
+		write(alt, relative.toString() + "\n");
+	}
+
+	@Test
+	public void testAlternate() throws Exception {
+		setAlternate(db2, db);
+		RevCommit c = createCommit();
+		assertCommit(c);
+		assertAlternateObjects(db2);
+	}
+
+	@Test
+	public void testAlternateCyclic2() throws Exception {
+		setAlternate(db2, db);
+		setAlternate(db, db2);
+		RevCommit c = createCommit();
+		assertCommit(c);
+		assertAlternateObjects(db2);
+	}
+
+	@Test
+	public void testAlternateCyclic3() throws Exception {
+		FileRepository db3 = createBareRepository();
+		setAlternate(db2, db3);
+		setAlternate(db3, db);
+		setAlternate(db, db2);
+		RevCommit c = createCommit();
+		assertCommit(c);
+		assertAlternateObjects(db2);
+	}
+
+	private RevCommit createCommit() throws IOException, GitAPIException,
+			NoFilepatternException, NoHeadException, NoMessageException,
+			UnmergedPathsException, ConcurrentRefUpdateException,
+			WrongRepositoryStateException, AbortedByHookException {
+		JGitTestUtil.writeTrashFile(db, "test", "test");
+		Git git = Git.wrap(db2);
+		git.add().addFilepattern("test").call();
+		RevCommit c = git.commit().setMessage("adding test").call();
+		return c;
+	}
+
+	private void assertCommit(RevCommit c) {
+		ObjectDirectory od = db2.getObjectDatabase();
+		assertTrue("can't find expected commit" + c.name(),
+				od.has(c.toObjectId()));
+	}
+
+	private void assertAlternateObjects(FileRepository repo) {
+		// check some objects in alternate
+		final ObjectId alternateObjects[] = new ObjectId[] {
+				ObjectId.fromString("49322bb17d3acc9146f98c97d078513228bbf3c0"),
+				ObjectId.fromString("d0114ab8ac326bab30e3a657a0397578c5a1af88"),
+				ObjectId.fromString("f73b95671f326616d66b2afb3bdfcdbbce110b44"),
+				ObjectId.fromString("6020a3b8d5d636e549ccbd0c53e2764684bb3125"),
+				ObjectId.fromString("0a3d7772488b6b106fb62813c4d6d627918d9181"),
+				ObjectId.fromString("da0f8ed91a8f2f0f067b3bdf26265d5ca48cf82c"),
+				ObjectId.fromString(
+						"cd4bcfc27da62c6b840de700be1c60a7e69952a5") };
+		ObjectDirectory od = repo.getObjectDatabase();
+		for (ObjectId o : alternateObjects) {
+			assertTrue(String.format("can't find object %s in alternate",
+					o.getName()), od.has(o));
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index c817dc3..9b97eb4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -711,7 +711,7 @@
 			}
 			ObjectWalk ow = walk.toObjectWalkWithSameObjects();
 
-			pw.preparePack(NullProgressMonitor.INSTANCE, ow, want, have);
+			pw.preparePack(NullProgressMonitor.INSTANCE, ow, want, have, NONE);
 			String id = pw.computeName().getName();
 			File packdir = new File(repo.getObjectsDirectory(), "pack");
 			File packFile = new File(packdir, "pack-" + id + ".pack");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
index 20b8c51..d9b58e2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
@@ -69,6 +69,15 @@
 
 	@Test
 	public void testBitmapSpansNoMerges() throws Exception {
+		testBitmapSpansNoMerges(false);
+	}
+
+	@Test
+	public void testBitmapSpansNoMergesWithTags() throws Exception {
+		testBitmapSpansNoMerges(true);
+	}
+
+	private void testBitmapSpansNoMerges(boolean withTags) throws Exception {
 		/*
 		 * Commit counts -> expected bitmap counts for history without merges.
 		 * The top 100 contiguous commits should always have bitmaps, and the
@@ -89,7 +98,10 @@
 			assertTrue(nextCommitCount > currentCommits); // programming error
 			for (int i = currentCommits; i < nextCommitCount; i++) {
 				String str = "A" + i;
-				bb.commit().message(str).add(str, str).create();
+				RevCommit rc = bb.commit().message(str).add(str, str).create();
+				if (withTags) {
+					tr.lightweightTag(str, rc);
+				}
 			}
 			currentCommits = nextCommitCount;
 
@@ -233,7 +245,7 @@
 				m8, m9);
 		PackWriterBitmapPreparer preparer = newPeparer(m9, commits);
 		List<BitmapCommit> selection = new ArrayList<>(
-				preparer.selectCommits(commits.size()));
+				preparer.selectCommits(commits.size(), PackWriter.NONE));
 
 		// Verify that the output is ordered by the separate "chains"
 		String[] expected = { m0.name(), m1.name(), m2.name(), m4.name(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 75b574e..f8c2d45 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -1672,6 +1672,20 @@
 		}
 	}
 
+	@Test
+	public void testLongFilename() throws Exception {
+		char[] bytes = new char[253];
+		Arrays.fill(bytes, 'f');
+		String longFileName = new String(bytes);
+		// 1
+		doit(mkmap(longFileName, "a"), mkmap(longFileName, "b"),
+				mkmap(longFileName, "a"));
+		writeTrashFile(longFileName, "a");
+		checkout();
+		assertNoConflicts();
+		assertUpdated(longFileName);
+	}
+
 	public void assertWorkDir(Map<String, String> i)
 			throws CorruptObjectException,
 			IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
index d4a3d62..5af62b6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
@@ -132,6 +132,45 @@
 	}
 
 	/**
+	 * Merge two modifications with a shared delete at the end. The underlying
+	 * diff algorithm has to provide consistent edit results to get the expected
+	 * merge result.
+	 *
+	 * @throws IOException
+	 */
+	@Test
+	public void testTwoModificationsWithSharedDelete() throws IOException {
+		assertEquals(t("Cb}n}"),
+				merge("ab}n}n}", "ab}n}", "Cb}n}"));
+	}
+
+	/**
+	 * Merge modifications with a shared insert in the middle. The
+	 * underlying diff algorithm has to provide consistent edit
+	 * results to get the expected merge result.
+	 *
+	 * @throws IOException
+	 */
+	@Test
+	public void testModificationsWithMiddleInsert() throws IOException {
+		assertEquals(t("aBcd123123uvwxPq"),
+				merge("abcd123uvwxpq", "aBcd123123uvwxPq", "abcd123123uvwxpq"));
+	}
+
+	/**
+	 * Merge modifications with a shared delete in the middle. The
+	 * underlying diff algorithm has to provide consistent edit
+	 * results to get the expected merge result.
+	 *
+	 * @throws IOException
+	 */
+	@Test
+	public void testModificationsWithMiddleDelete() throws IOException {
+		assertEquals(t("Abz}z123Q"),
+				merge("abz}z}z123q", "Abz}z123Q", "abz}z123q"));
+	}
+
+	/**
 	 * Test a conflicting region at the very start of the text.
 	 *
 	 * @throws IOException
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 a08dbbc..d8b8750 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
@@ -42,12 +42,17 @@
  */
 package org.eclipse.jgit.merge;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.Arrays;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.MergeResult;
@@ -59,8 +64,14 @@
 import org.eclipse.jgit.errors.NoMergeBaseException;
 import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
 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.treewalk.FileTreeIterator;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
@@ -408,7 +419,7 @@
 
 	/**
 	 * Merging two equal subtrees with an incore merger should lead to a merged
-	 * state (The 'Gerrit' use case).
+	 * state.
 	 *
 	 * @param strategy
 	 * @throws Exception
@@ -442,6 +453,43 @@
 	}
 
 	/**
+	 * Merging two equal subtrees with an incore merger should lead to a merged
+	 * state, without using a Repository (the 'Gerrit' use case).
+	 *
+	 * @param strategy
+	 * @throws Exception
+	 */
+	@Theory
+	public void checkMergeEqualTreesInCore_noRepo(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("d/1", "orig");
+		git.add().addFilepattern("d/1").call();
+		RevCommit first = git.commit().setMessage("added d/1").call();
+
+		writeTrashFile("d/1", "modified");
+		RevCommit masterCommit = git.commit().setAll(true)
+				.setMessage("modified d/1 on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("d/1", "modified");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified d/1 on side").call();
+
+		git.rm().addFilepattern("d/1").call();
+		git.rm().addFilepattern("d").call();
+
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ThreeWayMerger resolveMerger =
+					(ThreeWayMerger) strategy.newMerger(ins, db.getConfig());
+			boolean noProblems = resolveMerger.merge(masterCommit, sideCommit);
+			assertTrue(noProblems);
+		}
+	}
+
+	/**
 	 * Merging two equal subtrees when the index and HEAD does not contain any
 	 * file in that subtree should lead to a merged state.
 	 *
@@ -586,6 +634,136 @@
 		}
 	}
 
+	@Theory
+	public void checkContentMergeNoConflict(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("file", "1\n2\n3");
+		git.add().addFilepattern("file").call();
+		RevCommit first = git.commit().setMessage("added file").call();
+
+		writeTrashFile("file", "1master\n2\n3");
+		git.commit().setAll(true).setMessage("modified file on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("file", "1\n2\n3side");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified file on side").call();
+
+		git.checkout().setName("master").call();
+		MergeResult result =
+				git.merge().setStrategy(strategy).include(sideCommit).call();
+		assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+		String expected = "1master\n2\n3side";
+		assertEquals(expected, read("file"));
+	}
+
+	@Theory
+	public void checkContentMergeNoConflict_noRepo(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("file", "1\n2\n3");
+		git.add().addFilepattern("file").call();
+		RevCommit first = git.commit().setMessage("added file").call();
+
+		writeTrashFile("file", "1master\n2\n3");
+		RevCommit masterCommit = git.commit().setAll(true)
+				.setMessage("modified file on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("file", "1\n2\n3side");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified file on side").call();
+
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ResolveMerger merger =
+					(ResolveMerger) strategy.newMerger(ins, db.getConfig());
+			boolean noProblems = merger.merge(masterCommit, sideCommit);
+			assertTrue(noProblems);
+			assertEquals("1master\n2\n3side",
+					readBlob(merger.getResultTreeId(), "file"));
+		}
+	}
+
+	@Theory
+	public void checkContentMergeConflict(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("file", "1\n2\n3");
+		git.add().addFilepattern("file").call();
+		RevCommit first = git.commit().setMessage("added file").call();
+
+		writeTrashFile("file", "1master\n2\n3");
+		git.commit().setAll(true).setMessage("modified file on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("file", "1side\n2\n3");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified file on side").call();
+
+		git.checkout().setName("master").call();
+		MergeResult result =
+				git.merge().setStrategy(strategy).include(sideCommit).call();
+		assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+		String expected = "<<<<<<< HEAD\n"
+				+ "1master\n"
+				+ "=======\n"
+				+ "1side\n"
+				+ ">>>>>>> " + sideCommit.name() + "\n"
+				+ "2\n"
+				+ "3";
+		assertEquals(expected, read("file"));
+	}
+
+	@Theory
+	public void checkContentMergeConflict_noTree(MergeStrategy strategy)
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		writeTrashFile("file", "1\n2\n3");
+		git.add().addFilepattern("file").call();
+		RevCommit first = git.commit().setMessage("added file").call();
+
+		writeTrashFile("file", "1master\n2\n3");
+		RevCommit masterCommit = git.commit().setAll(true)
+				.setMessage("modified file on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(first)
+				.setName("side").call();
+		writeTrashFile("file", "1side\n2\n3");
+		RevCommit sideCommit = git.commit().setAll(true)
+				.setMessage("modified file on side").call();
+
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			ResolveMerger merger =
+					(ResolveMerger) strategy.newMerger(ins, db.getConfig());
+			boolean noProblems = merger.merge(masterCommit, sideCommit);
+			assertFalse(noProblems);
+			assertEquals(Arrays.asList("file"), merger.getUnmergedPaths());
+
+			MergeFormatter fmt = new MergeFormatter();
+			merger.getMergeResults().get("file");
+			try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+				fmt.formatMerge(out, merger.getMergeResults().get("file"),
+						"BASE", "OURS", "THEIRS", UTF_8.name());
+				String expected = "<<<<<<< OURS\n"
+						+ "1master\n"
+						+ "=======\n"
+						+ "1side\n"
+						+ ">>>>>>> THEIRS\n"
+						+ "2\n"
+						+ "3";
+				assertEquals(expected, new String(out.toByteArray(), UTF_8));
+			}
+		}
+	}
+
 	/**
 	 * Merging after criss-cross merges. In this case we merge together two
 	 * commits which have two equally good common ancestors
@@ -817,4 +995,15 @@
 						curMod >= lastMod);
 		}
 	}
+
+	private String readBlob(ObjectId treeish, String path) throws Exception {
+		TestRepository<?> tr = new TestRepository<>(db);
+		RevWalk rw = tr.getRevWalk();
+		RevTree tree = rw.parseTree(treeish);
+		RevObject obj = tr.get(tree, path);
+		if (obj == null) {
+			return null;
+		}
+		return new String(rw.getObjectReader().open(obj, OBJ_BLOB).getBytes(), UTF_8);
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index db9d87d..951568e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -72,6 +72,16 @@
 	}
 
 	@Test
+	public void testOurs_noRepo() throws IOException {
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			Merger ourMerger = MergeStrategy.OURS.newMerger(ins, db.getConfig());
+			boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
+			assertTrue(merge);
+			assertEquals(db.resolve("a^{tree}"), ourMerger.getResultTreeId());
+		}
+	}
+
+	@Test
 	public void testTheirs() throws IOException {
 		Merger ourMerger = MergeStrategy.THEIRS.newMerger(db);
 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
@@ -80,6 +90,16 @@
 	}
 
 	@Test
+	public void testTheirs_noRepo() throws IOException {
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			Merger ourMerger = MergeStrategy.THEIRS.newMerger(db);
+			boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
+			assertTrue(merge);
+			assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId());
+		}
+	}
+
+	@Test
 	public void testTrivialTwoWay() throws IOException {
 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
@@ -104,6 +124,16 @@
 	}
 
 	@Test
+	public void testTrivialTwoWay_noRepo() throws IOException {
+		try (ObjectInserter ins = db.newObjectInserter()) {
+			Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(ins, db.getConfig());
+			boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") });
+			assertTrue(merge);
+			assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId());
+		}
+	}
+
+	@Test
 	public void testTrivialTwoWay_conflict() throws IOException {
 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("f"), db.resolve("g") });
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
index a83a993..658b971 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
@@ -45,7 +45,10 @@
 
 package org.eclipse.jgit.transport;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -59,10 +62,16 @@
 import java.util.Set;
 
 import org.eclipse.jgit.errors.MissingBundlePrerequisiteException;
+import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -161,6 +170,39 @@
 		assertTrue(caught);
 	}
 
+	@Test
+	public void testCustomObjectReader() throws Exception {
+		String refName = "refs/heads/blob";
+		String data = "unflushed data";
+		ObjectId id;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		try (Repository repo = new InMemoryRepository(
+					new DfsRepositoryDescription("repo"));
+				ObjectInserter ins = repo.newObjectInserter();
+				ObjectReader or = ins.newReader()) {
+			id = ins.insert(OBJ_BLOB, Constants.encode(data));
+			BundleWriter bw = new BundleWriter(or);
+			bw.include(refName, id);
+			bw.writeBundle(NullProgressMonitor.INSTANCE, out);
+			assertNull(repo.exactRef(refName));
+			try {
+				repo.open(id, OBJ_BLOB);
+				fail("We should not be able to open the unflushed blob");
+			} catch (MissingObjectException e) {
+				// Expected.
+			}
+		}
+
+		try (Repository repo = new InMemoryRepository(
+					new DfsRepositoryDescription("copy"))) {
+			fetchFromBundle(repo, out.toByteArray());
+			Ref ref = repo.exactRef(refName);
+			assertNotNull(ref);
+			assertEquals(id, ref.getObjectId());
+			assertEquals(data, new String(repo.open(id, OBJ_BLOB).getBytes(), UTF_8));
+		}
+	}
+
 	private static FetchResult fetchFromBundle(final Repository newRepo,
 			final byte[] bundle) throws URISyntaxException,
 			NotSupportedException, TransportException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
index 4061b56..2c8273d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java
@@ -172,9 +172,18 @@
 		FS fs = FS.DETECTED.newInstance();
 		assumeTrue(fs instanceof FS_POSIX);
 
-		String r = FS.readPipe(fs.userHome(),
-				new String[] { "bash", "--login", "-c", "foobar" },
+		FS.readPipe(fs.userHome(),
+				new String[] { "/bin/sh", "-c", "exit 1" },
 				Charset.defaultCharset().name());
-		System.out.println(r);
+	}
+
+	@Test(expected = CommandFailedException.class)
+	public void testReadPipeCommandStartFailure()
+			throws CommandFailedException {
+		FS fs = FS.DETECTED.newInstance();
+
+		FS.readPipe(fs.userHome(),
+				  new String[] { "this-command-does-not-exist" },
+				  Charset.defaultCharset().name());
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
index 109d0e6..c0f4965 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
@@ -465,12 +465,12 @@
 
 	@Test
 	public void testRelativize_doc() {
-		// This is the javadoc example
+		// This is the example from the javadoc
 		String base = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\project");
 		String other = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\another_project\\pom.xml");
 		String expected = toOSPathString("..\\another_project\\pom.xml");
 
-		String actual = FileUtils.relativize(base, other);
+		String actual = FileUtils.relativizeNativePath(base, other);
 		assertEquals(expected, actual);
 	}
 
@@ -483,13 +483,13 @@
 		String expectedCaseSensitive = toOSPathString("..\\..\\Git\\test\\d\\f.txt");
 
 		if (systemReader.isWindows()) {
-			String actual = FileUtils.relativize(base, other);
+			String actual = FileUtils.relativizeNativePath(base, other);
 			assertEquals(expectedCaseInsensitive, actual);
 		} else if (systemReader.isMacOS()) {
-			String actual = FileUtils.relativize(base, other);
+			String actual = FileUtils.relativizeNativePath(base, other);
 			assertEquals(expectedCaseInsensitive, actual);
 		} else {
-			String actual = FileUtils.relativize(base, other);
+			String actual = FileUtils.relativizeNativePath(base, other);
 			assertEquals(expectedCaseSensitive, actual);
 		}
 	}
@@ -501,7 +501,7 @@
 		// 'file.java' is treated as a folder
 		String expected = toOSPathString("../../project");
 
-		String actual = FileUtils.relativize(base, other);
+		String actual = FileUtils.relativizeNativePath(base, other);
 		assertEquals(expected, actual);
 	}
 
@@ -511,7 +511,7 @@
 		String other = toOSPathString("file:/home/eclipse/runtime-New_configuration/project_1");
 		String expected = "";
 
-		String actual = FileUtils.relativize(base, other);
+		String actual = FileUtils.relativizeNativePath(base, other);
 		assertEquals(expected, actual);
 	}
 
@@ -521,7 +521,7 @@
 		String other = toOSPathString("/home/eclipse 3.4/runtime New_configuration/project_1/file");
 		String expected = "file";
 
-		String actual = FileUtils.relativize(base, other);
+		String actual = FileUtils.relativizeNativePath(base, other);
 		assertEquals(expected, actual);
 	}
 
diff --git a/org.eclipse.jgit.ui/BUILD b/org.eclipse.jgit.ui/BUILD
index 85ae5c0..eec4a38 100644
--- a/org.eclipse.jgit.ui/BUILD
+++ b/org.eclipse.jgit.ui/BUILD
@@ -2,7 +2,7 @@
 
 java_library(
     name = "ui",
-    srcs = glob(["src/**"]),
+    srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.ui/resources",
     resources = glob(["resources/**"]),
     deps = ["//org.eclipse.jgit:jgit"],
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index ba6b481..4312cc3 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="4.7.9"
-Import-Package: org.eclipse.jgit.errors;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.lib;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.nls;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revplot;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.revwalk;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.transport;version="[4.7.9,4.8.0)",
- org.eclipse.jgit.util;version="[4.7.9,4.8.0)"
+Export-Package: org.eclipse.jgit.awtui;version="4.8.1"
+Import-Package: org.eclipse.jgit.errors;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.lib;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.nls;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revplot;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.revwalk;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.transport;version="[4.8.1,4.9.0)",
+ org.eclipse.jgit.util;version="[4.8.1,4.9.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 9117f0a..06e45ef 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 9fa97b4..18ae9ea 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -3,8 +3,8 @@
     <resource path="META-INF/MANIFEST.MF">
         <filter id="924844039">
             <message_arguments>
-                <message_argument value="4.7.8"/>
-                <message_argument value="4.7.0"/>
+                <message_argument value="4.8.1"/>
+                <message_argument value="4.8.0"/>
             </message_arguments>
         </filter>
     </resource>
@@ -12,29 +12,26 @@
         <filter id="336658481">
             <message_arguments>
                 <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
-                <message_argument value="CONFIG_KEY_AUTODETACH"/>
-            </message_arguments>
-        </filter>
-        <filter id="336658481">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
-                <message_argument value="CONFIG_KEY_LOGEXPIRY"/>
-            </message_arguments>
-        </filter>
-        <filter id="336658481">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
                 <message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
             </message_arguments>
         </filter>
         <filter id="1141899266">
             <message_arguments>
                 <message_argument value="4.5"/>
-                <message_argument value="4.7"/>
+                <message_argument value="4.8"/>
                 <message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/lib/Constants.java" type="org.eclipse.jgit.lib.Constants">
+        <filter comment="LOCK_SUFFIX was backported to 4.7.3" id="1141899266">
+            <message_arguments>
+                <message_argument value="4.7"/>
+                <message_argument value="4.8"/>
+                <message_argument value="LOCK_SUFFIX"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/jgit/lib/GitmoduleEntry.java" type="org.eclipse.jgit.lib.GitmoduleEntry">
         <filter id="1109393411">
             <message_arguments>
@@ -55,17 +52,24 @@
         <filter id="1141899266">
             <message_arguments>
                 <message_argument value="4.5"/>
-                <message_argument value="4.7"/>
+                <message_argument value="4.8"/>
                 <message_argument value="createNewFile(File)"/>
             </message_arguments>
         </filter>
         <filter id="1141899266">
             <message_arguments>
                 <message_argument value="4.5"/>
-                <message_argument value="4.7"/>
+                <message_argument value="4.8"/>
                 <message_argument value="supportsAtomicCreateNewFile()"/>
             </message_arguments>
         </filter>
+        <filter id="1141899266">
+            <message_arguments>
+                <message_argument value="4.7"/>
+                <message_argument value="4.8"/>
+                <message_argument value="createNewFileAtomic(File)"/>
+            </message_arguments>
+        </filter>
         <filter id="1142947843">
             <message_arguments>
                 <message_argument value="4.5.6"/>
@@ -73,4 +77,13 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS$LockToken">
+        <filter id="1141899266">
+            <message_arguments>
+                <message_argument value="4.7"/>
+                <message_argument value="4.8"/>
+                <message_argument value="LockToken"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index 75f4fe6..a8a53f2 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -5,7 +5,7 @@
 ]
 
 SRCS = glob(
-    ["src/**"],
+    ["src/**/*.java"],
     exclude = INSECURE_CIPHER_FACTORY,
 )
 
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 5037e77..9b6a9f1 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 4.7.9.qualifier
+Bundle-Version: 4.8.1.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="4.7.9",
- org.eclipse.jgit.api;version="4.7.9";
+Export-Package: org.eclipse.jgit.annotations;version="4.8.1",
+ org.eclipse.jgit.api;version="4.8.1";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
@@ -21,51 +21,51 @@
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.7.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="4.7.9",
- org.eclipse.jgit.blame;version="4.7.9";
+ org.eclipse.jgit.api.errors;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.8.1",
+ org.eclipse.jgit.blame;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="4.7.9";
+ org.eclipse.jgit.diff;version="4.8.1";
   uses:="org.eclipse.jgit.patch,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="4.7.9";
+ org.eclipse.jgit.dircache;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util,
    org.eclipse.jgit.events,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="4.7.9";
+ org.eclipse.jgit.errors;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.internal.storage.pack,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="4.7.9";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="4.7.9",
- org.eclipse.jgit.gitrepo;version="4.7.9";
+ org.eclipse.jgit.events;version="4.8.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.8.1",
+ org.eclipse.jgit.gitrepo;version="4.8.1";
   uses:="org.eclipse.jgit.api,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.xml.sax.helpers,
    org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="4.7.9";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.7.9";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="4.7.9",
- org.eclipse.jgit.ignore.internal;version="4.7.9";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="4.7.9";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.ketch;version="4.7.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.dfs;version="4.7.9";
+ org.eclipse.jgit.gitrepo.internal;version="4.8.1";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.8.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.8.1",
+ org.eclipse.jgit.ignore.internal;version="4.8.1";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.8.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.ketch;version="4.8.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.8.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.http.server,
    org.eclipse.jgit.http.test,
    org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="4.7.9";
+ org.eclipse.jgit.internal.storage.file;version="4.8.1";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -73,9 +73,9 @@
    org.eclipse.jgit.lfs,
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test",
- org.eclipse.jgit.internal.storage.pack;version="4.7.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="4.7.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="4.7.9";
+ org.eclipse.jgit.internal.storage.pack;version="4.8.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.8.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.8.1";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
@@ -85,32 +85,33 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.submodule",
- org.eclipse.jgit.merge;version="4.7.9";
+ org.eclipse.jgit.lib.internal;version="4.8.1";x-internal:=true,
+ org.eclipse.jgit.merge;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.dircache,
    org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="4.7.9",
- org.eclipse.jgit.notes;version="4.7.9";
+ org.eclipse.jgit.nls;version="4.8.1",
+ org.eclipse.jgit.notes;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.7.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.7.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="4.7.9";
+ org.eclipse.jgit.patch;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="4.7.9";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.7.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.7.9";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.7.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="4.7.9";
+ org.eclipse.jgit.revwalk.filter;version="4.8.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.8.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.8.1";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -122,24 +123,24 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.7.9";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.7.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="4.7.9";
+ org.eclipse.jgit.transport.http;version="4.8.1";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
    org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="4.7.9";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="4.7.9";
+ org.eclipse.jgit.treewalk.filter;version="4.8.1";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.8.1";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.storage.file,
    org.ietf.jgss",
- org.eclipse.jgit.util.io;version="4.7.9",
- org.eclipse.jgit.util.sha1;version="4.7.9",
- org.eclipse.jgit.util.time;version="4.7.9"
+ org.eclipse.jgit.util.io;version="4.8.1",
+ org.eclipse.jgit.util.sha1;version="4.8.1",
+ org.eclipse.jgit.util.time;version="4.8.1"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
  com.jcraft.jsch;version="[0.1.37,0.2.0)",
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 885a9dd..63e6bbc 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 4.7.9.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="4.7.9.qualifier";roots="."
+Bundle-Version: 4.8.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.8.1.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index d64dca7..673f3ff 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>4.7.9-SNAPSHOT</version>
+    <version>4.8.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
@@ -206,8 +206,8 @@
     <pluginManagement>
       <plugins>
         <plugin>
-          <groupId>com.github.spotbugs</groupId>
-          <artifactId>spotbugs-maven-plugin</artifactId>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>findbugs-maven-plugin</artifactId>
           <configuration>
             <excludeFilterFile>findBugs/FindBugsExcludeFilter.xml</excludeFilterFile>
           </configuration>
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 55e786c..e0b5bbf 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -439,6 +439,7 @@
 noHMACsupport=No {0} support: {1}
 noMergeBase=No merge base could be determined. Reason={0}. {1}
 noMergeHeadSpecified=No merge head specified
+nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos
 noSuchRef=no such ref
 notABoolean=Not a boolean: {0}
 notABundle=not a bundle
@@ -546,6 +547,7 @@
 renamesRejoiningModifies=Rejoining modified file pairs
 repositoryAlreadyExists=Repository already exists: {0}
 repositoryConfigFileInvalid=Repository config file {0} invalid {1}
+repositoryIsRequired=repository is required
 repositoryNotFound=repository not found: {0}
 repositoryState_applyMailbox=Apply mailbox
 repositoryState_bare=Bare
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 4b815b4..d450c64 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, 2013 Chris Aniszczyk <caniszczyk@gmail.com>
+ * Copyright (C) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -58,6 +58,7 @@
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -75,6 +76,8 @@
 import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.transport.TagOpt;
 import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
 
 /**
  * Clone a repository into a new working directory
@@ -106,6 +109,46 @@
 
 	private Collection<String> branchesToClone;
 
+	private Callback callback;
+
+	private boolean directoryExistsInitially;
+
+	private boolean gitDirExistsInitially;
+
+	/**
+	 * Callback for status of clone operation.
+	 *
+	 * @since 4.8
+	 */
+	public interface Callback {
+		/**
+		 * Notify initialized submodules.
+		 *
+		 * @param submodules
+		 *            the submodules
+		 *
+		 */
+		void initializedSubmodules(Collection<String> submodules);
+
+		/**
+		 * Notify starting to clone a submodule.
+		 *
+		 * @param path
+		 *            the submodule path
+		 */
+		void cloningSubmodule(String path);
+
+		/**
+		 * Notify checkout of commit
+		 *
+		 * @param commit
+		 *            the id of the commit being checked out
+		 * @param path
+		 *            the submodule path
+		 */
+		void checkingOut(AnyObjectId commit, String path);
+	}
+
 	/**
 	 * Create clone command with no repository set
 	 */
@@ -130,26 +173,55 @@
 	@Override
 	public Git call() throws GitAPIException, InvalidRemoteException,
 			org.eclipse.jgit.api.errors.TransportException {
-		Repository repository = null;
+		URIish u = null;
 		try {
-			URIish u = new URIish(uri);
-			repository = init(u);
-			FetchResult result = fetch(repository, u);
-			if (!noCheckout)
-				checkout(repository, result);
-			return new Git(repository, true);
+			u = new URIish(uri);
+			verifyDirectories(u);
+		} catch (URISyntaxException e) {
+			throw new InvalidRemoteException(
+					MessageFormat.format(JGitText.get().invalidURL, uri));
+		}
+		Repository repository = null;
+		FetchResult fetchResult = null;
+		Thread cleanupHook = new Thread(() -> cleanup());
+		Runtime.getRuntime().addShutdownHook(cleanupHook);
+		try {
+			repository = init();
+			fetchResult = fetch(repository, u);
 		} catch (IOException ioe) {
 			if (repository != null) {
 				repository.close();
 			}
+			cleanup();
 			throw new JGitInternalException(ioe.getMessage(), ioe);
 		} catch (URISyntaxException e) {
 			if (repository != null) {
 				repository.close();
 			}
+			cleanup();
 			throw new InvalidRemoteException(MessageFormat.format(
 					JGitText.get().invalidRemote, remote));
+		} catch (GitAPIException | RuntimeException e) {
+			if (repository != null) {
+				repository.close();
+			}
+			cleanup();
+			throw e;
+		} finally {
+			Runtime.getRuntime().removeShutdownHook(cleanupHook);
 		}
+		if (!noCheckout) {
+			try {
+				checkout(repository, fetchResult);
+			} catch (IOException ioe) {
+				repository.close();
+				throw new JGitInternalException(ioe.getMessage(), ioe);
+			} catch (GitAPIException | RuntimeException e) {
+				repository.close();
+				throw e;
+			}
+		}
+		return new Git(repository, true);
 	}
 
 	private static boolean isNonEmptyDirectory(File dir) {
@@ -160,12 +232,12 @@
 		return false;
 	}
 
-	private Repository init(URIish u) throws GitAPIException {
-		InitCommand command = Git.init();
-		command.setBare(bare);
+	private void verifyDirectories(URIish u) {
 		if (directory == null && gitDir == null) {
 			directory = new File(u.getHumanishName(), Constants.DOT_GIT);
 		}
+		directoryExistsInitially = directory != null && directory.exists();
+		gitDirExistsInitially = gitDir != null && gitDir.exists();
 		validateDirs(directory, gitDir, bare);
 		if (isNonEmptyDirectory(directory)) {
 			throw new JGitInternalException(MessageFormat.format(
@@ -175,6 +247,11 @@
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().cloneNonEmptyDirectory, gitDir.getName()));
 		}
+	}
+
+	private Repository init() throws GitAPIException {
+		InitCommand command = Git.init();
+		command.setBare(bare);
 		if (directory != null) {
 			command.setDirectory(directory);
 		}
@@ -280,12 +357,18 @@
 	private void cloneSubmodules(Repository clonedRepo) throws IOException,
 			GitAPIException {
 		SubmoduleInitCommand init = new SubmoduleInitCommand(clonedRepo);
-		if (init.call().isEmpty())
+		Collection<String> submodules = init.call();
+		if (submodules.isEmpty()) {
 			return;
+		}
+		if (callback != null) {
+			callback.initializedSubmodules(submodules);
+		}
 
 		SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(clonedRepo);
 		configure(update);
 		update.setProgressMonitor(monitor);
+		update.setCallback(callback);
 		if (!update.call().isEmpty()) {
 			SubmoduleWalk walk = SubmoduleWalk.forIndex(clonedRepo);
 			while (walk.next()) {
@@ -523,6 +606,19 @@
 		return this;
 	}
 
+	/**
+	 * Register a progress callback.
+	 *
+	 * @param callback
+	 *            the callback
+	 * @return {@code this}
+	 * @since 4.8
+	 */
+	public CloneCommand setCallback(Callback callback) {
+		this.callback = callback;
+		return this;
+	}
+
 	private static void validateDirs(File directory, File gitDir, boolean bare)
 			throws IllegalStateException {
 		if (directory != null) {
@@ -548,4 +644,38 @@
 			}
 		}
 	}
+
+	private void cleanup() {
+		try {
+			if (directory != null) {
+				if (!directoryExistsInitially) {
+					FileUtils.delete(directory, FileUtils.RECURSIVE
+							| FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS);
+				} else {
+					deleteChildren(directory);
+				}
+			}
+			if (gitDir != null) {
+				if (!gitDirExistsInitially) {
+					FileUtils.delete(gitDir, FileUtils.RECURSIVE
+							| FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS);
+				} else {
+					deleteChildren(directory);
+				}
+			}
+		} catch (IOException e) {
+			// Ignore; this is a best-effort cleanup in error cases, and
+			// IOException should not be raised anyway
+		}
+	}
+
+	private void deleteChildren(File file) throws IOException {
+		if (!FS.DETECTED.isDirectory(file)) {
+			return;
+		}
+		for (File child : file.listFiles()) {
+			FileUtils.delete(child, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING
+					| FileUtils.IGNORE_ERRORS);
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index cc3302b..785c20c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -99,6 +99,24 @@
 
 	private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
 
+	private Callback callback;
+
+	/**
+	 * Callback for status of fetch operation.
+	 *
+	 * @since 4.8
+	 *
+	 */
+	public interface Callback {
+		/**
+		 * Notify fetching a submodule.
+		 *
+		 * @param name
+		 *            the submodule name.
+		 */
+		void fetchingSubmodule(String name);
+	}
+
 	/**
 	 * @param repo
 	 */
@@ -173,6 +191,9 @@
 							.setThin(thin).setRefSpecs(refSpecs)
 							.setDryRun(dryRun)
 							.setRecurseSubmodules(recurseMode);
+					if (callback != null) {
+						callback.fetchingSubmodule(walk.getPath());
+					}
 					results.addSubmodule(walk.getPath(), f.call());
 				}
 			}
@@ -434,4 +455,17 @@
 		this.tagOption = tagOpt;
 		return this;
 	}
+
+	/**
+	 * Register a progress callback.
+	 *
+	 * @param callback
+	 *            the callback
+	 * @return {@code this}
+	 * @since 4.8
+	 */
+	public FetchCommand setCallback(Callback callback) {
+		this.callback = callback;
+		return this;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index ae822da..9c5ae43 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -203,62 +203,63 @@
 	@Override
 	public PullResult call() throws GitAPIException,
 			WrongRepositoryStateException, InvalidConfigurationException,
-			DetachedHeadException, InvalidRemoteException, CanceledException,
+			InvalidRemoteException, CanceledException,
 			RefNotFoundException, RefNotAdvertisedException, NoHeadException,
 			org.eclipse.jgit.api.errors.TransportException {
 		checkCallable();
 
 		monitor.beginTask(JGitText.get().pullTaskName, 2);
+		Config repoConfig = repo.getConfig();
 
-		String branchName;
+		String branchName = null;
 		try {
 			String fullBranch = repo.getFullBranch();
-			if (fullBranch == null)
-				throw new NoHeadException(
-						JGitText.get().pullOnRepoWithoutHEADCurrentlyNotSupported);
-			if (!fullBranch.startsWith(Constants.R_HEADS)) {
-				// we can not pull if HEAD is detached and branch is not
-				// specified explicitly
-				throw new DetachedHeadException();
+			if (fullBranch != null
+					&& fullBranch.startsWith(Constants.R_HEADS)) {
+				branchName = fullBranch.substring(Constants.R_HEADS.length());
 			}
-			branchName = fullBranch.substring(Constants.R_HEADS.length());
 		} catch (IOException e) {
 			throw new JGitInternalException(
 					JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
 					e);
 		}
+		if (remoteBranchName == null && branchName != null) {
+			// get the name of the branch in the remote repository
+			// stored in configuration key branch.<branch name>.merge
+			remoteBranchName = repoConfig.getString(
+					ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
+					ConfigConstants.CONFIG_KEY_MERGE);
+		}
+		if (remoteBranchName == null) {
+			remoteBranchName = branchName;
+		}
+		if (remoteBranchName == null) {
+			throw new NoHeadException(
+					JGitText.get().cannotCheckoutFromUnbornBranch);
+		}
 
 		if (!repo.getRepositoryState().equals(RepositoryState.SAFE))
 			throw new WrongRepositoryStateException(MessageFormat.format(
 					JGitText.get().cannotPullOnARepoWithState, repo
 							.getRepositoryState().name()));
 
-		Config repoConfig = repo.getConfig();
-		if (remote == null) {
+		if (remote == null && branchName != null) {
 			// get the configured remote for the currently checked out branch
 			// stored in configuration key branch.<branch name>.remote
 			remote = repoConfig.getString(
 					ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
 					ConfigConstants.CONFIG_KEY_REMOTE);
 		}
-		if (remote == null)
+		if (remote == null) {
 			// fall back to default remote
 			remote = Constants.DEFAULT_REMOTE_NAME;
-
-		if (remoteBranchName == null)
-			// get the name of the branch in the remote repository
-			// stored in configuration key branch.<branch name>.merge
-			remoteBranchName = repoConfig.getString(
-					ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
-					ConfigConstants.CONFIG_KEY_MERGE);
+		}
 
 		// determines whether rebase should be used after fetching
-		if (pullRebaseMode == null) {
+		if (pullRebaseMode == null && branchName != null) {
 			pullRebaseMode = getRebaseMode(branchName, repoConfig);
 		}
 
-		if (remoteBranchName == null)
-			remoteBranchName = branchName;
 
 		final boolean isRemote = !remote.equals("."); //$NON-NLS-1$
 		String remoteUri;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index 29d5d49..4d3dff0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -89,6 +89,8 @@
 
 	private MergeStrategy strategy = MergeStrategy.RECURSIVE;
 
+	private CloneCommand.Callback callback;
+
 	/**
 	 * @param repo
 	 */
@@ -161,6 +163,9 @@
 				Repository submoduleRepo = generator.getRepository();
 				// Clone repository is not present
 				if (submoduleRepo == null) {
+					if (callback != null) {
+						callback.cloningSubmodule(generator.getPath());
+					}
 					CloneCommand clone = Git.cloneRepository();
 					configure(clone);
 					clone.setURI(url);
@@ -201,6 +206,10 @@
 								Constants.HEAD, true);
 						refUpdate.setNewObjectId(commit);
 						refUpdate.forceUpdate();
+						if (callback != null) {
+							callback.checkingOut(commit,
+									generator.getPath());
+						}
 					}
 				} finally {
 					submoduleRepo.close();
@@ -225,4 +234,17 @@
 		this.strategy = strategy;
 		return this;
 	}
+
+	/**
+	 * Set status callback for submodule clone operation.
+	 *
+	 * @param callback
+	 *            the callback
+	 * @return {@code this}
+	 * @since 4.8
+	 */
+	public SubmoduleUpdateCommand setCallback(CloneCommand.Callback callback) {
+		this.callback = callback;
+		return this;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java
index 08c25c2..b841f57 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java
@@ -38,7 +38,8 @@
 package org.eclipse.jgit.api.errors;
 
 /**
- * Exception thrown when the server rejected a too large pack
+ * Exception thrown when PackParser finds an object larger than a predefined
+ * limit
  *
  * @since 4.4
  */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
index bd6e5c8..5f01366 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
@@ -121,23 +121,7 @@
 			Subsequence<S> as = Subsequence.a(a, region);
 			Subsequence<S> bs = Subsequence.b(b, region);
 			EditList e = Subsequence.toBase(diffNonCommon(cs, as, bs), as, bs);
-
-			// The last insertion may need to be shifted later if it
-			// inserts elements that were previously reduced out as
-			// common at the end.
-			//
-			Edit last = e.get(e.size() - 1);
-			if (last.getType() == Edit.Type.INSERT) {
-				while (last.endB < b.size()
-						&& cmp.equals(b, last.beginB, b, last.endB)) {
-					last.beginA++;
-					last.endA++;
-					last.beginB++;
-					last.endB++;
-				}
-			}
-
-			return e;
+			return normalize(cmp, e, a, b);
 		}
 
 		case EMPTY:
@@ -153,6 +137,107 @@
 	}
 
 	/**
+	 * Reorganize an {@link EditList} for better diff consistency.
+	 * <p>
+	 * {@code DiffAlgorithms} may return {@link Edit.Type#INSERT} or
+	 * {@link Edit.Type#DELETE} edits that can be "shifted". For
+	 * example, the deleted section
+	 * <pre>
+	 * -a
+	 * -b
+	 * -c
+	 *  a
+	 *  b
+	 *  c
+	 * </pre>
+	 * can be shifted down by 1, 2 or 3 locations.
+	 * <p>
+	 * To avoid later merge issues, we shift such edits to a
+	 * consistent location. {@code normalize} uses a simple strategy of
+	 * shifting such edits to their latest possible location.
+	 * <p>
+	 * This strategy may not always produce an aesthetically pleasing
+	 * diff. For instance, it works well with
+	 * <pre>
+	 *  function1 {
+	 *   ...
+	 *  }
+	 *
+	 * +function2 {
+	 * + ...
+	 * +}
+	 * +
+	 * function3 {
+	 * ...
+	 * }
+	 * </pre>
+	 * but less so for
+	 * <pre>
+	 *  #
+	 *  # comment1
+	 *  #
+	 *  function1() {
+	 *  }
+	 *
+	 *  #
+	 * +# comment3
+	 * +#
+	 * +function3() {
+	 * +}
+	 * +
+	 * +#
+	 *  # comment2
+	 *  #
+	 *  function2() {
+	 *  }
+	 * </pre>
+	 * <a href="https://github.com/mhagger/diff-slider-tools">More
+	 * sophisticated strategies</a> are possible, say by calculating a
+	 * suitable "aesthetic cost" for each possible position and using
+	 * the lowest cost, but {@code normalize} just shifts edits
+	 * to the end as much as possible.
+	 *
+	 * @param <S>
+	 *            type of sequence being compared.
+	 * @param cmp
+	 *            the comparator supplying the element equivalence function.
+	 * @param e
+	 *            a modifiable edit list comparing the provided sequences.
+	 * @param a
+	 *            the first (also known as old or pre-image) sequence.
+	 * @param b
+	 *            the second (also known as new or post-image) sequence.
+	 * @return a modifiable edit list with edit regions shifted to their
+	 *         latest possible location. The result list is never null.
+	 * @since 4.7
+	 */
+	private static <S extends Sequence> EditList normalize(
+		SequenceComparator<? super S> cmp, EditList e, S a, S b) {
+		Edit prev = null;
+		for (int i = e.size() - 1; i >= 0; i--) {
+			Edit cur = e.get(i);
+			Edit.Type curType = cur.getType();
+
+			int maxA = (prev == null) ? a.size() : prev.beginA;
+			int maxB = (prev == null) ? b.size() : prev.beginB;
+
+			if (curType == Edit.Type.INSERT) {
+				while (cur.endA < maxA && cur.endB < maxB
+					&& cmp.equals(b, cur.beginB, b, cur.endB)) {
+					cur.shift(1);
+				}
+			} else if (curType == Edit.Type.DELETE) {
+				while (cur.endA < maxA && cur.endB < maxB
+					&& cmp.equals(a, cur.beginA, a, cur.endA)) {
+					cur.shift(1);
+				}
+			}
+			prev = cur;
+		}
+		return e;
+	}
+
+	/**
 	 * Compare two sequences and identify a list of edits between them.
 	 *
 	 * This method should be invoked only after the two sequences have been
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
index 7eecd13..a2e167f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
@@ -170,6 +170,21 @@
 	}
 
 	/**
+	 * Move the edit region by the specified amount.
+	 *
+	 * @param amount
+	 *            the region is shifted by this amount, and can be positive or
+	 *            negative.
+	 * @since 4.8
+	 */
+	public final void shift(int amount) {
+		beginA += amount;
+		endA += amount;
+		beginB += amount;
+		endB += amount;
+	}
+
+	/**
 	 * Construct a new edit representing the region before cut.
 	 *
 	 * @param cut
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 bc52473..d899429 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -220,7 +220,9 @@
 	 * must be allocated, and 1,000,000 file compares may need to be performed.
 	 *
 	 * @param limit
-	 *            new file limit.
+	 *            new file limit. 0 means no limit; a negative number means no
+	 *            inexact rename detection will be performed, only exact rename
+	 *            detection.
 	 */
 	public void setRenameLimit(int limit) {
 		renameLimit = limit;
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 84f0da9..aed76ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -1299,8 +1299,13 @@
 			return;
 		}
 
+		String name = f.getName();
+		if (name.length() > 200) {
+			name = name.substring(0, 200);
+		}
 		File tmpFile = File.createTempFile(
-				"._" + f.getName(), null, parentDir); //$NON-NLS-1$
+				"._" + name, null, parentDir); //$NON-NLS-1$
+
 		EolStreamType nonNullEolStreamType;
 		if (checkoutMetadata.eolStreamType != null) {
 			nonNullEolStreamType = checkoutMetadata.eolStreamType;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 94c8e43..ddc6add 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -60,6 +60,8 @@
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
+import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
+import org.eclipse.jgit.gitrepo.RepoProject.ReferenceFile;
 import org.eclipse.jgit.gitrepo.internal.RepoText;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Repository;
@@ -209,6 +211,15 @@
 						currentProject.getPath(),
 						attributes.getValue("src"), //$NON-NLS-1$
 						attributes.getValue("dest"))); //$NON-NLS-1$
+		} else if ("linkfile".equals(qName)) { //$NON-NLS-1$
+			if (currentProject == null) {
+				throw new SAXException(RepoText.get().invalidManifest);
+			}
+			currentProject.addLinkFile(new LinkFile(
+						rootRepo,
+						currentProject.getPath(),
+						attributes.getValue("src"), //$NON-NLS-1$
+						attributes.getValue("dest"))); //$NON-NLS-1$
 		} else if ("include".equals(qName)) { //$NON-NLS-1$
 			String name = attributes.getValue("name"); //$NON-NLS-1$
 			if (includedReader != null) {
@@ -359,19 +370,25 @@
 			else
 				last = p;
 		}
-		removeNestedCopyfiles();
+		removeNestedCopyAndLinkfiles();
 	}
 
-	/** Remove copyfiles that sit in a subdirectory of any other project. */
-	void removeNestedCopyfiles() {
+	private void removeNestedCopyAndLinkfiles() {
 		for (RepoProject proj : filteredProjects) {
 			List<CopyFile> copyfiles = new ArrayList<>(proj.getCopyFiles());
 			proj.clearCopyFiles();
 			for (CopyFile copyfile : copyfiles) {
-				if (!isNestedCopyfile(copyfile)) {
+				if (!isNestedReferencefile(copyfile)) {
 					proj.addCopyFile(copyfile);
 				}
 			}
+			List<LinkFile> linkfiles = new ArrayList<>(proj.getLinkFiles());
+			proj.clearLinkFiles();
+			for (LinkFile linkfile : linkfiles) {
+				if (!isNestedReferencefile(linkfile)) {
+					proj.addLinkFile(linkfile);
+				}
+			}
 		}
 	}
 
@@ -393,18 +410,18 @@
 		return false;
 	}
 
-	private boolean isNestedCopyfile(CopyFile copyfile) {
-		if (copyfile.dest.indexOf('/') == -1) {
-			// If the copyfile is at root level then it won't be nested.
+	private boolean isNestedReferencefile(ReferenceFile referencefile) {
+		if (referencefile.dest.indexOf('/') == -1) {
+			// If the referencefile is at root level then it won't be nested.
 			return false;
 		}
 		for (RepoProject proj : filteredProjects) {
-			if (proj.getPath().compareTo(copyfile.dest) > 0) {
+			if (proj.getPath().compareTo(referencefile.dest) > 0) {
 				// Early return as remaining projects can't be ancestor of this
-				// copyfile config (filteredProjects is sorted).
+				// referencefile config (filteredProjects is sorted).
 				return false;
 			}
-			if (proj.isAncestorOf(copyfile.dest)) {
+			if (proj.isAncestorOf(referencefile.dest)) {
 				return true;
 			}
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index e105dc0..1de8a0b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -49,11 +49,14 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.UnsupportedOperationException;
+import java.net.URI;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.StringJoiner;
 
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.Git;
@@ -67,6 +70,7 @@
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader;
 import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
+import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
 import org.eclipse.jgit.gitrepo.internal.RepoText;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.CommitBuilder;
@@ -106,7 +110,8 @@
  */
 public class RepoCommand extends GitCommand<RevCommit> {
 	private String manifestPath;
-	private String uri;
+	private String baseUri;
+	private URI targetUri;
 	private String groupsParam;
 	private String branch;
 	private String targetBranch = Constants.HEAD;
@@ -274,7 +279,25 @@
 	 * @return this command
 	 */
 	public RepoCommand setURI(String uri) {
-		this.uri = uri;
+		this.baseUri = uri;
+		return this;
+	}
+
+	/**
+	 * Set the URI of the superproject (this repository), so the .gitmodules
+	 * file can specify the submodule URLs relative to the superproject.
+	 *
+	 * @param uri
+	 *            the URI of the repository holding the superproject.
+	 * @return this command
+	 * @since 4.8
+	 */
+	public RepoCommand setTargetURI(String uri) {
+		// The repo name is interpreted as a directory, for example
+		// Gerrit (http://gerrit.googlesource.com/gerrit) has a
+		// .gitmodules referencing ../plugins/hooks, which is
+		// on http://gerrit.googlesource.com/plugins/hooks,
+		this.targetUri = URI.create(uri + "/"); //$NON-NLS-1$
 		return this;
 	}
 
@@ -452,9 +475,8 @@
 	public RevCommit call() throws GitAPIException {
 		try {
 			checkCallable();
-			if (uri == null || uri.length() == 0) {
-				throw new IllegalArgumentException(
-					JGitText.get().uriNotConfigured);
+			if (baseUri == null) {
+				baseUri = ""; //$NON-NLS-1$
 			}
 			if (inputStream == null) {
 				if (manifestPath == null || manifestPath.length() == 0)
@@ -478,7 +500,7 @@
 				git = new Git(repo);
 
 			ManifestParser parser = new ManifestParser(
-					includedReader, manifestPath, branch, uri, groupsParam, repo);
+					includedReader, manifestPath, branch, baseUri, groupsParam, repo);
 			try {
 				parser.read(inputStream);
 				for (RepoProject proj : parser.getFilteredProjects()) {
@@ -486,6 +508,7 @@
 							proj.getPath(),
 							proj.getRevision(),
 							proj.getCopyFiles(),
+							proj.getLinkFiles(),
 							proj.getGroups(),
 							proj.getRecommendShallow());
 				}
@@ -550,8 +573,13 @@
 						rec.append("\n"); //$NON-NLS-1$
 						attributes.append(rec.toString());
 					}
+
+					URI submodUrl = URI.create(nameUri);
+					if (targetUri != null) {
+						submodUrl = relativize(targetUri, submodUrl);
+					}
 					cfg.setString("submodule", path, "path", path); //$NON-NLS-1$ //$NON-NLS-2$
-					cfg.setString("submodule", path, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
+					cfg.setString("submodule", path, "url", submodUrl.toString()); //$NON-NLS-1$ //$NON-NLS-2$
 
 					// create gitlink
 					DirCacheEntry dcEntry = new DirCacheEntry(path);
@@ -568,6 +596,25 @@
 						dcEntry.setFileMode(FileMode.REGULAR_FILE);
 						builder.add(dcEntry);
 					}
+					for (LinkFile linkfile : proj.getLinkFiles()) {
+						String link;
+						if (linkfile.dest.contains("/")) { //$NON-NLS-1$
+							link = FileUtils.relativizeGitPath(
+									linkfile.dest.substring(0,
+											linkfile.dest.lastIndexOf('/')),
+									proj.getPath() + "/" + linkfile.src); //$NON-NLS-1$
+						} else {
+							link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$
+						}
+
+						objectId = inserter.insert(Constants.OBJ_BLOB,
+								link.getBytes(
+										Constants.CHARACTER_ENCODING));
+						dcEntry = new DirCacheEntry(linkfile.dest);
+						dcEntry.setObjectId(objectId);
+						dcEntry.setFileMode(FileMode.SYMLINK);
+						builder.add(dcEntry);
+					}
 				}
 				String content = cfg.toText();
 
@@ -642,13 +689,20 @@
 	}
 
 	private void addSubmodule(String url, String path, String revision,
-			List<CopyFile> copyfiles, Set<String> groups, String recommendShallow)
+			List<CopyFile> copyfiles, List<LinkFile> linkfiles,
+			Set<String> groups, String recommendShallow)
 			throws GitAPIException, IOException {
 		if (repo.isBare()) {
 			RepoProject proj = new RepoProject(url, path, revision, null, groups, recommendShallow);
 			proj.addCopyFiles(copyfiles);
+			proj.addLinkFiles(linkfiles);
 			bareProjects.add(proj);
 		} else {
+			if (!linkfiles.isEmpty()) {
+				throw new UnsupportedOperationException(
+						JGitText.get().nonBareLinkFilesNotSupported);
+			}
+
 			SubmoduleAddCommand add = git
 				.submoduleAdd()
 				.setPath(path)
@@ -672,6 +726,77 @@
 		}
 	}
 
+	/*
+	 * Assume we are document "a/b/index.html", what should we put in a href to get to "a/" ?
+	 * Returns the child if either base or child is not a bare path. This provides a missing feature in
+	 * java.net.URI (see http://bugs.java.com/view_bug.do?bug_id=6226081).
+	 */
+	private static final String SLASH = "/"; //$NON-NLS-1$
+	static URI relativize(URI current, URI target) {
+
+		// We only handle bare paths for now.
+		if (!target.toString().equals(target.getPath())) {
+			return target;
+		}
+		if (!current.toString().equals(current.getPath())) {
+			return target;
+		}
+
+		String cur = current.normalize().getPath();
+		String dest = target.normalize().getPath();
+
+		// TODO(hanwen): maybe (absolute, relative) should throw an exception.
+		if (cur.startsWith(SLASH) != dest.startsWith(SLASH)) {
+			return target;
+		}
+
+		while (cur.startsWith(SLASH)) {
+			cur = cur.substring(1);
+		}
+		while (dest.startsWith(SLASH)) {
+			dest = dest.substring(1);
+		}
+
+		if (cur.indexOf('/') == -1 || dest.indexOf('/') == -1) {
+			// Avoid having to special-casing in the next two ifs.
+			String prefix = "prefix/"; //$NON-NLS-1$
+			cur = prefix + cur;
+			dest = prefix + dest;
+		}
+
+		if (!cur.endsWith(SLASH)) {
+			// The current file doesn't matter.
+			int lastSlash = cur.lastIndexOf('/');
+			cur = cur.substring(0, lastSlash);
+		}
+		String destFile = ""; //$NON-NLS-1$
+		if (!dest.endsWith(SLASH)) {
+			// We always have to provide the destination file.
+			int lastSlash = dest.lastIndexOf('/');
+			destFile = dest.substring(lastSlash + 1, dest.length());
+			dest = dest.substring(0, dest.lastIndexOf('/'));
+		}
+
+		String[] cs = cur.split(SLASH);
+		String[] ds = dest.split(SLASH);
+
+		int common = 0;
+		while (common < cs.length && common < ds.length && cs[common].equals(ds[common])) {
+			common++;
+		}
+
+		StringJoiner j = new StringJoiner(SLASH);
+		for (int i = common; i < cs.length; i++) {
+			j.add(".."); //$NON-NLS-1$
+		}
+		for (int i = common; i < ds.length; i++) {
+			j.add(ds[i]);
+		}
+
+		j.add(destFile);
+		return URI.create(j.toString());
+	}
+
 	private static String findRef(String ref, Repository repo)
 			throws IOException {
 		if (!ObjectId.isId(ref)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index 700cf11..00cd38d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -70,14 +70,17 @@
 	private final String remote;
 	private final Set<String> groups;
 	private final List<CopyFile> copyfiles;
+	private final List<LinkFile> linkfiles;
 	private String recommendShallow;
 	private String url;
 	private String defaultRevision;
 
 	/**
-	 * The representation of a copy file configuration.
+	 * The representation of a reference file configuration.
+	 *
+	 * @since 4.8
 	 */
-	public static class CopyFile {
+	public static class ReferenceFile {
 		final Repository repo;
 		final String path;
 		final String src;
@@ -93,12 +96,31 @@
 		 * @param dest
 		 *            the destination path relative to the super project.
 		 */
-		public CopyFile(Repository repo, String path, String src, String dest) {
+		public ReferenceFile(Repository repo, String path, String src, String dest) {
 			this.repo = repo;
 			this.path = path;
 			this.src = src;
 			this.dest = dest;
 		}
+	}
+
+	/**
+	 * The representation of a copy file configuration.
+	 */
+	public static class CopyFile extends ReferenceFile {
+		/**
+		 * @param repo
+		 *            the super project.
+		 * @param path
+		 *            the path of the project containing this copyfile config.
+		 * @param src
+		 *            the source path relative to the sub repo.
+		 * @param dest
+		 *            the destination path relative to the super project.
+		 */
+		public CopyFile(Repository repo, String path, String src, String dest) {
+			super(repo, path, src, dest);
+		}
 
 		/**
 		 * Do the copy file action.
@@ -126,6 +148,27 @@
 	}
 
 	/**
+	 * The representation of a link file configuration.
+	 *
+	 * @since 4.8
+	 */
+	public static class LinkFile extends ReferenceFile {
+		/**
+		 * @param repo
+		 *            the super project.
+		 * @param path
+		 *            the path of the project containing this linkfile config.
+		 * @param src
+		 *            the source path relative to the sub repo.
+		 * @param dest
+		 *            the destination path relative to the super project.
+		 */
+		public LinkFile(Repository repo, String path, String src, String dest) {
+			super(repo, path, src, dest);
+		}
+	}
+
+	/**
 	 * @param name
 	 *            the relative path to the {@code remote}
 	 * @param path
@@ -156,6 +199,7 @@
 		this.groups = groups;
 		this.recommendShallow = recommendShallow;
 		copyfiles = new ArrayList<>();
+		linkfiles = new ArrayList<>();
 	}
 
 	/**
@@ -250,6 +294,16 @@
 	}
 
 	/**
+	 * Getter for the linkfile configurations.
+	 *
+	 * @return Immutable copy of {@code linkfiles}
+	 * @since 4.8
+	 */
+	public List<LinkFile> getLinkFiles() {
+		return Collections.unmodifiableList(linkfiles);
+	}
+
+	/**
 	 * Get the url of the sub repo.
 	 *
 	 * @return {@code url}
@@ -335,6 +389,35 @@
 		this.copyfiles.clear();
 	}
 
+	/**
+	 * Add a link file configuration.
+	 *
+	 * @param linkfile
+	 * @since 4.8
+	 */
+	public void addLinkFile(LinkFile linkfile) {
+		linkfiles.add(linkfile);
+	}
+
+	/**
+	 * Add a bunch of linkfile configurations.
+	 *
+	 * @param linkFiles
+	 * @since 4.8
+	 */
+	public void addLinkFiles(Collection<LinkFile> linkFiles) {
+		this.linkfiles.addAll(linkFiles);
+	}
+
+	/**
+	 * Clear all the linkfiles.
+	 *
+	 * @since 4.8
+	 */
+	public void clearLinkFiles() {
+		this.linkfiles.clear();
+	}
+
 	private String getPathWithSlash() {
 		if (path.endsWith("/")) //$NON-NLS-1$
 			return path;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index c1aca6a..62a6749 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -42,9 +42,12 @@
  */
 package org.eclipse.jgit.hooks;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
 import java.util.concurrent.Callable;
 
 import org.eclipse.jgit.api.errors.AbortedByHookException;
@@ -147,12 +150,19 @@
 	 */
 	protected void doRun() throws AbortedByHookException {
 		final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
-		final PrintStream hookErrRedirect = new PrintStream(errorByteArray);
+		PrintStream hookErrRedirect = null;
+		try {
+			hookErrRedirect = new PrintStream(errorByteArray, false,
+					UTF_8.name());
+		} catch (UnsupportedEncodingException e) {
+			// UTF-8 is guaranteed to be available
+		}
 		ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(),
 				getHookName(), getParameters(), getOutputStream(),
 				hookErrRedirect, getStdinArgs());
 		if (result.isExecutedWithError()) {
-			throw new AbortedByHookException(errorByteArray.toString(),
+			throw new AbortedByHookException(
+					new String(errorByteArray.toByteArray(), UTF_8),
 					getHookName(), result.getExitCode());
 		}
 	}
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 1bf55e3..aeda4a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -498,6 +498,7 @@
 	/***/ public String noHMACsupport;
 	/***/ public String noMergeBase;
 	/***/ public String noMergeHeadSpecified;
+	/***/ public String nonBareLinkFilesNotSupported;
 	/***/ public String noSuchRef;
 	/***/ public String notABoolean;
 	/***/ public String notABundle;
@@ -605,6 +606,7 @@
 	/***/ public String renamesRejoiningModifies;
 	/***/ public String repositoryAlreadyExists;
 	/***/ public String repositoryConfigFileInvalid;
+	/***/ public String repositoryIsRequired;
 	/***/ public String repositoryNotFound;
 	/***/ public String repositoryState_applyMailbox;
 	/***/ public String repositoryState_bare;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index ef0b80c..6fff656 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -322,6 +322,7 @@
 		HashEntry e1 = table.get(slot);
 		DfsBlock v = scan(e1, key, position);
 		if (v != null) {
+			ctx.stats.blockCacheHit++;
 			statHit.incrementAndGet();
 			return v;
 		}
@@ -334,6 +335,7 @@
 			if (e2 != e1) {
 				v = scan(e2, key, position);
 				if (v != null) {
+					ctx.stats.blockCacheHit++;
 					statHit.incrementAndGet();
 					creditSpace(blockSize);
 					return v;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index d380302..55f9cc2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -53,12 +53,12 @@
 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 static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.EnumSet;
 import java.util.GregorianCalendar;
 import java.util.HashSet;
@@ -112,7 +112,8 @@
 	private List<DfsPackFile> packsBefore;
 	private List<DfsPackFile> expiredGarbagePacks;
 
-	private Set<ObjectId> allHeads;
+	private Set<ObjectId> allHeadsAndTags;
+	private Set<ObjectId> allTags;
 	private Set<ObjectId> nonHeads;
 	private Set<ObjectId> txnHeads;
 	private Set<ObjectId> tagTargets;
@@ -234,7 +235,7 @@
 					JGitText.get().supportOnlyPackIndexVersion2);
 
 		startTimeMillis = SystemReader.getInstance().getCurrentTime();
-		ctx = (DfsReader) objdb.newReader();
+		ctx = objdb.newReader();
 		try {
 			refdb.refresh();
 			objdb.clearCache();
@@ -242,30 +243,36 @@
 			Collection<Ref> refsBefore = getAllRefs();
 			readPacksBefore();
 
-			if (packsBefore.isEmpty()) {
-				if (!expiredGarbagePacks.isEmpty()) {
-					objdb.commitPack(noPacks(), toPrune());
-				}
-				return true;
-			}
-
-			allHeads = new HashSet<>();
+			Set<ObjectId> allHeads = new HashSet<>();
+			allHeadsAndTags = new HashSet<>();
+			allTags = new HashSet<>();
 			nonHeads = new HashSet<>();
 			txnHeads = new HashSet<>();
 			tagTargets = new HashSet<>();
 			for (Ref ref : refsBefore) {
-				if (ref.isSymbolic() || ref.getObjectId() == null)
+				if (ref.isSymbolic() || ref.getObjectId() == null) {
 					continue;
-				if (isHead(ref) || isTag(ref))
+				}
+				if (isHead(ref)) {
 					allHeads.add(ref.getObjectId());
-				else if (RefTreeNames.isRefTree(refdb, ref.getName()))
+				} else if (isTag(ref)) {
+					allTags.add(ref.getObjectId());
+				} else if (RefTreeNames.isRefTree(refdb, ref.getName())) {
 					txnHeads.add(ref.getObjectId());
-				else
+				} else {
 					nonHeads.add(ref.getObjectId());
-				if (ref.getPeeledObjectId() != null)
+				}
+				if (ref.getPeeledObjectId() != null) {
 					tagTargets.add(ref.getPeeledObjectId());
+				}
 			}
-			tagTargets.addAll(allHeads);
+			// Don't exclude tags that are also branch tips.
+			allTags.removeAll(allHeads);
+			allHeadsAndTags.addAll(allHeads);
+			allHeadsAndTags.addAll(allTags);
+
+			// Hoist all branch tips and tags earlier in the pack file
+			tagTargets.addAll(allHeadsAndTags);
 
 			boolean rollback = true;
 			try {
@@ -307,13 +314,12 @@
 		packsBefore = new ArrayList<>(packs.length);
 		expiredGarbagePacks = new ArrayList<>(packs.length);
 
-		long mostRecentGC = mostRecentGC(packs);
 		long now = SystemReader.getInstance().getCurrentTime();
 		for (DfsPackFile p : packs) {
 			DfsPackDescription d = p.getPackDescription();
 			if (d.getPackSource() != UNREACHABLE_GARBAGE) {
 				packsBefore.add(p);
-			} else if (packIsExpiredGarbage(d, mostRecentGC, now)) {
+			} else if (packIsExpiredGarbage(d, now)) {
 				expiredGarbagePacks.add(p);
 			} else if (packIsCoalesceableGarbage(d, now)) {
 				packsBefore.add(p);
@@ -321,39 +327,13 @@
 		}
 	}
 
-	private static long mostRecentGC(DfsPackFile[] packs) {
-		long r = 0;
-		for (DfsPackFile p : packs) {
-			DfsPackDescription d = p.getPackDescription();
-			if (d.getPackSource() == GC || d.getPackSource() == GC_REST) {
-				r = Math.max(r, d.getLastModified());
-			}
-		}
-		return r;
-	}
-
-	private boolean packIsExpiredGarbage(DfsPackDescription d,
-			long mostRecentGC, long now) {
-		// It should be safe to remove an UNREACHABLE_GARBAGE pack if it:
-		//
-		// (a) Predates the most recent prior run of this class. This check
-		// ensures the graph traversal algorithm had a chance to consider
-		// all objects in this pack and copied them into a GC or GC_REST
-		// pack if the graph contained live edges to the objects.
-		//
-		// This check is safe because of the ordering of packing; the GC
-		// packs are written first and then the UNREACHABLE_GARBAGE is
-		// constructed. Any UNREACHABLE_GARBAGE dated earlier than the GC
-		// was input to the prior GC's graph traversal.
-		//
-		// (b) Is older than garbagePackTtl. This check gives concurrent
-		// inserter threads sufficient time to identify an object is not
-		// in the graph and should have a new copy written, rather than
-		// relying on something from an UNREACHABLE_GARBAGE pack.
-		//
-		// Both (a) and (b) must be met to safely remove UNREACHABLE_GARBAGE.
+	private boolean packIsExpiredGarbage(DfsPackDescription d, long now) {
+		// Consider the garbage pack as expired when it's older than
+		// garbagePackTtl. This check gives concurrent inserter threads
+		// sufficient time to identify an object is not in the graph and should
+		// have a new copy written, rather than relying on something from an
+		// UNREACHABLE_GARBAGE pack.
 		return d.getPackSource() == UNREACHABLE_GARBAGE
-				&& d.getLastModified() < mostRecentGC
 				&& garbageTtlMillis > 0
 				&& now - d.getLastModified() >= garbageTtlMillis;
 	}
@@ -448,12 +428,12 @@
 	}
 
 	private void packHeads(ProgressMonitor pm) throws IOException {
-		if (allHeads.isEmpty())
+		if (allHeadsAndTags.isEmpty())
 			return;
 
 		try (PackWriter pw = newPackWriter()) {
 			pw.setTagTargets(tagTargets);
-			pw.preparePack(pm, allHeads, PackWriter.NONE);
+			pw.preparePack(pm, allHeadsAndTags, NONE, NONE, allTags);
 			if (0 < pw.getObjectCount())
 				writePack(GC, pw, pm,
 						estimateGcPackSize(INSERT, RECEIVE, COMPACT, GC));
@@ -467,7 +447,7 @@
 		try (PackWriter pw = newPackWriter()) {
 			for (ObjectIdSet packedObjs : newPackObj)
 				pw.excludeObjects(packedObjs);
-			pw.preparePack(pm, nonHeads, allHeads);
+			pw.preparePack(pm, nonHeads, allHeadsAndTags);
 			if (0 < pw.getObjectCount())
 				writePack(GC_REST, pw, pm,
 						estimateGcPackSize(INSERT, RECEIVE, COMPACT, GC_REST));
@@ -481,7 +461,7 @@
 		try (PackWriter pw = newPackWriter()) {
 			for (ObjectIdSet packedObjs : newPackObj)
 				pw.excludeObjects(packedObjs);
-			pw.preparePack(pm, txnHeads, PackWriter.NONE);
+			pw.preparePack(pm, txnHeads, NONE);
 			if (0 < pw.getObjectCount())
 				writePack(GC_TXN, pw, pm, 0 /* unknown pack size */);
 		}
@@ -605,8 +585,4 @@
 		DfsBlockCache.getInstance().getOrCreate(pack, null);
 		return pack;
 	}
-
-	private static List<DfsPackDescription> noPacks() {
-		return Collections.emptyList();
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index fd72756..e65c9fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -530,7 +530,7 @@
 	}
 
 	private class Reader extends ObjectReader {
-		private final DfsReader ctx = new DfsReader(db);
+		private final DfsReader ctx = db.newReader();
 
 		@Override
 		public ObjectReader newReader() {
@@ -647,7 +647,7 @@
 
 		@Override
 		public ObjectStream openStream() throws IOException {
-			final DfsReader ctx = new DfsReader(db);
+			final DfsReader ctx = db.newReader();
 			if (srcPack != packKey) {
 				try {
 					// Post DfsInserter.flush() use the normal code path.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index b1cb72d..32ee6c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -170,7 +170,7 @@
 	}
 
 	@Override
-	public ObjectReader newReader() {
+	public DfsReader newReader() {
 		return new DfsReader(this);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 0a29ac5..f7c87a4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -201,7 +201,7 @@
 			pm = NullProgressMonitor.INSTANCE;
 
 		DfsObjDatabase objdb = repo.getObjectDatabase();
-		try (DfsReader ctx = (DfsReader) objdb.newReader()) {
+		try (DfsReader ctx = objdb.newReader()) {
 			PackConfig pc = new PackConfig(repo);
 			pc.setIndexVersion(2);
 			pc.setDeltaCompress(false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index f15d427..ae2e7e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -251,6 +251,8 @@
 
 			PackIndex idx;
 			try {
+				ctx.stats.readIdx++;
+				long start = System.nanoTime();
 				ReadableChannel rc = ctx.db.openFile(packDesc, INDEX);
 				try {
 					InputStream in = Channels.newInputStream(rc);
@@ -260,10 +262,11 @@
 						bs = (wantSize / bs) * bs;
 					else if (bs <= 0)
 						bs = wantSize;
-					in = new BufferedInputStream(in, bs);
-					idx = PackIndex.read(in);
+					idx = PackIndex.read(new BufferedInputStream(in, bs));
+					ctx.stats.readIdxBytes += rc.position();
 				} finally {
 					rc.close();
+					ctx.stats.readIdxMicros += elapsedMicros(start);
 				}
 			} catch (EOFException e) {
 				invalid = true;
@@ -286,6 +289,10 @@
 		}
 	}
 
+	private static long elapsedMicros(long start) {
+		return (System.nanoTime() - start) / 1000L;
+	}
+
 	final boolean isGarbage() {
 		return packDesc.getPackSource() == UNREACHABLE_GARBAGE;
 	}
@@ -314,6 +321,8 @@
 			long size;
 			PackBitmapIndex idx;
 			try {
+				ctx.stats.readBitmap++;
+				long start = System.nanoTime();
 				ReadableChannel rc = ctx.db.openFile(packDesc, BITMAP_INDEX);
 				try {
 					InputStream in = Channels.newInputStream(rc);
@@ -329,6 +338,8 @@
 				} finally {
 					size = rc.position();
 					rc.close();
+					ctx.stats.readIdxBytes += size;
+					ctx.stats.readIdxMicros += elapsedMicros(start);
 				}
 			} catch (EOFException e) {
 				IOException e2 = new IOException(MessageFormat.format(
@@ -777,6 +788,8 @@
 		if (invalid)
 			throw new PackInvalidException(getPackName());
 
+		ctx.stats.readBlock++;
+		long start = System.nanoTime();
 		ReadableChannel rc = ctx.db.openFile(packDesc, PACK);
 		try {
 			int size = blockSize(rc);
@@ -803,6 +816,7 @@
 			byte[] buf = new byte[size];
 			rc.position(pos);
 			int cnt = read(rc, ByteBuffer.wrap(buf, 0, size));
+			ctx.stats.readBlockBytes += cnt;
 			if (cnt != size) {
 				if (0 <= len) {
 					throw new EOFException(MessageFormat.format(
@@ -824,10 +838,10 @@
 				length = len = rc.size();
 			}
 
-			DfsBlock v = new DfsBlock(key, pos, buf);
-			return v;
+			return new DfsBlock(key, pos, buf);
 		} finally {
 			rc.close();
+			ctx.stats.readBlockMicros += elapsedMicros(start);
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index 755b163..d611469 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -95,7 +95,7 @@
  * See the base {@link ObjectReader} documentation for details. Notably, a
  * reader is not thread safe.
  */
-public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
+public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
 	private static final int MAX_RESOLVE_MATCHES = 256;
 
 	/** Temporary buffer large enough for at least one raw object id. */
@@ -104,17 +104,21 @@
 	/** Database this reader loads objects from. */
 	final DfsObjDatabase db;
 
+	final DfsReaderIoStats.Accumulator stats = new DfsReaderIoStats.Accumulator();
+
 	private Inflater inf;
-
 	private DfsBlock block;
-
 	private DeltaBaseCache baseCache;
-
 	private DfsPackFile last;
-
 	private boolean avoidUnreachable;
 
-	DfsReader(DfsObjDatabase db) {
+	/**
+	 * Initialize a new DfsReader
+	 *
+	 * @param db
+	 *            parent DfsObjDatabase.
+	 */
+	protected DfsReader(DfsObjDatabase db) {
 		this.db = db;
 		this.streamFileThreshold = db.getReaderOptions().getStreamFileThreshold();
 	}
@@ -131,7 +135,7 @@
 
 	@Override
 	public ObjectReader newReader() {
-		return new DfsReader(db);
+		return db.newReader();
 	}
 
 	@Override
@@ -170,6 +174,7 @@
 		PackList packList = db.getPackList();
 		resolveImpl(packList, id, matches);
 		if (matches.size() < MAX_RESOLVE_MATCHES && packList.dirty()) {
+			stats.scanPacks++;
 			resolveImpl(db.scanPacks(packList), id, matches);
 		}
 		return matches;
@@ -198,6 +203,7 @@
 		if (hasImpl(packList, objectId)) {
 			return true;
 		} else if (packList.dirty()) {
+			stats.scanPacks++;
 			return hasImpl(db.scanPacks(packList), objectId);
 		}
 		return false;
@@ -234,6 +240,7 @@
 			return checkType(ldr, objectId, typeHint);
 		}
 		if (packList.dirty()) {
+			stats.scanPacks++;
 			ldr = openImpl(db.scanPacks(packList), objectId);
 			if (ldr != null) {
 				return checkType(ldr, objectId, typeHint);
@@ -316,6 +323,7 @@
 		List<FoundObject<T>> r = new ArrayList<>();
 		findAllImpl(packList, pending, r);
 		if (!pending.isEmpty() && packList.dirty()) {
+			stats.scanPacks++;
 			findAllImpl(db.scanPacks(packList), pending, r);
 		}
 		for (T t : pending) {
@@ -452,7 +460,6 @@
 		final IOException findAllError = error;
 		return new AsyncObjectSizeQueue<T>() {
 			private FoundObject<T> cur;
-
 			private long sz;
 
 			@Override
@@ -718,9 +725,10 @@
 		for (int dstoff = 0;;) {
 			int n = inf.inflate(dstbuf, dstoff, dstbuf.length - dstoff);
 			dstoff += n;
-			if (inf.finished() || (headerOnly && dstoff == dstbuf.length))
+			if (inf.finished() || (headerOnly && dstoff == dstbuf.length)) {
+				stats.inflatedBytes += dstoff;
 				return dstoff;
-			if (inf.needsInput()) {
+			} else if (inf.needsInput()) {
 				pin(pack, position);
 				position += block.setInput(position, inf);
 			} else if (n == 0)
@@ -764,6 +772,11 @@
 		block = null;
 	}
 
+	/** @return IO statistics accumulated by this reader. */
+	public DfsReaderIoStats getIoStats() {
+		return new DfsReaderIoStats(stats);
+	}
+
 	/** Release the current window cursor. */
 	@Override
 	public void close() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
new file mode 100644
index 0000000..9a174c8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017, 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.dfs;
+
+/** IO statistics for a {@link DfsReader}. */
+public class DfsReaderIoStats {
+	/** POJO to accumulate IO statistics. */
+	public static class Accumulator {
+		/** Number of times the reader explicitly called scanPacks. */
+		long scanPacks;
+
+		/** Total number of complete pack indexes read into memory. */
+		long readIdx;
+
+		/** Total number of complete bitmap indexes read into memory. */
+		long readBitmap;
+
+		/** Total number of bytes read from indexes. */
+		long readIdxBytes;
+
+		/** Total microseconds spent reading pack or bitmap indexes. */
+		long readIdxMicros;
+
+		/** Total number of block cache hits. */
+		long blockCacheHit;
+
+		/** Total number of discrete blocks read from pack file(s). */
+		long readBlock;
+
+		/** Total number of compressed bytes read as block sized units. */
+		long readBlockBytes;
+
+		/** Total microseconds spent reading {@link #readBlock} blocks. */
+		long readBlockMicros;
+
+		/** Total number of bytes decompressed. */
+		long inflatedBytes;
+
+		Accumulator() {
+		}
+	}
+
+	private final Accumulator stats;
+
+	DfsReaderIoStats(Accumulator stats) {
+		this.stats = stats;
+	}
+
+	/** @return number of times the reader explicitly called scanPacks. */
+	public long getScanPacks() {
+		return stats.scanPacks;
+	}
+
+	/** @return total number of complete pack indexes read into memory. */
+	public long getReadPackIndexCount() {
+		return stats.readIdx;
+	}
+
+	/** @return total number of complete bitmap indexes read into memory. */
+	public long getReadBitmapIndexCount() {
+		return stats.readBitmap;
+	}
+
+	/** @return total number of bytes read from indexes. */
+	public long getReadIndexBytes() {
+		return stats.readIdxBytes;
+	}
+
+	/** @return total microseconds spent reading pack or bitmap indexes. */
+	public long getReadIndexMicros() {
+		return stats.readIdxMicros;
+	}
+
+	/** @return total number of block cache hits. */
+	public long getBlockCacheHits() {
+		return stats.blockCacheHit;
+	}
+
+	/** @return total number of discrete blocks read from pack file(s). */
+	public long getReadBlocksCount() {
+		return stats.readBlock;
+	}
+
+	/** @return total number of compressed bytes read as block sized units. */
+	public long getReadBlocksBytes() {
+		return stats.readBlockBytes;
+	}
+
+	/** @return total microseconds spent reading blocks. */
+	public long getReadBlocksMicros() {
+		return stats.readBlockMicros;
+	}
+
+	/** @return total number of bytes decompressed. */
+	public long getInflatedBytes() {
+		return stats.inflatedBytes;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
index 6d40a75..73a93e6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
@@ -99,7 +99,7 @@
 
 	@Override
 	public ObjectStream openStream() throws MissingObjectException, IOException {
-		DfsReader ctx = new DfsReader(db);
+		DfsReader ctx = db.newReader();
 		InputStream in;
 		try {
 			in = new PackInputStream(pack, objectOffset + headerLength, ctx);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index d47b304..ae8260a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -47,8 +47,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Set;
 
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -160,43 +162,70 @@
 		return alts;
 	}
 
+	private Set<AlternateHandle.Id> skipMe(Set<AlternateHandle.Id> skips) {
+		Set<AlternateHandle.Id> withMe = new HashSet<>();
+		if (skips != null) {
+			withMe.addAll(skips);
+		}
+		withMe.add(getAlternateId());
+		return withMe;
+	}
+
 	@Override
 	void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
 			throws IOException {
-		// In theory we could accelerate the loose object scan using our
-		// unpackedObjects map, but its not worth the huge code complexity.
-		// Scanning a single loose directory is fast enough, and this is
-		// unlikely to be called anyway.
-		//
 		wrapped.resolve(matches, id);
 	}
 
 	@Override
 	public boolean has(final AnyObjectId objectId) throws IOException {
-		if (unpackedObjects.contains(objectId))
+		return has(objectId, null);
+	}
+
+	private boolean has(final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+			throws IOException {
+		if (unpackedObjects.contains(objectId)) {
 			return true;
-		if (wrapped.hasPackedObject(objectId))
+		}
+		if (wrapped.hasPackedObject(objectId)) {
 			return true;
+		}
+		skips = skipMe(skips);
 		for (CachedObjectDirectory alt : myAlternates()) {
-			if (alt.has(objectId))
-				return true;
+			if (!skips.contains(alt.getAlternateId())) {
+				if (alt.has(objectId, skips)) {
+					return true;
+				}
+			}
 		}
 		return false;
 	}
 
 	@Override
-	ObjectLoader openObject(final WindowCursor curs,
-			final AnyObjectId objectId) throws IOException {
+	ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId)
+			throws IOException {
+		return openObject(curs, objectId, null);
+	}
+
+	private ObjectLoader openObject(final WindowCursor curs,
+			final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+			throws IOException {
 		ObjectLoader ldr = openLooseObject(curs, objectId);
-		if (ldr != null)
+		if (ldr != null) {
 			return ldr;
+		}
 		ldr = wrapped.openPackedObject(curs, objectId);
-		if (ldr != null)
+		if (ldr != null) {
 			return ldr;
+		}
+		skips = skipMe(skips);
 		for (CachedObjectDirectory alt : myAlternates()) {
-			ldr = alt.openObject(curs, objectId);
-			if (ldr != null)
-				return ldr;
+			if (!skips.contains(alt.getAlternateId())) {
+				ldr = alt.openObject(curs, objectId, skips);
+				if (ldr != null) {
+					return ldr;
+				}
+			}
 		}
 		return null;
 	}
@@ -260,4 +289,8 @@
 			super(id);
 		}
 	}
+
+	private AlternateHandle.Id getAlternateId() {
+		return wrapped.getAlternateId();
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index ef9aa37..6a674aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -490,10 +490,28 @@
 	 */
 	@Override
 	public Set<ObjectId> getAdditionalHaves() {
+		return getAdditionalHaves(null);
+	}
+
+	/**
+	 * Objects known to exist but not expressed by {@link #getAllRefs()}.
+	 * <p>
+	 * When a repository borrows objects from another repository, it can
+	 * advertise that it safely has that other repository's references, without
+	 * exposing any other details about the other repository.  This may help
+	 * a client trying to push changes avoid pushing more than it needs to.
+	 *
+	 * @param skips
+	 *            Set of AlternateHandle Ids already seen
+	 *
+	 * @return unmodifiable collection of other known objects.
+	 */
+	private Set<ObjectId> getAdditionalHaves(Set<AlternateHandle.Id> skips) {
 		HashSet<ObjectId> r = new HashSet<>();
+		skips = objectDatabase.addMe(skips);
 		for (AlternateHandle d : objectDatabase.myAlternates()) {
-			if (d instanceof AlternateRepository) {
-				Repository repo;
+			if (d instanceof AlternateRepository && !skips.contains(d.getId())) {
+				FileRepository repo;
 
 				repo = ((AlternateRepository) d).repository;
 				for (Ref ref : repo.getAllRefs().values()) {
@@ -502,7 +520,7 @@
 					if (ref.getPeeledObjectId() != null)
 						r.add(ref.getPeeledObjectId());
 				}
-				r.addAll(repo.getAdditionalHaves());
+				r.addAll(repo.getAdditionalHaves(skips));
 			}
 		}
 		return r;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index a4a2baa..7ff209f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -80,7 +80,6 @@
 import java.util.TreeMap;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -107,6 +106,7 @@
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.lib.internal.WorkQueue;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.ReflogReader;
@@ -151,7 +151,19 @@
 
 	private static final int DEFAULT_AUTOLIMIT = 6700;
 
-	private static ExecutorService executor = Executors.newFixedThreadPool(1);
+	private static volatile ExecutorService executor;
+
+	/**
+	 * Set the executor for running auto-gc in the background. If no executor is
+	 * set JGit's own WorkQueue will be used.
+	 *
+	 * @param e
+	 *            the executor to be used for running auto-gc
+	 * @since 4.8
+	 */
+	public static void setExecutor(ExecutorService e) {
+		executor = e;
+	}
 
 	private final FileRepository repo;
 
@@ -265,10 +277,14 @@
 			return Collections.emptyList();
 		};
 		// TODO(ms): in 5.0 change signature and return the Future
-		executor.submit(gcTask);
+		executor().submit(gcTask);
 		return Collections.emptyList();
 	}
 
+	private ExecutorService executor() {
+		return (executor != null) ? executor : WorkQueue.getExecutor();
+	}
+
 	private Collection<PackFile> doGc() throws IOException, ParseException {
 		if (automatic && !needGc()) {
 			return Collections.emptyList();
@@ -790,7 +806,9 @@
 		long time = System.currentTimeMillis();
 		Collection<Ref> refsBefore = getAllRefs();
 
+		Set<ObjectId> allHeadsAndTags = new HashSet<>();
 		Set<ObjectId> allHeads = new HashSet<>();
+		Set<ObjectId> allTags = new HashSet<>();
 		Set<ObjectId> nonHeads = new HashSet<>();
 		Set<ObjectId> txnHeads = new HashSet<>();
 		Set<ObjectId> tagTargets = new HashSet<>();
@@ -800,16 +818,21 @@
 		for (Ref ref : refsBefore) {
 			checkCancelled();
 			nonHeads.addAll(listRefLogObjects(ref, 0));
-			if (ref.isSymbolic() || ref.getObjectId() == null)
+			if (ref.isSymbolic() || ref.getObjectId() == null) {
 				continue;
-			if (isHead(ref) || isTag(ref))
+			}
+			if (isHead(ref)) {
 				allHeads.add(ref.getObjectId());
-			else if (RefTreeNames.isRefTree(refdb, ref.getName()))
+			} else if (isTag(ref)) {
+				allTags.add(ref.getObjectId());
+			} else if (RefTreeNames.isRefTree(refdb, ref.getName())) {
 				txnHeads.add(ref.getObjectId());
-			else
+			} else {
 				nonHeads.add(ref.getObjectId());
-			if (ref.getPeeledObjectId() != null)
+			}
+			if (ref.getPeeledObjectId() != null) {
 				tagTargets.add(ref.getPeeledObjectId());
+			}
 		}
 
 		List<ObjectIdSet> excluded = new LinkedList<>();
@@ -819,13 +842,19 @@
 				excluded.add(f.getIndex());
 		}
 
-		tagTargets.addAll(allHeads);
+		// Don't exclude tags that are also branch tips
+		allTags.removeAll(allHeads);
+		allHeadsAndTags.addAll(allHeads);
+		allHeadsAndTags.addAll(allTags);
+
+		// Hoist all branch tips and tags earlier in the pack file
+		tagTargets.addAll(allHeadsAndTags);
 		nonHeads.addAll(indexObjects);
 
 		List<PackFile> ret = new ArrayList<>(2);
 		PackFile heads = null;
-		if (!allHeads.isEmpty()) {
-			heads = writePack(allHeads, Collections.<ObjectId> emptySet(),
+		if (!allHeadsAndTags.isEmpty()) {
+			heads = writePack(allHeadsAndTags, PackWriter.NONE, allTags,
 					tagTargets, excluded);
 			if (heads != null) {
 				ret.add(heads);
@@ -833,12 +862,14 @@
 			}
 		}
 		if (!nonHeads.isEmpty()) {
-			PackFile rest = writePack(nonHeads, allHeads, tagTargets, excluded);
+			PackFile rest = writePack(nonHeads, allHeadsAndTags, PackWriter.NONE,
+					tagTargets, excluded);
 			if (rest != null)
 				ret.add(rest);
 		}
 		if (!txnHeads.isEmpty()) {
-			PackFile txn = writePack(txnHeads, PackWriter.NONE, null, excluded);
+			PackFile txn = writePack(txnHeads, PackWriter.NONE, PackWriter.NONE,
+					null, excluded);
 			if (txn != null)
 				ret.add(txn);
 		}
@@ -1074,8 +1105,9 @@
 	}
 
 	private PackFile writePack(@NonNull Set<? extends ObjectId> want,
-			@NonNull Set<? extends ObjectId> have, Set<ObjectId> tagTargets,
-			List<ObjectIdSet> excludeObjects) throws IOException {
+			@NonNull Set<? extends ObjectId> have, @NonNull Set<ObjectId> tags,
+			Set<ObjectId> tagTargets, List<ObjectIdSet> excludeObjects)
+			throws IOException {
 		checkCancelled();
 		File tmpPack = null;
 		Map<PackExt, File> tmpExts = new TreeMap<>(
@@ -1101,12 +1133,13 @@
 			// prepare the PackWriter
 			pw.setDeltaBaseAsOffset(true);
 			pw.setReuseDeltaCommits(false);
-			if (tagTargets != null)
+			if (tagTargets != null) {
 				pw.setTagTargets(tagTargets);
+			}
 			if (excludeObjects != null)
 				for (ObjectIdSet idx : excludeObjects)
 					pw.excludeObjects(idx);
-			pw.preparePack(pm, want, have);
+			pw.preparePack(pm, want, have, PackWriter.NONE, tags);
 			if (pw.getObjectCount() == 0)
 				return null;
 			checkCancelled();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index e1934a1..6ae559a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -64,6 +64,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -117,6 +118,8 @@
 	/** Maximum number of candidates offered as resolutions of abbreviation. */
 	private static final int RESOLVE_ABBREV_LIMIT = 256;
 
+	private final AlternateHandle handle = new AlternateHandle(this);
+
 	private final Config config;
 
 	private final File objects;
@@ -294,26 +297,38 @@
 	@Override
 	public boolean has(AnyObjectId objectId) {
 		return unpackedObjectCache.isUnpacked(objectId)
-				|| hasPackedInSelfOrAlternate(objectId)
-				|| hasLooseInSelfOrAlternate(objectId);
+				|| hasPackedInSelfOrAlternate(objectId, null)
+				|| hasLooseInSelfOrAlternate(objectId, null);
 	}
 
-	private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId) {
-		if (hasPackedObject(objectId))
+	private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId,
+			Set<AlternateHandle.Id> skips) {
+		if (hasPackedObject(objectId)) {
 			return true;
+		}
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			if (alt.db.hasPackedInSelfOrAlternate(objectId))
-				return true;
+			if (!skips.contains(alt.getId())) {
+				if (alt.db.hasPackedInSelfOrAlternate(objectId, skips)) {
+					return true;
+				}
+			}
 		}
 		return false;
 	}
 
-	private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId) {
-		if (fileFor(objectId).exists())
+	private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId,
+			Set<AlternateHandle.Id> skips) {
+		if (fileFor(objectId).exists()) {
 			return true;
+		}
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			if (alt.db.hasLooseInSelfOrAlternate(objectId))
-				return true;
+			if (!skips.contains(alt.getId())) {
+				if (alt.db.hasLooseInSelfOrAlternate(objectId, skips)) {
+					return true;
+				}
+			}
 		}
 		return false;
 	}
@@ -340,6 +355,12 @@
 	@Override
 	void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
 			throws IOException {
+		resolve(matches, id, null);
+	}
+
+	private void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+			Set<AlternateHandle.Id> skips)
+			throws IOException {
 		// Go through the packs once. If we didn't find any resolutions
 		// scan for new packs and check once more.
 		int oldSize = matches.size();
@@ -376,10 +397,14 @@
 			}
 		}
 
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			alt.db.resolve(matches, id);
-			if (matches.size() > RESOLVE_ABBREV_LIMIT)
-				return;
+			if (!skips.contains(alt.getId())) {
+				alt.db.resolve(matches, id, skips);
+				if (matches.size() > RESOLVE_ABBREV_LIMIT) {
+					return;
+				}
+			}
 		}
 	}
 
@@ -388,37 +413,50 @@
 			throws IOException {
 		if (unpackedObjectCache.isUnpacked(objectId)) {
 			ObjectLoader ldr = openLooseObject(curs, objectId);
-			if (ldr != null)
+			if (ldr != null) {
 				return ldr;
+			}
 		}
-		ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId);
-		if (ldr != null)
+		ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId, null);
+		if (ldr != null) {
 			return ldr;
-		return openLooseFromSelfOrAlternate(curs, objectId);
+		}
+		return openLooseFromSelfOrAlternate(curs, objectId, null);
 	}
 
 	private ObjectLoader openPackedFromSelfOrAlternate(WindowCursor curs,
-			AnyObjectId objectId) {
+			AnyObjectId objectId, Set<AlternateHandle.Id> skips) {
 		ObjectLoader ldr = openPackedObject(curs, objectId);
-		if (ldr != null)
+		if (ldr != null) {
 			return ldr;
+		}
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId);
-			if (ldr != null)
-				return ldr;
+			if (!skips.contains(alt.getId())) {
+				ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId, skips);
+				if (ldr != null) {
+					return ldr;
+				}
+			}
 		}
 		return null;
 	}
 
 	private ObjectLoader openLooseFromSelfOrAlternate(WindowCursor curs,
-			AnyObjectId objectId) throws IOException {
+			AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+					throws IOException {
 		ObjectLoader ldr = openLooseObject(curs, objectId);
-		if (ldr != null)
+		if (ldr != null) {
 			return ldr;
+		}
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId);
-			if (ldr != null)
-				return ldr;
+			if (!skips.contains(alt.getId())) {
+				ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId, skips);
+				if (ldr != null) {
+					return ldr;
+				}
+			}
 		}
 		return null;
 	}
@@ -469,37 +507,49 @@
 			throws IOException {
 		if (unpackedObjectCache.isUnpacked(id)) {
 			long len = getLooseObjectSize(curs, id);
-			if (0 <= len)
+			if (0 <= len) {
 				return len;
+			}
 		}
-		long len = getPackedSizeFromSelfOrAlternate(curs, id);
-		if (0 <= len)
+		long len = getPackedSizeFromSelfOrAlternate(curs, id, null);
+		if (0 <= len) {
 			return len;
-		return getLooseSizeFromSelfOrAlternate(curs, id);
+		}
+		return getLooseSizeFromSelfOrAlternate(curs, id, null);
 	}
 
 	private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
-			AnyObjectId id) {
+			AnyObjectId id, Set<AlternateHandle.Id> skips) {
 		long len = getPackedObjectSize(curs, id);
-		if (0 <= len)
+		if (0 <= len) {
 			return len;
+		}
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id);
-			if (0 <= len)
-				return len;
+			if (!skips.contains(alt.getId())) {
+				len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id, skips);
+				if (0 <= len) {
+					return len;
+				}
+			}
 		}
 		return -1;
 	}
 
 	private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
-			AnyObjectId id) throws IOException {
+			AnyObjectId id, Set<AlternateHandle.Id> skips) throws IOException {
 		long len = getLooseObjectSize(curs, id);
-		if (0 <= len)
+		if (0 <= len) {
 			return len;
+		}
+		skips = addMe(skips);
 		for (AlternateHandle alt : myAlternates()) {
-			len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id);
-			if (0 <= len)
-				return len;
+			if (!skips.contains(alt.getId())) {
+				len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id, skips);
+				if (0 <= len) {
+					return len;
+				}
+			}
 		}
 		return -1;
 	}
@@ -546,7 +596,12 @@
 
 	@Override
 	void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
-			WindowCursor curs) throws IOException {
+																	WindowCursor curs) throws IOException {
+		selectObjectRepresentation(packer, otp, curs, null);
+	}
+
+	private void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
+			WindowCursor curs, Set<AlternateHandle.Id> skips) throws IOException {
 		PackList pList = packList.get();
 		SEARCH: for (;;) {
 			for (final PackFile p : pList.packs) {
@@ -567,8 +622,12 @@
 			break SEARCH;
 		}
 
-		for (AlternateHandle h : myAlternates())
-			h.db.selectObjectRepresentation(packer, otp, curs);
+		skips = addMe(skips);
+		for (AlternateHandle h : myAlternates()) {
+			if (!skips.contains(h.getId())) {
+				h.db.selectObjectRepresentation(packer, otp, curs, skips);
+			}
+		}
 	}
 
 	private void handlePackError(IOException e, PackFile p) {
@@ -930,6 +989,14 @@
 		return alt;
 	}
 
+	Set<AlternateHandle.Id> addMe(Set<AlternateHandle.Id> skips) {
+		if (skips == null) {
+			skips = new HashSet<>();
+		}
+		skips.add(handle.getId());
+		return skips;
+	}
+
 	private AlternateHandle[] loadAlternates() throws IOException {
 		final List<AlternateHandle> l = new ArrayList<>(4);
 		final BufferedReader br = open(alternatesFile);
@@ -996,6 +1063,38 @@
 	}
 
 	static class AlternateHandle {
+		static class Id {
+			String alternateId;
+
+			public Id(File object) {
+				try {
+					this.alternateId = object.getCanonicalPath();
+				} catch (Exception e) {
+					alternateId = null;
+				}
+			}
+
+			@Override
+			public boolean equals(Object o) {
+				if (o == this) {
+					return true;
+				}
+				if (o == null || !(o instanceof Id)) {
+					return false;
+				}
+				Id aId = (Id) o;
+				return Objects.equals(alternateId, aId.alternateId);
+			}
+
+			@Override
+			public int hashCode() {
+				if (alternateId == null) {
+					return 1;
+				}
+				return alternateId.hashCode();
+			}
+		}
+
 		final ObjectDirectory db;
 
 		AlternateHandle(ObjectDirectory db) {
@@ -1005,6 +1104,10 @@
 		void close() {
 			db.close();
 		}
+
+		public Id getId(){
+			return db.getAlternateId();
+		}
 	}
 
 	static class AlternateRepository extends AlternateHandle {
@@ -1029,4 +1132,8 @@
 	CachedObjectDirectory newCachedFileObjectDatabase() {
 		return new CachedObjectDirectory(this);
 	}
+
+	AlternateHandle.Id getAlternateId() {
+		return new AlternateHandle.Id(objects);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 93dbee3..7271560 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -233,7 +233,9 @@
 
 	private List<CachedPack> cachedPacks = new ArrayList<>(2);
 
-	private Set<ObjectId> tagTargets = Collections.emptySet();
+	private Set<ObjectId> tagTargets = NONE;
+
+	private Set<? extends ObjectId> excludeFromBitmapSelection = NONE;
 
 	private ObjectIdSet[] excludeInPacks;
 
@@ -712,8 +714,7 @@
 	public void preparePack(ProgressMonitor countingMonitor,
 			@NonNull Set<? extends ObjectId> want,
 			@NonNull Set<? extends ObjectId> have) throws IOException {
-		preparePack(countingMonitor,
-				want, have, Collections.<ObjectId> emptySet());
+		preparePack(countingMonitor, want, have, NONE, NONE);
 	}
 
 	/**
@@ -721,9 +722,9 @@
 	 * <p>
 	 * Like {@link #preparePack(ProgressMonitor, Set, Set)} but also allows
 	 * specifying commits that should not be walked past ("shallow" commits).
-	 * The caller is responsible for filtering out commits that should not
-	 * be shallow any more ("unshallow" commits as in {@link #setShallowPack})
-	 * from the shallow set.
+	 * The caller is responsible for filtering out commits that should not be
+	 * shallow any more ("unshallow" commits as in {@link #setShallowPack}) from
+	 * the shallow set.
 	 *
 	 * @param countingMonitor
 	 *            progress during object enumeration.
@@ -731,27 +732,67 @@
 	 *            objects of interest, ancestors of which will be included in
 	 *            the pack. Must not be {@code null}.
 	 * @param have
-	 *            objects whose ancestors (up to and including
-	 *            {@code shallow} commits) do not need to be included in the
-	 *            pack because they are already available from elsewhere.
-	 *            Must not be {@code null}.
+	 *            objects whose ancestors (up to and including {@code shallow}
+	 *            commits) do not need to be included in the pack because they
+	 *            are already available from elsewhere. Must not be
+	 *            {@code null}.
 	 * @param shallow
 	 *            commits indicating the boundary of the history marked with
-	 *            {@code have}. Shallow commits have parents but those
-	 *            parents are considered not to be already available.
-	 *            Parents of {@code shallow} commits and earlier generations
-	 *            will be included in the pack if requested by {@code want}.
-	 *            Must not be {@code null}.
+	 *            {@code have}. Shallow commits have parents but those parents
+	 *            are considered not to be already available. Parents of
+	 *            {@code shallow} commits and earlier generations will be
+	 *            included in the pack if requested by {@code want}. Must not be
+	 *            {@code null}.
 	 * @throws IOException
-	 *            an I/O problem occured while reading objects.
+	 *             an I/O problem occurred while reading objects.
 	 */
 	public void preparePack(ProgressMonitor countingMonitor,
 			@NonNull Set<? extends ObjectId> want,
 			@NonNull Set<? extends ObjectId> have,
 			@NonNull Set<? extends ObjectId> shallow) throws IOException {
+		preparePack(countingMonitor, want, have, shallow, NONE);
+	}
+
+	/**
+	 * Prepare the list of objects to be written to the pack stream.
+	 * <p>
+	 * Like {@link #preparePack(ProgressMonitor, Set, Set)} but also allows
+	 * specifying commits that should not be walked past ("shallow" commits).
+	 * The caller is responsible for filtering out commits that should not be
+	 * shallow any more ("unshallow" commits as in {@link #setShallowPack}) from
+	 * the shallow set.
+	 *
+	 * @param countingMonitor
+	 *            progress during object enumeration.
+	 * @param want
+	 *            objects of interest, ancestors of which will be included in
+	 *            the pack. Must not be {@code null}.
+	 * @param have
+	 *            objects whose ancestors (up to and including {@code shallow}
+	 *            commits) do not need to be included in the pack because they
+	 *            are already available from elsewhere. Must not be
+	 *            {@code null}.
+	 * @param shallow
+	 *            commits indicating the boundary of the history marked with
+	 *            {@code have}. Shallow commits have parents but those parents
+	 *            are considered not to be already available. Parents of
+	 *            {@code shallow} commits and earlier generations will be
+	 *            included in the pack if requested by {@code want}. Must not be
+	 *            {@code null}.
+	 * @param noBitmaps
+	 *            collection of objects to be excluded from bitmap commit
+	 *            selection.
+	 * @throws IOException
+	 *             an I/O problem occurred while reading objects.
+	 */
+	public void preparePack(ProgressMonitor countingMonitor,
+			@NonNull Set<? extends ObjectId> want,
+			@NonNull Set<? extends ObjectId> have,
+			@NonNull Set<? extends ObjectId> shallow,
+			@NonNull Set<? extends ObjectId> noBitmaps) throws IOException {
 		try (ObjectWalk ow = getObjectWalk()) {
 			ow.assumeShallow(shallow);
-			preparePack(countingMonitor, ow, want, have);
+			preparePack(countingMonitor, ow, want, have, noBitmaps);
 		}
 	}
 
@@ -784,13 +825,17 @@
 	 *            points of graph traversal). Pass {@link #NONE} if all objects
 	 *            reachable from {@code want} are desired, such as when serving
 	 *            a clone.
+	 * @param noBitmaps
+	 *            collection of objects to be excluded from bitmap commit
+	 *            selection.
 	 * @throws IOException
 	 *             when some I/O problem occur during reading objects.
 	 */
 	public void preparePack(ProgressMonitor countingMonitor,
 			@NonNull ObjectWalk walk,
 			@NonNull Set<? extends ObjectId> interestingObjects,
-			@NonNull Set<? extends ObjectId> uninterestingObjects)
+			@NonNull Set<? extends ObjectId> uninterestingObjects,
+			@NonNull Set<? extends ObjectId> noBitmaps)
 			throws IOException {
 		if (countingMonitor == null)
 			countingMonitor = NullProgressMonitor.INSTANCE;
@@ -798,7 +843,7 @@
 			throw new IllegalArgumentException(
 					JGitText.get().shallowPacksRequireDepthWalk);
 		findObjectsToPack(countingMonitor, walk, interestingObjects,
-				uninterestingObjects);
+				uninterestingObjects, noBitmaps);
 	}
 
 	/**
@@ -965,8 +1010,9 @@
 	/**
 	 * Write the prepared pack to the supplied stream.
 	 * <p>
-	 * Called after {@link #preparePack(ProgressMonitor, ObjectWalk, Set, Set)}
-	 * or {@link #preparePack(ProgressMonitor, Set, Set)}.
+	 * Called after
+	 * {@link #preparePack(ProgressMonitor, ObjectWalk, Set, Set, Set)} or
+	 * {@link #preparePack(ProgressMonitor, Set, Set)}.
 	 * <p>
 	 * Performs delta search if enabled and writes the pack stream.
 	 * <p>
@@ -1652,12 +1698,14 @@
 
 	private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor,
 			@NonNull ObjectWalk walker, @NonNull Set<? extends ObjectId> want,
-			@NonNull Set<? extends ObjectId> have) throws IOException {
+			@NonNull Set<? extends ObjectId> have,
+			@NonNull Set<? extends ObjectId> noBitmaps) throws IOException {
 		final long countingStart = System.currentTimeMillis();
 		beginPhase(PackingPhase.COUNTING, countingMonitor, ProgressMonitor.UNKNOWN);
 
 		stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want));
 		stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have));
+		excludeFromBitmapSelection = noBitmaps;
 
 		canBuildBitmaps = config.isBuildBitmaps()
 				&& !shallowPack
@@ -1874,7 +1922,6 @@
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
 		BitmapBuilder haveBitmap = bitmapWalker.findObjects(have, null, true);
-		bitmapWalker.reset();
 		BitmapBuilder wantBitmap = bitmapWalker.findObjects(want, haveBitmap,
 				false);
 		BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap);
@@ -2071,19 +2118,17 @@
 		PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
 				reader, writeBitmaps, pm, stats.interestingObjects, config);
 
-		Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits =
-				bitmapPreparer.selectCommits(numCommits);
+		Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits = bitmapPreparer
+				.selectCommits(numCommits, excludeFromBitmapSelection);
 
 		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
+			if (!cmit.isReuseWalker()) {
 				walker = bitmapPreparer.newBitmapWalker();
-
+			}
 			BitmapBuilder bitmap = walker.findObjects(
 					Collections.singleton(cmit), null, false);
 
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
index 07a03b4..8bedddb 100644
--- 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
@@ -141,6 +141,8 @@
 	 *
 	 * @param expectedCommitCount
 	 *            count of commits in the pack
+	 * @param excludeFromBitmapSelection
+	 *            commits that should be excluded from bitmap selection
 	 * @return commit objects for which bitmap indices should be built
 	 * @throws IncorrectObjectTypeException
 	 *             if any of the processed objects is not a commit
@@ -149,7 +151,8 @@
 	 * @throws MissingObjectException
 	 *             if an expected object is missing
 	 */
-	Collection<BitmapCommit> selectCommits(int expectedCommitCount)
+	Collection<BitmapCommit> selectCommits(int expectedCommitCount,
+			Set<? extends ObjectId> excludeFromBitmapSelection)
 			throws IncorrectObjectTypeException, IOException,
 			MissingObjectException {
 		/*
@@ -164,7 +167,7 @@
 		RevWalk rw = new RevWalk(reader);
 		rw.setRetainBody(false);
 		CommitSelectionHelper selectionHelper = setupTipCommitBitmaps(rw,
-				expectedCommitCount);
+				expectedCommitCount, excludeFromBitmapSelection);
 		pm.endTask();
 
 		int totCommits = selectionHelper.getCommitCount();
@@ -363,6 +366,8 @@
 	 * @param expectedCommitCount
 	 *            expected count of commits. The actual count may be less due to
 	 *            unreachable garbage.
+	 * @param excludeFromBitmapSelection
+	 *            commits that should be excluded from bitmap selection
 	 * @return a {@link CommitSelectionHelper} containing bitmaps for the tip
 	 *         commits
 	 * @throws IncorrectObjectTypeException
@@ -373,8 +378,10 @@
 	 *             if an expected object is missing
 	 */
 	private CommitSelectionHelper setupTipCommitBitmaps(RevWalk rw,
-			int expectedCommitCount) throws IncorrectObjectTypeException,
-					IOException, MissingObjectException {
+			int expectedCommitCount,
+			Set<? extends ObjectId> excludeFromBitmapSelection)
+			throws IncorrectObjectTypeException, IOException,
+			MissingObjectException {
 		BitmapBuilder reuse = commitBitmapIndex.newBitmapBuilder();
 		List<BitmapCommit> reuseCommits = new ArrayList<>();
 		for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
@@ -403,7 +410,8 @@
 		Set<RevCommit> peeledWant = new HashSet<>(want.size());
 		for (AnyObjectId objectId : want) {
 			RevObject ro = rw.peel(rw.parseAny(objectId));
-			if (!(ro instanceof RevCommit) || reuse.contains(ro)) {
+			if (!(ro instanceof RevCommit) || reuse.contains(ro)
+					|| excludeFromBitmapSelection.contains(ro)) {
 				continue;
 			}
 
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
index 2ec4d56..a5c3b71 100644
--- 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
@@ -44,7 +44,7 @@
 package org.eclipse.jgit.internal.storage.pack;
 
 import java.io.IOException;
-import java.util.Set;
+import java.util.Arrays;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -84,9 +84,60 @@
 		return countOfBitmapIndexMisses;
 	}
 
-	BitmapBuilder findObjects(Set<? extends ObjectId> start, BitmapBuilder seen, boolean ignoreMissingStart)
+	BitmapBuilder findObjects(Iterable<? extends ObjectId> start, BitmapBuilder seen,
+			boolean ignoreMissing)
+			throws MissingObjectException, IncorrectObjectTypeException,
+				   IOException {
+		if (!ignoreMissing) {
+			return findObjectsWalk(start, seen, false);
+		}
+
+		try {
+			return findObjectsWalk(start, seen, true);
+		} catch (MissingObjectException ignore) {
+			// An object reachable from one of the "start"s is missing.
+			// Walk from the "start"s one at a time so it can be excluded.
+		}
+
+		final BitmapBuilder result = bitmapIndex.newBitmapBuilder();
+		for (ObjectId obj : start) {
+			Bitmap bitmap = bitmapIndex.getBitmap(obj);
+			if (bitmap != null) {
+				result.or(bitmap);
+			}
+		}
+
+		for (ObjectId obj : start) {
+			if (result.contains(obj)) {
+				continue;
+			}
+			try {
+				result.or(findObjectsWalk(Arrays.asList(obj), result, false));
+			} catch (MissingObjectException ignore) {
+				// An object reachable from this "start" is missing.
+				//
+				// This can happen when the client specified a "have" line
+				// pointing to an object that is present but unreachable:
+				// "git prune" and "git fsck" only guarantee that the object
+				// database will continue to contain all objects reachable
+				// from a ref and does not guarantee connectivity for other
+				// objects in the object database.
+				//
+				// In this situation, skip the relevant "start" and move on
+				// to the next one.
+				//
+				// TODO(czhen): Make findObjectsWalk resume the walk instead
+				// once RevWalk and ObjectWalk support that.
+			}
+		}
+		return result;
+	}
+
+	private BitmapBuilder findObjectsWalk(Iterable<? extends ObjectId> start, BitmapBuilder seen,
+			boolean ignoreMissingStart)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
+		walker.reset();
 		final BitmapBuilder bitmapResult = bitmapIndex.newBitmapBuilder();
 
 		for (ObjectId obj : start) {
@@ -141,10 +192,6 @@
 		return bitmapResult;
 	}
 
-	void reset() {
-		walker.reset();
-	}
-
 	/**
 	 * A RevFilter that adds the visited commits to {@code bitmap} as a side
 	 * effect.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
index 54c8052..a75293d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
@@ -46,6 +46,8 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jgit.lib.internal.WorkQueue;
+
 /** ProgressMonitor that batches update events. */
 public abstract class BatchingProgressMonitor implements ProgressMonitor {
 	private long delayStartTime;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
index 1047a6d..c4923a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
@@ -43,6 +43,8 @@
 
 package org.eclipse.jgit.lib;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -177,8 +179,8 @@
 		while (tokenCount < 3 && nextSpace < lineEnd) {
 			switch (tokenCount) {
 			case 0:
-				String actionToken = new String(buf, tokenBegin, nextSpace
-						- tokenBegin - 1);
+				String actionToken = new String(buf, tokenBegin,
+						nextSpace - tokenBegin - 1, UTF_8);
 				tokenBegin = nextSpace;
 				action = RebaseTodoLine.Action.parse(actionToken);
 				if (action == null)
@@ -186,14 +188,14 @@
 				break;
 			case 1:
 				nextSpace = RawParseUtils.next(buf, tokenBegin, ' ');
-				String commitToken = new String(buf, tokenBegin, nextSpace
-						- tokenBegin - 1);
+				String commitToken = new String(buf, tokenBegin,
+						nextSpace - tokenBegin - 1, UTF_8);
 				tokenBegin = nextSpace;
 				commit = AbbreviatedObjectId.fromString(commitToken);
 				break;
 			case 2:
-				return new RebaseTodoLine(action, commit, RawParseUtils.decode(
-						buf, tokenBegin, 1 + lineEnd));
+				return new RebaseTodoLine(action, commit,
+						RawParseUtils.decode(buf, tokenBegin, 1 + lineEnd));
 			}
 			tokenCount++;
 		}
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 aa70f42..bd23ab9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -1151,6 +1151,33 @@
 	}
 
 	/**
+	 * Locate a reference to a commit and immediately parse its content.
+	 * <p>
+	 * This method only returns successfully if the commit object exists,
+	 * is verified to be a commit, and was parsed without error.
+	 *
+	 * @param id
+	 *            name of the commit object.
+	 * @return reference to the commit object. Never null.
+	 * @throws MissingObjectException
+	 *             the supplied commit does not exist.
+	 * @throws IncorrectObjectTypeException
+	 *             the supplied id is not a commit or an annotated tag.
+	 * @throws IOException
+	 *             a pack file or loose object could not be read.
+	 * @since 4.8
+	 */
+	public RevCommit parseCommit(AnyObjectId id) throws IncorrectObjectTypeException,
+			IOException, MissingObjectException {
+		if (id instanceof RevCommit && ((RevCommit) id).getRawBuffer() != null) {
+			return (RevCommit) id;
+		}
+		try (RevWalk walk = new RevWalk(this)) {
+			return walk.parseCommit(id);
+		}
+	}
+
+	/**
 	 * Create a new in-core index representation and read an index from disk.
 	 * <p>
 	 * The new index will be read before it is returned to the caller. Read
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 baa5286..53e9fe3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -55,6 +55,7 @@
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.internal.WorkQueue;
 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/SubmoduleConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
index 3126160..12f7b82 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
@@ -43,6 +43,10 @@
 
 package org.eclipse.jgit.lib;
 
+import java.util.Locale;
+
+import org.eclipse.jgit.util.StringUtils;
+
 /**
  * Submodule section of a Git configuration file.
  *
@@ -75,12 +79,17 @@
 
 		@Override
 		public String toConfigValue() {
-			return configValue;
+			return name().toLowerCase(Locale.ROOT).replace('_', '-');
 		}
 
 		@Override
 		public boolean matchConfigValue(String s) {
-			return configValue.equals(s);
+			if (StringUtils.isEmptyOrNull(s)) {
+				return false;
+			}
+			s = s.replace('-', '_');
+			return name().equalsIgnoreCase(s)
+					|| configValue.equalsIgnoreCase(s);
 		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
index 7675fcc..c31c3c6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
@@ -44,7 +44,10 @@
 
 package org.eclipse.jgit.lib;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.Writer;
 
@@ -56,7 +59,7 @@
 
 	/** Initialize a new progress monitor. */
 	public TextProgressMonitor() {
-		this(new PrintWriter(System.err));
+		this(new PrintWriter(new OutputStreamWriter(System.err, UTF_8)));
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java
similarity index 95%
rename from org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java
index 07b87f5..3303f47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java
@@ -41,7 +41,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.lib.internal;
 
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -50,7 +50,7 @@
 /**
  * Simple work queue to run tasks in the background
  */
-class WorkQueue {
+public class WorkQueue {
 	private static final ScheduledThreadPoolExecutor executor;
 
 	static final Object executorKiller;
@@ -94,7 +94,10 @@
 		};
 	}
 
-	static ScheduledThreadPoolExecutor getExecutor() {
+	/**
+	 * @return the WorkQueue's executor
+	 */
+	public static ScheduledThreadPoolExecutor getExecutor() {
 		return executor;
 	}
 }
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 656480e..af3d5ca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
@@ -49,6 +49,8 @@
 import java.util.HashMap;
 
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 
 /**
@@ -170,4 +172,20 @@
 	 * @return the new merge instance which implements this strategy.
 	 */
 	public abstract Merger newMerger(Repository db, boolean inCore);
+
+	/**
+	 * Create a new merge instance.
+	 * <p>
+	 * The merge will happen in memory, working folder will not be modified, in
+	 * case of a non-trivial merge that requires manual resolution, the merger
+	 * will fail.
+	 *
+	 * @param inserter
+	 *            inserter to write results back to.
+	 * @param config
+	 *            repo config for reading diff algorithm settings.
+	 * @return the new merge instance which implements this strategy.
+	 * @since 4.8
+	 */
+	public abstract Merger newMerger(ObjectInserter inserter, Config config);
 }
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 bee2d03..0c4488c 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,7 @@
 import java.io.IOException;
 import java.text.MessageFormat;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.NoMergeBaseException;
 import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
@@ -70,7 +71,15 @@
  * Instance of a specific {@link MergeStrategy} for a single {@link Repository}.
  */
 public abstract class Merger {
-	/** The repository this merger operates on. */
+	/**
+	 * The repository this merger operates on.
+	 * <p>
+	 * Null if and only if the merger was constructed with {@link
+	 * #Merger(ObjectInserter)}. Callers that want to assume the repo is not null
+	 * (e.g. because of a previous check that the merger is not in-core) may use
+	 * {@link #nonNullRepo()}.
+	 */
+	@Nullable
 	protected final Repository db;
 
 	/** Reader to support {@link #walk} and other object loading. */
@@ -104,20 +113,55 @@
 	 *            the repository this merger will read and write data on.
 	 */
 	protected Merger(final Repository local) {
+		if (local == null) {
+			throw new NullPointerException(JGitText.get().repositoryIsRequired);
+		}
 		db = local;
-		inserter = db.newObjectInserter();
+		inserter = local.newObjectInserter();
 		reader = inserter.newReader();
 		walk = new RevWalk(reader);
 	}
 
 	/**
+	 * Create a new in-core merge instance from an inserter.
+	 *
+	 * @param oi
+	 *            the inserter to write objects to. Will be closed at the
+	 *            conclusion of {@code merge}, unless {@code flush} is false.
+	 * @since 4.8
+	 */
+	protected Merger(ObjectInserter oi) {
+		db = null;
+		inserter = oi;
+		reader = oi.newReader();
+		walk = new RevWalk(reader);
+	}
+
+	/**
 	 * @return the repository this merger operates on.
 	 */
+	@Nullable
 	public Repository getRepository() {
 		return db;
 	}
 
-	/** @return an object writer to create objects in {@link #getRepository()}. */
+	/**
+	 * @return non-null repository instance
+	 * @throws NullPointerException
+	 *             if the merger was constructed without a repository.
+	 * @since 4.8
+	 */
+	protected Repository nonNullRepo() {
+		if (db == null) {
+			throw new NullPointerException(JGitText.get().repositoryIsRequired);
+		}
+		return db;
+	}
+
+	/**
+	 * @return an object writer to create objects, writing objects to {@link
+	 * #getRepository()} (if a repository was provided).
+	 */
 	public ObjectInserter getObjectInserter() {
 		return inserter;
 	}
@@ -131,7 +175,9 @@
 	 *
 	 * @param oi
 	 *            the inserter instance to use. Must be associated with the
-	 *            repository instance returned by {@link #getRepository()}.
+	 *            repository instance returned by {@link #getRepository()} (if a
+	 *            repository was provided). Will be closed at the conclusion of
+	 *            {@code merge}, unless {@code flush} is false.
 	 */
 	public void setObjectInserter(ObjectInserter oi) {
 		walk.close();
@@ -173,9 +219,9 @@
 	 *
 	 * @since 3.5
 	 * @param flush
-	 *            whether to flush the underlying object inserter when finished to
-	 *            store any content-merged blobs and virtual merged bases; if
-	 *            false, callers are responsible for flushing.
+	 *            whether to flush and close the underlying object inserter when
+	 *            finished to store any content-merged blobs and virtual merged
+	 *            bases; if false, callers are responsible for flushing.
 	 * @param tips
 	 *            source trees to be combined together. The merge base is not
 	 *            included in this set.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index f8e1998..1375cd3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -61,7 +61,9 @@
 import org.eclipse.jgit.errors.NoMergeBaseException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
 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;
@@ -111,6 +113,17 @@
 	}
 
 	/**
+	 * Normal recursive merge, implies inCore.
+	 *
+	 * @param inserter
+	 * @param config
+	 * @since 4.8
+	 */
+	protected RecursiveMerger(ObjectInserter inserter, Config config) {
+		super(inserter, config);
+	}
+
+	/**
 	 * 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.
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 90107be..86003e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -44,6 +44,9 @@
  */
 package org.eclipse.jgit.merge;
 
+import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
 import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
@@ -79,9 +82,10 @@
 import org.eclipse.jgit.errors.IndexWriteException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NoWorkTreeException;
-import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevTree;
@@ -266,18 +270,25 @@
 	 */
 	protected MergeAlgorithm mergeAlgorithm;
 
+	private static MergeAlgorithm getMergeAlgorithm(Config config) {
+		SupportedAlgorithm diffAlg = config.getEnum(
+				CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
+				HISTOGRAM);
+		return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
+	}
+
+	private static String[] defaultCommitNames() {
+		return new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+
 	/**
 	 * @param local
 	 * @param inCore
 	 */
 	protected ResolveMerger(Repository local, boolean inCore) {
 		super(local);
-		SupportedAlgorithm diffAlg = local.getConfig().getEnum(
-				ConfigConstants.CONFIG_DIFF_SECTION, null,
-				ConfigConstants.CONFIG_KEY_ALGORITHM,
-				SupportedAlgorithm.HISTOGRAM);
-		mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
-		commitNames = new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		mergeAlgorithm = getMergeAlgorithm(local.getConfig());
+		commitNames = defaultCommitNames();
 		this.inCore = inCore;
 
 		if (inCore) {
@@ -295,10 +306,24 @@
 		this(local, false);
 	}
 
+	/**
+	 * @param inserter
+	 * @param config
+	 * @since 4.8
+	 */
+	protected ResolveMerger(ObjectInserter inserter, Config config) {
+		super(inserter);
+		mergeAlgorithm = getMergeAlgorithm(config);
+		commitNames = defaultCommitNames();
+		inCore = true;
+		implicitDirCache = false;
+		dircache = DirCache.newInCore();
+	}
+
 	@Override
 	protected boolean mergeImpl() throws IOException {
 		if (implicitDirCache)
-			dircache = getRepository().lockDirCache();
+			dircache = nonNullRepo().lockDirCache();
 
 		try {
 			return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
@@ -315,7 +340,7 @@
 		// of a non-empty directory, for which delete() would fail.
 		for (int i = toBeDeleted.size() - 1; i >= 0; i--) {
 			String fileName = toBeDeleted.get(i);
-			File f = new File(db.getWorkTree(), fileName);
+			File f = new File(nonNullRepo().getWorkTree(), fileName);
 			if (!f.delete())
 				if (!f.isDirectory())
 					failingPaths.put(fileName,
@@ -348,7 +373,7 @@
 			return;
 		}
 
-		DirCache dc = db.readDirCache();
+		DirCache dc = nonNullRepo().readDirCache();
 		Iterator<String> mpathsIt=modifiedFiles.iterator();
 		while(mpathsIt.hasNext()) {
 			String mpath=mpathsIt.next();
@@ -785,8 +810,8 @@
 	 */
 	private File writeMergedFile(MergeResult<RawText> result)
 			throws FileNotFoundException, IOException {
-		File workTree = db.getWorkTree();
-		FS fs = db.getFS();
+		File workTree = nonNullRepo().getWorkTree();
+		FS fs = nonNullRepo().getFS();
 		File of = new File(workTree, tw.getPathString());
 		File parentFolder = of.getParentFile();
 		if (!fs.exists(parentFolder))
@@ -802,7 +827,7 @@
 	private ObjectId insertMergeResult(MergeResult<RawText> result)
 			throws IOException {
 		TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
-				db.getDirectory(), 10 << 20);
+				db != null ? nonNullRepo().getDirectory() : null, 10 << 20);
 		try {
 			new MergeFormatter().formatMerge(buf, result,
 					Arrays.asList(commitNames), CHARACTER_ENCODING);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
index 12d6c6b..2224dbc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
@@ -46,7 +46,9 @@
 
 import java.io.IOException;
 
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 
 /**
@@ -89,6 +91,11 @@
 		return new OneSide(db, treeIndex);
 	}
 
+	@Override
+	public Merger newMerger(final ObjectInserter inserter, final Config config) {
+		return new OneSide(inserter, treeIndex);
+	}
+
 	static class OneSide extends Merger {
 		private final int treeIndex;
 
@@ -97,6 +104,11 @@
 			treeIndex = index;
 		}
 
+		protected OneSide(final ObjectInserter inserter, final int index) {
+			super(inserter);
+			treeIndex = index;
+		}
+
 		@Override
 		protected boolean mergeImpl() throws IOException {
 			return treeIndex < sourceTrees.length;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
index 22e608e..56128dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
@@ -43,6 +43,8 @@
 
 package org.eclipse.jgit.merge;
 
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 
 /**
@@ -63,6 +65,11 @@
 	}
 
 	@Override
+	public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
+		return new RecursiveMerger(inserter, config);
+	}
+
+	@Override
 	public String getName() {
 		return "recursive"; //$NON-NLS-1$
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java
index 07368e5..17044b5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java
@@ -43,6 +43,8 @@
  */
 package org.eclipse.jgit.merge;
 
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 
 /**
@@ -60,8 +62,16 @@
 		return new ResolveMerger(db, inCore);
 	}
 
+	/**
+	 * @since 4.8
+	 */
+	@Override
+	public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
+		return new ResolveMerger(inserter, config);
+	}
+
 	@Override
 	public String getName() {
 		return "resolve"; //$NON-NLS-1$
 	}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
index ec903c1..cd427bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -49,6 +49,7 @@
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
@@ -89,6 +90,14 @@
 		return newMerger(db);
 	}
 
+	/**
+	 * @since 4.8
+	 */
+	@Override
+	public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
+		return new InCoreMerger(inserter);
+	}
+
 	private static class InCoreMerger extends ThreeWayMerger {
 		private static final int T_BASE = 0;
 
@@ -110,6 +119,12 @@
 			cache = DirCache.newInCore();
 		}
 
+		InCoreMerger(final ObjectInserter inserter) {
+			super(inserter);
+			tw = new NameConflictTreeWalk(null, reader);
+			cache = DirCache.newInCore();
+		}
+
 		@Override
 		protected boolean mergeImpl() throws IOException {
 			tw.addTree(mergeBase());
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 fbedaef..b3ef0fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
@@ -50,6 +50,7 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTree;
@@ -85,6 +86,17 @@
 	}
 
 	/**
+	 * Create a new in-core merge instance from an inserter.
+	 *
+	 * @param inserter
+	 *            the inserter to write objects to.
+	 * @since 4.8
+	 */
+	protected ThreeWayMerger(ObjectInserter inserter) {
+		super(inserter);
+	}
+
+	/**
 	 * Set the common ancestor tree.
 	 *
 	 * @param id
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index 5d7e72d..73ce985 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -69,26 +69,21 @@
  */
 class MergeBaseGenerator extends Generator {
 	private static final int PARSED = RevWalk.PARSED;
-
 	private static final int IN_PENDING = RevWalk.SEEN;
-
 	private static final int POPPED = RevWalk.TEMP_MARK;
-
 	private static final int MERGE_BASE = RevWalk.REWRITE;
 
 	private final RevWalk walker;
-
 	private final DateRevQueue pending;
 
 	private int branchMask;
-
 	private int recarryTest;
-
 	private int recarryMask;
-
 	private int mergeBaseAncestor = -1;
 	private LinkedList<RevCommit> ret = new LinkedList<>();
 
+	private CarryStack stack;
+
 	MergeBaseGenerator(final RevWalk w) {
 		walker = w;
 		pending = new DateRevQueue();
@@ -202,29 +197,56 @@
 		return null;
 	}
 
-	private void carryOntoHistory(RevCommit c, final int carry) {
+	private void carryOntoHistory(RevCommit c, int carry) {
+		stack = null;
 		for (;;) {
-			final RevCommit[] pList = c.parents;
-			if (pList == null)
-				return;
-			final int n = pList.length;
-			if (n == 0)
-				return;
-
-			for (int i = 1; i < n; i++) {
-				final RevCommit p = pList[i];
-				if (!carryOntoOne(p, carry))
-					carryOntoHistory(p, carry);
-			}
-
-			c = pList[0];
-			if (carryOntoOne(c, carry))
+			carryOntoHistoryInnerLoop(c, carry);
+			if (stack == null) {
 				break;
+			}
+			c = stack.c;
+			carry = stack.carry;
+			stack = stack.prev;
 		}
 	}
 
-	private boolean carryOntoOne(final RevCommit p, final int carry) {
-		final boolean haveAll = (p.flags & carry) == carry;
+	private void carryOntoHistoryInnerLoop(RevCommit c, int carry) {
+		for (;;) {
+			RevCommit[] parents = c.parents;
+			if (parents == null || parents.length == 0) {
+				break;
+			}
+
+			int e = parents.length - 1;
+			for (int i = 0; i < e; i++) {
+				RevCommit p = parents[i];
+				if (carryOntoOne(p, carry) == CONTINUE) {
+					// Walking p will be required, buffer p on stack.
+					stack = new CarryStack(stack, p, carry);
+				}
+				// For other results from carryOntoOne:
+				// HAVE_ALL: p has all bits, do nothing to skip that path.
+				// CONTINUE_ON_STACK: callee pushed StackElement for p.
+			}
+
+			c = parents[e];
+			if (carryOntoOne(c, carry) != CONTINUE) {
+				break;
+			}
+		}
+	}
+
+	private static final int CONTINUE = 0;
+	private static final int HAVE_ALL = 1;
+	private static final int CONTINUE_ON_STACK = 2;
+
+	private int carryOntoOne(RevCommit p, int carry) {
+		// If we already had all carried flags, our parents do too.
+		// Return HAVE_ALL to stop caller from running down this leg
+		// of the revision graph any further.
+		//
+		// Otherwise return CONTINUE to ask the caller to walk history.
+		int rc = (p.flags & carry) == carry ? HAVE_ALL : CONTINUE;
 		p.flags |= carry;
 
 		if ((p.flags & recarryMask) == recarryTest) {
@@ -232,17 +254,23 @@
 			// voted to be one. Inject ourselves back at the front of the
 			// pending queue and tell all of our ancestors they are within
 			// the merge base now.
-			//
 			p.flags &= ~POPPED;
 			pending.add(p);
-			carryOntoHistory(p, branchMask | MERGE_BASE);
-			return true;
+			stack = new CarryStack(stack, p, branchMask | MERGE_BASE);
+			return CONTINUE_ON_STACK;
 		}
+		return rc;
+	}
 
-		// If we already had all carried flags, our parents do too.
-		// Return true to stop the caller from running down this leg
-		// of the revision graph any further.
-		//
-		return haveAll;
+	private static class CarryStack {
+		final CarryStack prev;
+		final RevCommit c;
+		final int carry;
+
+		CarryStack(CarryStack prev, RevCommit c, int carry) {
+			this.prev = prev;
+			this.c = c;
+			this.carry = carry;
+		}
 	}
 }
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 37d70e3..0920f21 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -58,6 +58,7 @@
 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.Ref;
 import org.eclipse.jgit.lib.Repository;
@@ -84,6 +85,8 @@
 public class BundleWriter {
 	private final Repository db;
 
+	private final ObjectReader reader;
+
 	private final Map<String, ObjectId> include;
 
 	private final Set<RevCommit> assume;
@@ -100,8 +103,26 @@
 	 * @param repo
 	 *            repository where objects are stored.
 	 */
-	public BundleWriter(final Repository repo) {
+	public BundleWriter(Repository repo) {
 		db = repo;
+		reader = null;
+		include = new TreeMap<>();
+		assume = new HashSet<>();
+		tagTargets = new HashSet<>();
+	}
+
+	/**
+	 * Create a writer for a bundle.
+	 *
+	 * @param or
+	 *            reader for reading objects. Will be closed at the end of {@link
+	 *            #writeBundle(ProgressMonitor, OutputStream)}, but readers may be
+	 *            reused after closing.
+	 * @since 4.8
+	 */
+	public BundleWriter(ObjectReader or) {
+		db = null;
+		reader = or;
 		include = new TreeMap<>();
 		assume = new HashSet<>();
 		tagTargets = new HashSet<>();
@@ -112,7 +133,8 @@
 	 *
 	 * @param pc
 	 *            configuration controlling packing parameters. If null the
-	 *            source repository's settings will be used.
+	 *            source repository's settings will be used, or the default
+	 *            settings if constructed without a repo.
 	 */
 	public void setPackConfig(PackConfig pc) {
 		this.packConfig = pc;
@@ -196,10 +218,7 @@
 	 */
 	public void writeBundle(ProgressMonitor monitor, OutputStream os)
 			throws IOException {
-		PackConfig pc = packConfig;
-		if (pc == null)
-			pc = new PackConfig(db);
-		try (PackWriter packWriter = new PackWriter(pc, db.newObjectReader())) {
+		try (PackWriter packWriter = newPackWriter()) {
 			packWriter.setObjectCountCallback(callback);
 
 			final HashSet<ObjectId> inc = new HashSet<>();
@@ -242,6 +261,14 @@
 		}
 	}
 
+	private PackWriter newPackWriter() {
+		PackConfig pc = packConfig;
+		if (pc == null) {
+			pc = db != null ? new PackConfig(db) : new PackConfig();
+		}
+		return new PackWriter(pc, reader != null ? reader : db.newObjectReader());
+	}
+
 	/**
 	 * Set the {@link ObjectCountCallback}.
 	 * <p>
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 7de5a9f..e4de57a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -58,6 +58,7 @@
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
 
+import java.io.ByteArrayOutputStream;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -234,7 +235,7 @@
 
 	private InputStream rawIn;
 
-	private OutputStream rawOut;
+	private ResponseBufferedOutputStream rawOut;
 
 	private PacketLineIn pckIn;
 
@@ -643,11 +644,10 @@
 	 *            other network connections this should be null.
 	 * @throws IOException
 	 */
-	public void upload(final InputStream input, final OutputStream output,
+	public void upload(final InputStream input, OutputStream output,
 			final OutputStream messages) throws IOException {
 		try {
 			rawIn = input;
-			rawOut = output;
 			if (messages != null)
 				msgOut = messages;
 
@@ -655,11 +655,17 @@
 				final Thread caller = Thread.currentThread();
 				timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
 				TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
-				TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
+				@SuppressWarnings("resource")
+				TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
 				i.setTimeout(timeout * 1000);
 				o.setTimeout(timeout * 1000);
 				rawIn = i;
-				rawOut = o;
+				output = o;
+			}
+
+			rawOut = new ResponseBufferedOutputStream(output);
+			if (biDirectionalPipe) {
+				rawOut.stopBuffering();
 			}
 
 			pckIn = new PacketLineIn(rawIn);
@@ -719,6 +725,8 @@
 
 	private void service() throws IOException {
 		boolean sendPack;
+		// If it's a non-bidi request, we need to read the entire request before
+		// writing a response. Buffer the response until then.
 		try {
 			if (biDirectionalPipe)
 				sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@@ -774,6 +782,8 @@
 				throw new UploadPackInternalServerErrorException(err);
 			}
 			throw err;
+		} finally {
+			rawOut.stopBuffering();
 		}
 
 		if (sendPack)
@@ -1509,7 +1519,7 @@
 				walk.reset();
 
 				ObjectWalk ow = rw.toObjectWalkWithSameObjects();
-				pw.preparePack(pm, ow, wantAll, commonBase);
+				pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
 				rw = ow;
 			}
 
@@ -1568,4 +1578,47 @@
 			adv.addSymref(Constants.HEAD, head.getLeaf().getName());
 		}
 	}
+
+	private static class ResponseBufferedOutputStream extends OutputStream {
+		private final OutputStream rawOut;
+
+		private OutputStream out;
+
+		ResponseBufferedOutputStream(OutputStream rawOut) {
+			this.rawOut = rawOut;
+			this.out = new ByteArrayOutputStream();
+		}
+
+		@Override
+		public void write(int b) throws IOException {
+			out.write(b);
+		}
+
+		@Override
+		public void write(byte b[]) throws IOException {
+			out.write(b);
+		}
+
+		@Override
+		public void write(byte b[], int off, int len) throws IOException {
+			out.write(b, off, len);
+		}
+
+		@Override
+		public void flush() throws IOException {
+			out.flush();
+		}
+
+		@Override
+		public void close() throws IOException {
+			out.close();
+		}
+
+		void stopBuffering() throws IOException {
+			if (out != rawOut) {
+				((ByteArrayOutputStream) out).writeTo(rawOut);
+				out = rawOut;
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index c0b29ef..59cf798 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -45,6 +45,7 @@
 
 import java.io.IOException;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.lib.FileMode;
@@ -110,7 +111,7 @@
 	 *            the reader the walker will obtain tree data from.
 	 * @since 4.3
 	 */
-	public NameConflictTreeWalk(Repository repo, final ObjectReader or) {
+	public NameConflictTreeWalk(@Nullable Repository repo, final ObjectReader or) {
 		super(repo, or);
 	}
 
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 183bf46..9c2c905 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -530,7 +530,13 @@
 			if (env != null) {
 				pb.environment().putAll(env);
 			}
-			Process p = pb.start();
+			Process p;
+			try {
+				p = pb.start();
+			} catch (IOException e) {
+				// Process failed to start
+				throw new CommandFailedException(-1, e.getMessage(), e);
+			}
 			p.getOutputStream().close();
 			GobblerThread gobbler = new GobblerThread(p, command, dir);
 			gobbler.start();
@@ -896,7 +902,7 @@
 	}
 
 	/**
-	 * See {@link FileUtils#relativize(String, String)}.
+	 * See {@link FileUtils#relativizePath(String, String, String, boolean)}.
 	 *
 	 * @param base
 	 *            The path against which <code>other</code> should be
@@ -905,11 +911,11 @@
 	 *            The path that will be made relative to <code>base</code>.
 	 * @return A relative path that, when resolved against <code>base</code>,
 	 *         will yield the original <code>other</code>.
-	 * @see FileUtils#relativize(String, String)
+	 * @see FileUtils#relativizePath(String, String, String, boolean)
 	 * @since 3.7
 	 */
 	public String relativize(String base, String other) {
-		return FileUtils.relativize(base, other);
+		return FileUtils.relativizePath(base, other, File.separator, this.isCaseSensitive());
 	}
 
 	/**
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 31cfe42..f2a387a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -468,10 +468,71 @@
 		throw new IOException(JGitText.get().cannotCreateTempDir);
 	}
 
+
 	/**
-	 * This will try and make a given path relative to another.
+	 * @deprecated Use the more-clearly-named
+	 *             {@link FileUtils#relativizeNativePath(String, String)}
+	 *             instead, or directly call
+	 *             {@link FileUtils#relativizePath(String, String, String, boolean)}
+	 *
+	 *             Expresses <code>other</code> as a relative file path from
+	 *             <code>base</code>. File-separator and case sensitivity are
+	 *             based on the current file system.
+	 *
+	 *             See also
+	 *             {@link FileUtils#relativizePath(String, String, String, boolean)}.
+	 *
+	 * @param base
+	 *            Base path
+	 * @param other
+	 *            Destination path
+	 * @return Relative path from <code>base</code> to <code>other</code>
+	 * @since 3.7
+	 */
+	@Deprecated
+	public static String relativize(String base, String other) {
+		return relativizeNativePath(base, other);
+	}
+
+	/**
+	 * Expresses <code>other</code> as a relative file path from <code>base</code>.
+	 * File-separator and case sensitivity are based on the current file system.
+	 *
+	 * See also {@link FileUtils#relativizePath(String, String, String, boolean)}.
+	 *
+	 * @param base
+	 *            Base path
+	 * @param other
+	 *             Destination path
+	 * @return Relative path from <code>base</code> to <code>other</code>
+	 * @since 4.8
+	 */
+	public static String relativizeNativePath(String base, String other) {
+		return FS.DETECTED.relativize(base, other);
+	}
+
+	/**
+	 * Expresses <code>other</code> as a relative file path from <code>base</code>.
+	 * File-separator and case sensitivity are based on Git's internal representation of files (which matches Unix).
+	 *
+	 * See also {@link FileUtils#relativizePath(String, String, String, boolean)}.
+	 *
+	 * @param base
+	 *            Base path
+	 * @param other
+	 *             Destination path
+	 * @return Relative path from <code>base</code> to <code>other</code>
+	 * @since 4.8
+	 */
+	public static String relativizeGitPath(String base, String other) {
+		return relativizePath(base, other, "/", false); //$NON-NLS-1$
+	}
+
+
+	/**
+	 * Expresses <code>other</code> as a relative file path from <code>base</code>
 	 * <p>
-	 * For example, if this is called with the two following paths :
+	 * For example, if called with the two following paths :
 	 *
 	 * <pre>
 	 * <code>base = "c:\\Users\\jdoe\\eclipse\\git\\project"</code>
@@ -480,9 +541,7 @@
 	 *
 	 * This will return "..\\another_project\\pom.xml".
 	 * </p>
-	 * <p>
-	 * This method uses {@link File#separator} to split the paths into segments.
-	 * </p>
+	 *
 	 * <p>
 	 * <b>Note</b> that this will return the empty String if <code>base</code>
 	 * and <code>other</code> are equal.
@@ -494,41 +553,44 @@
 	 *            folder and not a file.
 	 * @param other
 	 *            The path that will be made relative to <code>base</code>.
+	 * @param dirSeparator
+	 *            A string that separates components of the path. In practice, this is "/" or "\\".
+	 * @param caseSensitive
+	 *            Whether to consider differently-cased directory names as distinct
 	 * @return A relative path that, when resolved against <code>base</code>,
 	 *         will yield the original <code>other</code>.
-	 * @since 3.7
+	 * @since 4.8
 	 */
-	public static String relativize(String base, String other) {
+	public static String relativizePath(String base, String other, String dirSeparator, boolean caseSensitive) {
 		if (base.equals(other))
 			return ""; //$NON-NLS-1$
 
-		final boolean ignoreCase = !FS.DETECTED.isCaseSensitive();
-		final String[] baseSegments = base.split(Pattern.quote(File.separator));
+		final String[] baseSegments = base.split(Pattern.quote(dirSeparator));
 		final String[] otherSegments = other.split(Pattern
-				.quote(File.separator));
+				.quote(dirSeparator));
 
 		int commonPrefix = 0;
 		while (commonPrefix < baseSegments.length
 				&& commonPrefix < otherSegments.length) {
-			if (ignoreCase
+			if (caseSensitive
+					&& baseSegments[commonPrefix]
+					.equals(otherSegments[commonPrefix]))
+				commonPrefix++;
+			else if (!caseSensitive
 					&& baseSegments[commonPrefix]
 							.equalsIgnoreCase(otherSegments[commonPrefix]))
 				commonPrefix++;
-			else if (!ignoreCase
-					&& baseSegments[commonPrefix]
-							.equals(otherSegments[commonPrefix]))
-				commonPrefix++;
 			else
 				break;
 		}
 
 		final StringBuilder builder = new StringBuilder();
 		for (int i = commonPrefix; i < baseSegments.length; i++)
-			builder.append("..").append(File.separator); //$NON-NLS-1$
+			builder.append("..").append(dirSeparator); //$NON-NLS-1$
 		for (int i = commonPrefix; i < otherSegments.length; i++) {
 			builder.append(otherSegments[i]);
 			if (i < otherSegments.length - 1)
-				builder.append(File.separator);
+				builder.append(dirSeparator);
 		}
 		return builder.toString();
 	}
diff --git a/pom.xml b/pom.xml
index 4764bdf..b74de4e 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>4.7.9-SNAPSHOT</version>
+  <version>4.8.1-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -70,6 +70,10 @@
     <connection>scm:git:https://git.eclipse.org/r/jgit/jgit</connection>
   </scm>
 
+  <prerequisites>
+    <maven>3.3.1</maven>
+  </prerequisites>
+
   <ciManagement>
     <system>hudson</system>
     <url>https://hudson.eclipse.org/jgit/</url>
@@ -191,7 +195,7 @@
     <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>4.6.0.201612231935-r</jgit-last-release-version>
+    <jgit-last-release-version>4.7.0.201704051617-r</jgit-last-release-version>
     <jsch-version>0.1.54</jsch-version>
     <javaewah-version>1.1.6</javaewah-version>
     <junit-version>4.12</junit-version>
@@ -200,15 +204,16 @@
     <commons-compress-version>1.6</commons-compress-version>
     <osgi-core-version>4.3.1</osgi-core-version>
     <servlet-api-version>3.1.0</servlet-api-version>
-    <jetty-version>9.3.17.v20170317</jetty-version>
-    <japicmp-version>0.5.3</japicmp-version>
+    <jetty-version>9.4.5.v20170502</jetty-version>
+    <japicmp-version>0.10.0</japicmp-version>
     <httpclient-version>4.3.6</httpclient-version>
     <slf4j-version>1.7.2</slf4j-version>
     <log4j-version>1.2.15</log4j-version>
     <maven-javadoc-plugin-version>2.10.4</maven-javadoc-plugin-version>
-    <tycho-extras-version>0.26.0</tycho-extras-version>
+    <tycho-extras-version>1.0.0</tycho-extras-version>
     <gson-version>2.2.4</gson-version>
-    <spotbugs-maven-plugin-version>3.1.6</spotbugs-maven-plugin-version>
+    <findbugs-maven-plugin-version>3.0.4</findbugs-maven-plugin-version>
+    <maven-surefire-report-plugin-version>2.20</maven-surefire-report-plugin-version>
 
     <!-- Properties to enable jacoco code coverage analysis -->
     <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -352,7 +357,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
-          <version>2.19.1</version>
+          <version>2.20</version>
           <configuration>
             <forkCount>${test-fork-count}</forkCount>
             <reuseForks>true</reuseForks>
@@ -366,9 +371,9 @@
         </plugin>
 
         <plugin>
-          <groupId>com.github.spotbugs</groupId>
-          <artifactId>spotbugs-maven-plugin</artifactId>
-          <version>${spotbugs-maven-plugin-version}</version>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>findbugs-maven-plugin</artifactId>
+          <version>${findbugs-maven-plugin-version}</version>
           <configuration>
             <findbugsXmlOutput>true</findbugsXmlOutput>
             <failOnError>false</failOnError>
@@ -385,13 +390,16 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>3.7</version>
+          <version>3.8</version>
           <configuration>
             <sourceEncoding>utf-8</sourceEncoding>
             <minimumTokens>100</minimumTokens>
             <targetJdk>1.8</targetJdk>
             <format>xml</format>
             <failOnViolation>false</failOnViolation>
+            <excludes>
+              <exclude>**/UbcCheck.java</exclude>
+            </excludes>
           </configuration>
           <executions>
             <execution>
@@ -405,7 +413,7 @@
         <plugin>
           <groupId>org.eclipse.cbi.maven.plugins</groupId>
           <artifactId>eclipse-jarsigner-plugin</artifactId>
-          <version>1.1.3</version>
+          <version>1.1.4</version>
         </plugin>
         <plugin>
           <groupId>org.eclipse.tycho.extras</groupId>
@@ -430,14 +438,14 @@
             <dependency><!-- add support for ssh/scp -->
               <groupId>org.apache.maven.wagon</groupId>
               <artifactId>wagon-ssh</artifactId>
-              <version>2.10</version>
+              <version>2.12</version>
             </dependency>
           </dependencies>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-report-plugin</artifactId>
-          <version>2.19.1</version>
+          <version>${maven-surefire-report-plugin-version}</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -571,14 +579,14 @@
         <version>2.5</version>
       </plugin>
       <plugin>
-        <groupId>com.github.spotbugs</groupId>
-        <artifactId>spotbugs-maven-plugin</artifactId>
-        <version>${spotbugs-maven-plugin-version}</version>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>${findbugs-maven-plugin-version}</version>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-report-plugin</artifactId>
-        <version>2.19.1</version>
+        <version>${maven-surefire-report-plugin-version}</version>
         <configuration>
           <aggregate>true</aggregate>
           <alwaysGenerateSurefireReport>false</alwaysGenerateSurefireReport>
@@ -743,8 +751,8 @@
       <build>
         <plugins>
           <plugin>
-            <groupId>com.github.spotbugs</groupId>
-            <artifactId>spotbugs-maven-plugin</artifactId>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>findbugs-maven-plugin</artifactId>
           </plugin>
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
diff --git a/tools/version.sh b/tools/version.sh
index 81ffe06..e5c98ec 100755
--- a/tools/version.sh
+++ b/tools/version.sh
@@ -131,6 +131,7 @@
 		$seen_version = 1 if (!/<\?xml/ &&
 		s/(version=")[^"]*(")/${1}'"$OSGI_V"'${2}/);
 	}
+	s/(import feature="org\.eclipse\.jgit.*" version=")[^"]*(")/${1}'"$API_V"'${2}/;
 	' org.eclipse.jgit.packaging/org.*.feature/feature.xml
 
 perl -pi~ -e '