Merge branch 'stable-4.10' into stable-4.11

* stable-4.10:
  Prepare 4.9.6-SNAPSHOT builds
  JGit v4.9.5.201809180939-r
  Prepare 4.7.5-SNAPSHOT builds
  JGit v4.7.4.201809180905-r
  Update API problem filter

Change-Id: I6633f1e147444385c999a6ff1d443b8a07d08bfc
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/BUILD b/BUILD
index be6dd76..5fea669 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,12 @@
 package(default_visibility = ["//visibility:public"])
 
+config_setting(
+    name = "jdk9",
+    values = {
+        "java_toolchain": "@bazel_tools//tools/jdk:toolchain_jdk9",
+    },
+)
+
 genrule(
     name = "all",
     testonly = 1,
diff --git a/WORKSPACE b/WORKSPACE
index 234879f..df99c95 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -16,6 +16,12 @@
 )
 
 maven_jar(
+    name = "jzlib",
+    artifact = "com.jcraft:jzlib:1.1.1",
+    sha1 = "a1551373315ffc2f96130a0e5704f74e151777ba",
+)
+
+maven_jar(
     name = "javaewah",
     artifact = "com.googlecode.javaewah:JavaEWAH:1.1.6",
     sha1 = "94ad16d728b374d65bd897625f3fbb3da223a2b6",
@@ -65,14 +71,14 @@
 
 maven_jar(
     name = "commons-compress",
-    artifact = "org.apache.commons:commons-compress:1.6",
-    sha1 = "c7d9b580aff9e9f1998361f16578e63e5c064699",
+    artifact = "org.apache.commons:commons-compress:1.15",
+    sha1 = "b686cd04abaef1ea7bc5e143c080563668eec17e",
 )
 
 maven_jar(
     name = "tukaani-xz",
-    artifact = "org.tukaani:xz:1.3",
-    sha1 = "66db21c8484120cb6a51b5b3ea47b6f383942bec",
+    artifact = "org.tukaani:xz:1.6",
+    sha1 = "05b6f921f1810bdf90e25471968f741f87168b64",
 )
 
 maven_jar(
@@ -101,8 +107,8 @@
 
 maven_jar(
     name = "gson",
-    artifact = "com.google.code.gson:gson:2.2.4",
-    sha1 = "a60a5e993c98c864010053cb901b7eab25306568",
+    artifact = "com.google.code.gson:gson:2.8.2",
+    sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf",
 )
 
 JETTY_VER = "9.4.8.v20171121"
diff --git a/lib/BUILD b/lib/BUILD
index a3936ee..0e659e5 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -30,7 +30,10 @@
 
 java_library(
     name = "gson",
-    visibility = ["//org.eclipse.jgit.lfs.server:__pkg__"],
+    visibility = [
+        "//org.eclipse.jgit.lfs:__pkg__",
+        "//org.eclipse.jgit.lfs.server:__pkg__",
+    ],
     exports = ["@gson//jar"],
 )
 
@@ -114,6 +117,15 @@
 )
 
 java_library(
+    name = "jzlib",
+    visibility = [
+        "//org.eclipse.jgit:__pkg__",
+        "//org.eclipse.jgit.test:__pkg__",
+    ],
+    exports = ["@jzlib//jar"],
+)
+
+java_library(
     name = "junit",
     testonly = 1,
     visibility = ["//visibility:public"],
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index f0f7712..7a6e435 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -4,13 +4,13 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.ant.test
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.junit;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.ant.tasks;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index 1386a26..404888d 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
index 4d260cf..565b75c 100644
--- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index a056886..c972356 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,11 +3,11 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.ant
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)"
+  org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="4.10.1";
+Export-Package: org.eclipse.jgit.ant.tasks;version="4.11.3";
  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 56dc17b..fd8de39 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.10.1-SNAPSHOT</version>
+		<version>4.11.3-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
index 06ddbab..13c32a6 100644
--- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index de627aa..1a402d8 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.archive
 Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -13,15 +13,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.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.api;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.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.10.1";
+Export-Package: org.eclipse.jgit.archive;version="4.11.3";
   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 6c43e29..e5a7502 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.10.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.10.1.qualifier";roots="."
+Bundle-Version: 4.11.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.11.3.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index fde3f51..ddd4b90 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
index 4d260cf..565b75c 100644
--- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 6da3208..21c3b7f 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %Bundle-Name
 Automatic-Module-Name: org.eclipse.jgit.http.apache
 Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
@@ -23,10 +23,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.10.1,4.11.0)",
- org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="4.10.1";
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.11.3";
   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 b3bcc50..a1543cc 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.10.1-SNAPSHOT</version>
+		<version>4.11.3-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
index 4d260cf..565b75c 100644
--- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index e850235..d3c8123 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.http.server
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="4.10.1",
- org.eclipse.jgit.http.server.glue;version="4.10.1";
+Export-Package: org.eclipse.jgit.http.server;version="4.11.3",
+ org.eclipse.jgit.http.server.glue;version="4.11.3";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="4.10.1";
+ org.eclipse.jgit.http.server.resolver;version="4.11.3";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -18,12 +18,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.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)"
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 454d808..1110078 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java
index 91e749e..0d935fc 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/FileSender.java
@@ -137,8 +137,7 @@
 		rsp.setHeader(HDR_CONTENT_LENGTH, Long.toString(end - pos));
 
 		if (sendBody) {
-			final OutputStream out = rsp.getOutputStream();
-			try {
+			try (OutputStream out = rsp.getOutputStream()) {
 				final byte[] buf = new byte[4096];
 				source.seek(pos);
 				while (pos < end) {
@@ -151,8 +150,6 @@
 					pos += n;
 				}
 				out.flush();
-			} finally {
-				out.close();
 			}
 		}
 	}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
index d6955b4..b9ca12b 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
@@ -314,11 +314,8 @@
 		res.setStatus(HttpServletResponse.SC_OK);
 		res.setContentType(type);
 		res.setContentLength(buf.length);
-		OutputStream os = res.getOutputStream();
-		try {
+		try (OutputStream os = res.getOutputStream()) {
 			os.write(buf);
-		} finally {
-			os.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
index ccb76ad..4f70cf7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
@@ -74,29 +74,30 @@
 		rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING);
 
 		final Repository db = getRepository(req);
-		final OutputStreamWriter out = new OutputStreamWriter(
+		try (OutputStreamWriter out = new OutputStreamWriter(
 				new SmartOutputStream(req, rsp, true),
-				Constants.CHARSET);
-		final RefAdvertiser adv = new RefAdvertiser() {
-			@Override
-			protected void writeOne(final CharSequence line) throws IOException {
-				// Whoever decided that info/refs should use a different
-				// delimiter than the native git:// protocol shouldn't
-				// be allowed to design this sort of stuff. :-(
-				out.append(line.toString().replace(' ', '\t'));
-			}
+				Constants.CHARSET)) {
+			final RefAdvertiser adv = new RefAdvertiser() {
+				@Override
+				protected void writeOne(final CharSequence line)
+						throws IOException {
+					// Whoever decided that info/refs should use a different
+					// delimiter than the native git:// protocol shouldn't
+					// be allowed to design this sort of stuff. :-(
+					out.append(line.toString().replace(' ', '\t'));
+				}
 
-			@Override
-			protected void end() {
-				// No end marker required for info/refs format.
-			}
-		};
-		adv.init(db);
-		adv.setDerefTags(true);
+				@Override
+				protected void end() {
+					// No end marker required for info/refs format.
+				}
+			};
+			adv.init(db);
+			adv.setDerefTags(true);
 
-		Map<String, Ref> refs = db.getRefDatabase().getRefs(ALL);
-		refs.remove(Constants.HEAD);
-		adv.send(refs);
-		out.close();
+			Map<String, Ref> refs = db.getRefDatabase().getRefs(ALL);
+			refs.remove(Constants.HEAD);
+			adv.send(refs);
+		}
 	}
 }
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index a7f3667..c793fda 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.http.test
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -25,25 +25,25 @@
  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.10.1,4.11.0)",
- org.eclipse.jgit.http.server;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.http.server.glue;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.http.server.resolver;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.junit;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.junit.http;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.http.server;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 81a7610..4f336da 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
index 0e92b14..5a46967 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
@@ -135,8 +135,7 @@
 		final RevCommit Q = src.commit().add("Q", Q_txt).create();
 		final Repository db = src.getRepository();
 		final String dstName = Constants.R_HEADS + "new.branch";
-		final Transport t = Transport.open(db, remoteURI);
-		try {
+		try (Transport t = Transport.open(db, remoteURI)) {
 			final String srcExpr = Q.name();
 			final boolean forceUpdate = false;
 			final String localName = null;
@@ -154,8 +153,6 @@
 						+ "come back next year!", //
 						error.getMessage());
 			}
-		} finally {
-			t.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
index 727f9ba..4ff81c5 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -140,9 +140,8 @@
 		assertEquals("http", remoteURI.getScheme());
 
 		Map<String, Ref> map;
-		Transport t = Transport.open(dst, remoteURI);
+		try (Transport t = Transport.open(dst, remoteURI)) {
 		((TransportHttp) t).setUseSmartHttp(false);
-		try {
 			// I didn't make up these public interface names, I just
 			// approved them for inclusion into the code base. Sorry.
 			// --spearce
@@ -150,14 +149,9 @@
 			assertTrue("isa TransportHttp", t instanceof TransportHttp);
 			assertTrue("isa HttpTransport", t instanceof HttpTransport);
 
-			FetchConnection c = t.openFetch();
-			try {
+			try (FetchConnection c = t.openFetch()) {
 				map = c.getRefsMap();
-			} finally {
-				c.close();
 			}
-		} finally {
-			t.close();
 		}
 
 		assertNotNull("have map of refs", map);
@@ -201,12 +195,9 @@
 		Repository dst = createBareRepository();
 		assertFalse(dst.hasObject(A_txt));
 
-		Transport t = Transport.open(dst, remoteURI);
+		try (Transport t = Transport.open(dst, remoteURI)) {
 		((TransportHttp) t).setUseSmartHttp(false);
-		try {
 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
-		} finally {
-			t.close();
 		}
 
 		assertTrue(dst.hasObject(A_txt));
@@ -229,12 +220,9 @@
 		Repository dst = createBareRepository();
 		assertFalse(dst.hasObject(A_txt));
 
-		Transport t = Transport.open(dst, remoteURI);
-		((TransportHttp) t).setUseSmartHttp(false);
-		try {
+		try (Transport t = Transport.open(dst, remoteURI)) {
+			((TransportHttp) t).setUseSmartHttp(false);
 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
-		} finally {
-			t.close();
 		}
 
 		assertTrue(dst.hasObject(A_txt));
@@ -265,9 +253,8 @@
 		final RevCommit Q = src.commit().create();
 		final Repository db = src.getRepository();
 
-		Transport t = Transport.open(db, remoteURI);
-		((TransportHttp) t).setUseSmartHttp(false);
-		try {
+		try (Transport t = Transport.open(db, remoteURI)) {
+			((TransportHttp) t).setUseSmartHttp(false);
 			try {
 				t.push(NullProgressMonitor.INSTANCE, push(src, Q));
 				fail("push incorrectly completed against a smart server");
@@ -275,8 +262,6 @@
 				String exp = "smart HTTP push disabled";
 				assertEquals(exp, nse.getMessage());
 			}
-		} finally {
-			t.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
index de7891c..8dce98b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
@@ -176,7 +176,6 @@
 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
 		final Repository clientRepo = client.getRepository();
 		final String srvBranchName = Constants.R_HEADS + "new.branch";
-		Transport t;
 
 		maxPackSize = 0;
 		postHook = null;
@@ -188,8 +187,7 @@
 			}
 		};
 
-		t = Transport.open(clientRepo, srvURI);
-		try {
+		try (Transport t = Transport.open(clientRepo, srvURI)) {
 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
 					srvBranchName, false, null, null);
 			try {
@@ -199,8 +197,6 @@
 			} catch (Exception e) {
 				assertTrue(e instanceof TransportException);
 			}
-		} finally {
-			t.close();
 		}
 	}
 
@@ -218,7 +214,6 @@
 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
 		final Repository clientRepo = client.getRepository();
 		final String srvBranchName = Constants.R_HEADS + "new.branch";
-		Transport t;
 
 		maxPackSize = 0;
 		postHook = null;
@@ -231,8 +226,7 @@
 			}
 		};
 
-		t = Transport.open(clientRepo, srvURI);
-		try {
+		try (Transport t = Transport.open(clientRepo, srvURI)) {
 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
 					srvBranchName, false, null, null);
 			try {
@@ -242,8 +236,6 @@
 			} catch (Exception e) {
 				assertTrue(e instanceof TransportException);
 			}
-		} finally {
-			t.close();
 		}
 	}
 
@@ -266,7 +258,6 @@
 		final RevCommit Q = client.commit().add("Q", Q_txt).create();
 		final Repository clientRepo = client.getRepository();
 		final String srvBranchName = Constants.R_HEADS + "new.branch";
-		Transport t;
 
 		// this maxPackSize leads to an unPackError
 		maxPackSize = 100;
@@ -283,8 +274,7 @@
 			}
 		};
 
-		t = Transport.open(clientRepo, srvURI);
-		try {
+		try (Transport t = Transport.open(clientRepo, srvURI)) {
 			RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
 					srvBranchName, false, null, null);
 			try {
@@ -294,8 +284,6 @@
 			} catch (Exception e) {
 				assertTrue(e instanceof TooLargePackException);
 			}
-		} finally {
-			t.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index adb69ec..7795658 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -149,11 +149,9 @@
 		final RevCommit Q = src.commit().add("Q", Q_txt).create();
 		final Repository db = src.getRepository();
 		final String dstName = Constants.R_HEADS + "new.branch";
-		Transport t;
 		PushResult result;
 
-		t = Transport.open(db, remoteURI);
-		try {
+		try (Transport t = Transport.open(db, remoteURI)) {
 			final String srcExpr = Q.name();
 			final boolean forceUpdate = false;
 			final String localName = null;
@@ -163,8 +161,6 @@
 					srcExpr, dstName, forceUpdate, localName, oldId);
 			result = t.push(NullProgressMonitor.INSTANCE, Collections
 					.singleton(update));
-		} finally {
-			t.close();
 		}
 
 		assertTrue(remoteRepository.hasObject(Q_txt));
@@ -193,12 +189,10 @@
 		final RevCommit Q = src.commit().add("Q", Q_txt).create();
 		final Repository db = src.getRepository();
 		final String dstName = Constants.R_HEADS + "new.branch";
-		Transport t;
 		PushResult result;
 
-		t = Transport.open(db, remoteURI);
 		OutputStream out = new ByteArrayOutputStream();
-		try {
+		try (Transport t = Transport.open(db, remoteURI)) {
 			final String srcExpr = Q.name();
 			final boolean forceUpdate = false;
 			final String localName = null;
@@ -208,8 +202,6 @@
 					srcExpr, dstName, forceUpdate, localName, oldId);
 			result = t.push(NullProgressMonitor.INSTANCE,
 					Collections.singleton(update), out);
-		} finally {
-			t.close();
 		}
 
 		String expectedMessage = "message line 1\n" //
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
index 4c08ec2..0415bcb 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
@@ -144,11 +144,9 @@
 		final RevCommit Q = src.commit().add("Q", Q_txt).create();
 		final Repository db = src.getRepository();
 		final String dstName = Constants.R_HEADS + "new.branch";
-		Transport t;
 		PushResult result;
 
-		t = Transport.open(db, remoteURI);
-		try {
+		try (Transport t = Transport.open(db, remoteURI)) {
 			final String srcExpr = Q.name();
 			final boolean forceUpdate = false;
 			final String localName = null;
@@ -158,8 +156,6 @@
 					srcExpr, dstName, forceUpdate, localName, oldId);
 			result = t.push(NullProgressMonitor.INSTANCE,
 					Collections.singleton(update));
-		} finally {
-			t.close();
 		}
 		assertEquals("expected 1 RemoteUpdate", 1, result.getRemoteUpdates()
 				.size());
diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 966af78..1ece244 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.junit.http
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -22,16 +22,16 @@
  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.ssl;version="[9.4.5,10.0.0)",
- org.eclipse.jgit.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.http.server;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.junit;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.http.server;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.11.3,4.12.0)",
  org.junit;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="4.10.1";
+Export-Package: org.eclipse.jgit.junit.http;version="4.11.3";
   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 6fe484d..1e934cc 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
index 43181f2..c1abe37 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
@@ -59,79 +59,80 @@
 
 /** Logs request made through {@link AppServer}. */
 class TestRequestLog extends HandlerWrapper {
-  private static final int MAX = 16;
+	private static final int MAX = 16;
 
-  private final List<AccessEvent> events = new ArrayList<>();
+	private final List<AccessEvent> events = new ArrayList<>();
 
-  private final Semaphore active = new Semaphore(MAX);
+	private final Semaphore active = new Semaphore(MAX, true);
 
-  /** Reset the log back to its original empty state. */
-  void clear() {
-    try {
-      for (;;) {
-        try {
-          active.acquire(MAX);
-          break;
-        } catch (InterruptedException e) {
-          continue;
-        }
-      }
+	/** Reset the log back to its original empty state. */
+	void clear() {
+		try {
+			for (;;) {
+				try {
+					active.acquire(MAX);
+					break;
+				} catch (InterruptedException e) {
+					continue;
+				}
+			}
 
-      synchronized (events) {
-        events.clear();
-      }
-    } finally {
-      active.release(MAX);
-    }
-  }
+			synchronized (events) {
+				events.clear();
+			}
+		} finally {
+			active.release(MAX);
+		}
+	}
 
-  /** @return all of the events made since the last clear. */
-  List<AccessEvent> getEvents() {
-    try {
-      for (;;) {
-        try {
-          active.acquire(MAX);
-          break;
-        } catch (InterruptedException e) {
-          continue;
-        }
-      }
+	/** @return all of the events made since the last clear. */
+	List<AccessEvent> getEvents() {
+		try {
+			for (;;) {
+				try {
+					active.acquire(MAX);
+					break;
+				} catch (InterruptedException e) {
+					continue;
+				}
+			}
 
-      synchronized (events) {
-        return events;
-      }
-    } finally {
-      active.release(MAX);
-    }
-  }
+			synchronized (events) {
+				return events;
+			}
+		} finally {
+			active.release(MAX);
+		}
+	}
 
-  /** {@inheritDoc} */
-  @Override
-  public void handle(String target, Request baseRequest, HttpServletRequest request,
-      HttpServletResponse response) throws IOException, ServletException {
-    try {
-      for (;;) {
-        try {
-          active.acquire();
-          break;
-        } catch (InterruptedException e) {
-          continue;
-        }
-      }
+	/** {@inheritDoc} */
+	@Override
+	public void handle(String target, Request baseRequest,
+			HttpServletRequest request, HttpServletResponse response)
+			throws IOException, ServletException {
+		try {
+			for (;;) {
+				try {
+					active.acquire();
+					break;
+				} catch (InterruptedException e) {
+					continue;
+				}
+			}
 
-      super.handle(target, baseRequest, request, response);
+			super.handle(target, baseRequest, request, response);
 
-      if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
-        log((Request) request, (Response) response);
+			if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
+				log((Request) request, (Response) response);
 
-    } finally {
-      active.release();
-    }
-  }
+		} finally {
+			active.release();
+		}
+	}
 
-  private void log(Request request, Response response) {
-    synchronized (events) {
-      events.add(new AccessEvent(request, response));
-    }
-  }
+	private void log(Request request, Response response) {
+		synchronized (events) {
+			events.add(new AccessEvent(request, response));
+		}
+	}
 }
diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 1e60a90..73d1ab1 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,31 +3,31 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.junit
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.merge;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util.time;version="[4.10.1,4.11.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.api.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.dircache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.merge;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.io;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.time;version="[4.11.3,4.12.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)",
  org.junit.runners.model;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="4.10.1";
+Export-Package: org.eclipse.jgit.junit;version="4.11.3";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -36,4 +36,4 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.storage.file,
    org.eclipse.jgit.api",
- org.eclipse.jgit.junit.time;version="4.10.1"
+ org.eclipse.jgit.junit.time;version="4.11.3"
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index df94de6..4e0970f 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
index 4ba2606..cef81a0 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
@@ -45,6 +45,8 @@
 
 package org.eclipse.jgit.junit;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -180,18 +182,11 @@
 		URL url = cl().getResource(CLASSPATH_TO_RESOURCES + name);
 		if (url == null)
 			throw new FileNotFoundException(name);
-		InputStream in = url.openStream();
-		try {
-			FileOutputStream out = new FileOutputStream(dest);
-			try {
-				byte[] buf = new byte[4096];
-				for (int n; (n = in.read(buf)) > 0;)
-					out.write(buf, 0, n);
-			} finally {
-				out.close();
-			}
-		} finally {
-			in.close();
+		try (InputStream in = url.openStream();
+				FileOutputStream out = new FileOutputStream(dest)) {
+			byte[] buf = new byte[4096];
+			for (int n; (n = in.read(buf)) > 0;)
+				out.write(buf, 0, n);
 		}
 	}
 
@@ -250,11 +245,9 @@
 	public static void write(final File f, final String body)
 			throws IOException {
 		FileUtils.mkdirs(f.getParentFile(), true);
-		Writer w = new OutputStreamWriter(new FileOutputStream(f), "UTF-8");
-		try {
+		try (Writer w = new OutputStreamWriter(new FileOutputStream(f),
+				UTF_8)) {
 			w.write(body);
-		} finally {
-			w.close();
 		}
 	}
 
@@ -270,7 +263,7 @@
 	 */
 	public static String read(final File file) throws IOException {
 		final byte[] body = IO.readFully(file);
-		return new String(body, 0, body.length, "UTF-8");
+		return new String(body, 0, body.length, UTF_8);
 	}
 
 	/**
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 9dac11e..568bc3b 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -45,6 +45,7 @@
 
 package org.eclipse.jgit.junit;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
@@ -351,7 +352,7 @@
 			if (0 != (includedOptions & CONTENT)) {
 				sb.append(", content:"
 						+ new String(repo.open(entry.getObjectId(),
-						Constants.OBJ_BLOB).getCachedBytes(), "UTF-8"));
+						Constants.OBJ_BLOB).getCachedBytes(), UTF_8));
 			}
 			if (0 != (includedOptions & ASSUME_UNCHANGED))
 				sb.append(", assume-unchanged:"
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index 044f080..afc2c44 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -46,6 +46,7 @@
 
 package org.eclipse.jgit.junit;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 
 import java.io.File;
@@ -197,14 +198,14 @@
 	 */
 	protected static void checkFile(File f, final String checkData)
 			throws IOException {
-		Reader r = new InputStreamReader(new FileInputStream(f), "UTF-8");
-		try {
-			char[] data = new char[checkData.length()];
-			if (checkData.length() != r.read(data))
-				throw new IOException("Internal error reading file data from "+f);
-			assertEquals(checkData, new String(data));
-		} finally {
-			r.close();
+		try (Reader r = new InputStreamReader(new FileInputStream(f),
+				UTF_8)) {
+			if (checkData.length() > 0) {
+				char[] data = new char[checkData.length()];
+				assertEquals(data.length, r.read(data));
+				assertEquals(checkData, new String(data));
+			}
+			assertEquals(-1, r.read());
 		}
 	}
 
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
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 266a084..d6d887b 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -28,11 +28,24 @@
  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.10.1,4.11.0)",
- org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.test;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.api;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.api.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.server;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.test;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index 7aa399f..d089032 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/CheckoutTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/CheckoutTest.java
new file mode 100644
index 0000000..fb6225b
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/CheckoutTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016, Christian Halstrick <christian.halstrick@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.lfs.server.fs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lfs.BuiltinLFS;
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CheckoutTest extends LfsServerTest {
+
+	Git git;
+	private TestRepository tdb;
+
+	@Override
+	@Before
+	public void setup() throws Exception {
+		super.setup();
+
+		BuiltinLFS.register();
+
+		Path tmp = Files.createTempDirectory("jgit_test_");
+		Repository db = FileRepositoryBuilder
+				.create(tmp.resolve(".git").toFile());
+		db.create();
+		StoredConfig cfg = db.getConfig();
+		cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, true);
+		cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_REQUIRED, false);
+		cfg.setString(ConfigConstants.CONFIG_SECTION_LFS, null, "url",
+				server.getURI().toString() + "/lfs");
+		cfg.save();
+
+		tdb = new TestRepository<>(db);
+		tdb.branch("test").commit()
+				.add(".gitattributes",
+						"*.bin filter=lfs diff=lfs merge=lfs -text ")
+				.add("a.bin",
+						"version https://git-lfs.github.com/spec/v1\noid sha256:8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414\nsize 7\n")
+				.create();
+		git = Git.wrap(db);
+		tdb.branch("test2").commit().add(".gitattributes",
+				"*.bin filter=lfs diff=lfs merge=lfs -text ").create();
+	}
+
+	@After
+	public void cleanup() throws Exception {
+		tdb.getRepository().close();
+		FileUtils.delete(tdb.getRepository().getWorkTree(),
+				FileUtils.RECURSIVE);
+	}
+
+	@Test
+	public void testUnknownContent() throws Exception {
+		git.checkout().setName("test").call();
+		// unknown content. We will see the pointer file
+		assertEquals(
+				"version https://git-lfs.github.com/spec/v1\noid sha256:8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414\nsize 7\n",
+				JGitTestUtil.read(git.getRepository(), "a.bin"));
+		assertEquals("[POST /lfs/objects/batch 200]",
+				server.getRequests().toString());
+	}
+
+	@Test(expected = JGitInternalException.class)
+	public void testUnknownContentRequired() throws Exception {
+		StoredConfig cfg = tdb.getRepository().getConfig();
+		cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_REQUIRED, true);
+		cfg.save();
+
+		// must throw
+		git.checkout().setName("test").call();
+	}
+
+	@Test
+	public void testKnownContent() throws Exception {
+		putContent(
+				LongObjectId.fromString(
+						"8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414"),
+				"1234567");
+		git.checkout().setName("test").call();
+		// known content. we will see the actual content of the LFS blob.
+		assertEquals(
+				"1234567",
+				JGitTestUtil.read(git.getRepository(), "a.bin"));
+		assertEquals(
+				"[PUT /lfs/objects/8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 200"
+						+ ", POST /lfs/objects/batch 200"
+						+ ", GET /lfs/objects/8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 200]",
+				server.getRequests().toString());
+
+		git.checkout().setName("test2").call();
+		assertFalse(JGitTestUtil.check(git.getRepository(), "a.bin"));
+		git.checkout().setName("test").call();
+		// unknown content. We will see the pointer file
+		assertEquals("1234567",
+				JGitTestUtil.read(git.getRepository(), "a.bin"));
+		assertEquals(3, server.getRequests().size());
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
index 5da502e..90fe116 100644
--- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
@@ -75,9 +75,12 @@
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jgit.junit.http.AppServer;
+import org.eclipse.jgit.lfs.errors.LfsException;
 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
 import org.eclipse.jgit.lfs.lib.Constants;
 import org.eclipse.jgit.lfs.lib.LongObjectId;
+import org.eclipse.jgit.lfs.server.LargeFileRepository;
+import org.eclipse.jgit.lfs.server.LfsProtocolServlet;
 import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.IO;
@@ -122,7 +125,27 @@
 		this.repository = new FileLfsRepository(null, dir);
 		servlet = new FileLfsServlet(repository, timeout);
 		app.addServlet(new ServletHolder(servlet), "/objects/*");
+
+		LfsProtocolServlet protocol = new LfsProtocolServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected LargeFileRepository getLargeFileRepository(
+					LfsRequest request, String path) {
+				return repository;
+			}
+
+			@Override
+			protected LargeFileRepository getLargeFileRepository(
+					LfsRequest request, String path, String auth)
+					throws LfsException {
+				return repository;
+			}
+		};
+		app.addServlet(new ServletHolder(protocol), "/objects/batch");
+
 		server.setUp();
+		this.repository.setUrl(server.getURI() + "/lfs/objects/");
 	}
 
 	@After
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java
new file mode 100644
index 0000000..b081a8e
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.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.lfs.server.fs;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.RemoteAddCommand;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lfs.BuiltinLFS;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.IO;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PushTest extends LfsServerTest {
+
+	Git git;
+
+	private TestRepository localDb;
+
+	private Repository remoteDb;
+
+	@Override
+	@Before
+	public void setup() throws Exception {
+		super.setup();
+
+		BuiltinLFS.register();
+
+		Path rtmp = Files.createTempDirectory("jgit_test_");
+		remoteDb = FileRepositoryBuilder.create(rtmp.toFile());
+		remoteDb.create(true);
+
+		Path tmp = Files.createTempDirectory("jgit_test_");
+		Repository db = FileRepositoryBuilder
+				.create(tmp.resolve(".git").toFile());
+		db.create(false);
+		StoredConfig cfg = db.getConfig();
+		cfg.setString("filter", "lfs", "usejgitbuiltin", "true");
+		cfg.setString("lfs", null, "url", server.getURI().toString() + "/lfs");
+		cfg.save();
+
+		localDb = new TestRepository<>(db);
+		localDb.branch("master").commit().add(".gitattributes",
+				"*.bin filter=lfs diff=lfs merge=lfs -text ").create();
+		git = Git.wrap(db);
+
+		URIish uri = new URIish(
+				"file://" + remoteDb.getDirectory());
+		RemoteAddCommand radd = git.remoteAdd();
+		radd.setUri(uri);
+		radd.setName(Constants.DEFAULT_REMOTE_NAME);
+		radd.call();
+
+		git.checkout().setName("master").call();
+		git.push().call();
+	}
+
+	@After
+	public void cleanup() throws Exception {
+		remoteDb.close();
+		localDb.getRepository().close();
+		FileUtils.delete(localDb.getRepository().getWorkTree(),
+				FileUtils.RECURSIVE);
+		FileUtils.delete(remoteDb.getDirectory(), FileUtils.RECURSIVE);
+	}
+
+	@Test
+	public void testPushSimple() throws Exception {
+		JGitTestUtil.writeTrashFile(localDb.getRepository(), "a.bin",
+				"1234567");
+		git.add().addFilepattern("a.bin").call();
+		RevCommit commit = git.commit().setMessage("add lfs blob").call();
+		git.push().call();
+
+		// check object in remote db, should be LFS pointer
+		ObjectId id = commit.getId();
+		try (RevWalk walk = new RevWalk(remoteDb)) {
+			RevCommit rc = walk.parseCommit(id);
+			try (TreeWalk tw = new TreeWalk(walk.getObjectReader())) {
+				tw.addTree(rc.getTree());
+				tw.setFilter(PathFilter.create("a.bin"));
+				tw.next();
+
+				assertEquals(tw.getPathString(), "a.bin");
+				ObjectLoader ldr = walk.getObjectReader()
+						.open(tw.getObjectId(0), Constants.OBJ_BLOB);
+				try(InputStream is = ldr.openStream()) {
+					assertEquals(
+							"version https://git-lfs.github.com/spec/v1\noid sha256:8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414\nsize 7\n",
+							new String(IO
+									.readWholeStream(is,
+											(int) ldr.getSize())
+									.array()));
+				}
+			}
+
+		}
+
+		assertEquals(
+				"[POST /lfs/objects/batch 200, PUT /lfs/objects/8bb0cf6eb9b17d0f7d22b456f121257dc1254e1f01665370476383ea776df414 200]",
+				server.getRequests().toString());
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
index ede0f7d..89394ec 100644
--- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index ddd35a4..8c665d8 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -3,36 +3,36 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs.server
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.server;version="4.10.1";
+Export-Package: org.eclipse.jgit.lfs.server;version="4.11.3";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="4.10.1";
+ org.eclipse.jgit.lfs.server.fs;version="4.11.3";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="4.10.1";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="4.10.1";
+ org.eclipse.jgit.lfs.server.internal;version="4.11.3";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="4.11.3";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: com.google.gson;version="[2.2.4,3.0.0)",
+Import-Package: com.google.gson;version="[2.8.0,3.0.0)",
  javax.servlet;version="[3.1.0,4.0.0)",
  javax.servlet.annotation;version="[3.1.0,4.0.0)",
  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.10.1,4.11.0)",
- org.eclipse.jgit.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.annotations;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.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 351802d..59b6dc1 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
index 5b12be6..688aef2 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
@@ -67,7 +67,7 @@
  */
 public class FileLfsRepository implements LargeFileRepository {
 
-	private final String url;
+	private String url;
 	private final Path dir;
 
 	/**
@@ -179,4 +179,21 @@
 		while (o >= p)
 			dst[o--] = '0';
 	}
+
+	/**
+	 * @return the url of the content server
+	 * @since 4.11
+	 */
+	public String getUrl() {
+		return url;
+	}
+
+	/**
+	 * @param url
+	 *            the url of the content server
+	 * @since 4.11
+	 */
+	public void setUrl(String url) {
+		this.url = url;
+	}
 }
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java
index b805ef5..c86e15b 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java
@@ -183,10 +183,10 @@
 		}
 		rsp.reset();
 		rsp.setStatus(status);
-		PrintWriter writer = rsp.getWriter();
-		LfsGson.toJson(message, writer);
-		writer.flush();
-		writer.close();
+		try (PrintWriter writer = rsp.getWriter()) {
+			LfsGson.toJson(message, writer);
+			writer.flush();
+		}
 		rsp.flushBuffer();
 	}
 }
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 5ae0989..62f0356f 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,23 +3,23 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs.test
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.junit;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)",
  org.junit.runners;version="[4.12,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="4.10.1";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="4.11.3";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 6206ba0..8dfd261 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
index 2ace2e3..146a25e 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java
@@ -61,11 +61,12 @@
 		final String s = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
 		AnyLongObjectId id = LongObjectId.fromString(s);
 		LfsPointer ptr = new LfsPointer(id, 4);
-		ByteArrayOutputStream baos = new ByteArrayOutputStream();
-		ptr.encode(baos);
-		baos.close();
-		assertEquals("version https://git-lfs.github.com/spec/v1\noid sha256:"
-				+ s + "\nsize 4\n",
-				baos.toString(UTF_8.name()));
+		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+			ptr.encode(baos);
+			assertEquals(
+					"version https://git-lfs.github.com/spec/v1\noid sha256:"
+							+ s + "\nsize 4\n",
+					baos.toString(UTF_8.name()));
+		}
 	}
 }
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
index d6dd3aa..8642e7e 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
@@ -392,9 +392,10 @@
 	public void testCopyToWriter() throws IOException {
 		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
 		ByteArrayOutputStream os = new ByteArrayOutputStream(64);
-		OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET);
-		id1.copyTo(w);
-		w.close();
+		try (OutputStreamWriter w = new OutputStreamWriter(os,
+				Constants.CHARSET)) {
+			id1.copyTo(w);
+		}
 		assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0));
 	}
 
@@ -402,10 +403,11 @@
 	public void testCopyToWriterWithBuf() throws IOException {
 		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
 		ByteArrayOutputStream os = new ByteArrayOutputStream(64);
-		OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET);
-		char[] buf = new char[64];
-		id1.copyTo(buf, w);
-		w.close();
+		try (OutputStreamWriter w = new OutputStreamWriter(os,
+				Constants.CHARSET)) {
+			char[] buf = new char[64];
+			id1.copyTo(buf, w);
+		}
 		assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0));
 	}
 
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
index ede0f7d..89394ec 100644
--- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.lfs/BUILD b/org.eclipse.jgit.lfs/BUILD
index 0c7b1b2..cd291da 100644
--- a/org.eclipse.jgit.lfs/BUILD
+++ b/org.eclipse.jgit.lfs/BUILD
@@ -6,6 +6,7 @@
     resource_strip_prefix = "org.eclipse.jgit.lfs/resources",
     resources = glob(["resources/**"]),
     deps = [
+        "//lib:gson",
         "//org.eclipse.jgit:jgit",
     ],
 )
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index 7421587..ccb99bb 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,20 +3,33 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.lfs
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs;version="4.10.1",
- org.eclipse.jgit.lfs.errors;version="4.10.1",
- org.eclipse.jgit.lfs.internal;version="4.10.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.10.1"
+Export-Package: org.eclipse.jgit.lfs;version="4.11.3",
+ org.eclipse.jgit.lfs.errors;version="4.11.3",
+ org.eclipse.jgit.lfs.internal;version="4.11.3";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.11.3"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[4.10.1,4.11.0)";resolution:=optional,
- org.eclipse.jgit.attributes;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)"
+Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
+ com.google.gson.stream;version="[2.8.2,3.0.0)",
+ org.apache.http.impl.client;version="[4.2.6,5.0.0)",
+ org.apache.http.impl.conn;version="[4.2.6,5.0.0)",
+ org.eclipse.jgit.annotations;version="[4.11.3,4.12.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.attributes;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.diff;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.hooks;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.pack;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.io;version="[4.11.3,4.12.0)"
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 8585ccf..8209c18 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
@@ -70,6 +70,10 @@
       <artifactId>org.eclipse.jgit</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+    </dependency>
   </dependencies>
   <build>
     <sourceDirectory>src/</sourceDirectory>
diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
index e08e28c..0e00f14 100644
--- a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
+++ b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
@@ -1,11 +1,19 @@
 corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted.
 incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH.
-inconsistentMediafileLength=mediafile {0} has unexpected length; expected {1} but found {2}.
+inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}.
+inconsistentContentLength=Unexpected content length reported by LFS server ({0}), expected {1} but reported was {2}
 invalidLongId=Invalid id: {0}
 invalidLongIdLength=Invalid id length {0}; should be {1}
+lfsUnavailable=LFS is not available for repository {0}
+protocolError=LFS Protocol Error {0}: {1}
 requiredHashFunctionNotAvailable=Required hash function {0} not available.
 repositoryNotFound=Repository {0} not found
 repositoryReadOnly=Repository {0} is read-only
 lfsUnavailable=LFS is not available for repository {0}
 lfsUnathorized=Not authorized to perform operation {0} on repository {1}
 lfsFailedToGetRepository=failed to get repository {0}
+lfsNoDownloadUrl="Need to download object from LFS server but couldn't determine LFS server URL"
+serverFailure=When trying to open a connection to {0} the server responded with an error code. rc={1}
+wrongAmoutOfDataReceived=While downloading data from the content server {0} {1} bytes have been received while {2} have been expected
+userConfigInvalid="User config file {0} invalid {1}"
+missingLocalObject="Local Object {0} is missing"
\ No newline at end of file
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
new file mode 100644
index 0000000..415caa9
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.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.lfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.Attribute;
+import org.eclipse.jgit.hooks.PrePushHook;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.LfsFactory;
+
+/**
+ * Implementation of {@link LfsFactory}, using built-in (optional) LFS support.
+ *
+ * @since 4.11
+ */
+public class BuiltinLFS extends LfsFactory {
+
+	private BuiltinLFS() {
+		SmudgeFilter.register();
+		CleanFilter.register();
+	}
+
+	/**
+	 * Activates the built-in LFS support.
+	 */
+	public static void register() {
+		setInstance(new BuiltinLFS());
+	}
+
+	@Override
+	public boolean isAvailable() {
+		return true;
+	}
+
+	@Override
+	public ObjectLoader applySmudgeFilter(Repository db, ObjectLoader loader,
+			Attribute attribute) throws IOException {
+		if (isEnabled(db) && (attribute == null || isEnabled(db, attribute))) {
+			return LfsBlobFilter.smudgeLfsBlob(db, loader);
+		} else {
+			return loader;
+		}
+	}
+
+	@Override
+	public LfsInputStream applyCleanFilter(Repository db, InputStream input,
+			long length, Attribute attribute) throws IOException {
+		if (isEnabled(db, attribute)) {
+			return new LfsInputStream(LfsBlobFilter.cleanLfsBlob(db, input));
+		} else {
+			return new LfsInputStream(input, length);
+		}
+	}
+
+	@Override
+	public @Nullable PrePushHook getPrePushHook(Repository repo,
+			PrintStream outputStream) {
+		if (isEnabled(repo)) {
+			return new LfsPrePushHook(repo, outputStream);
+		}
+		return null;
+	}
+
+	/**
+	 * @param db
+	 *            the repository
+	 * @return whether LFS is requested for the given repo.
+	 */
+	@Override
+	public boolean isEnabled(Repository db) {
+		if (db == null) {
+			return false;
+		}
+		return db.getConfig().getBoolean(ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_USEJGITBUILTIN,
+				false);
+	}
+
+	/**
+	 * @param db
+	 *            the repository
+	 * @param attribute
+	 *            the attribute to check
+	 * @return whether LFS filter is enabled for the given .gitattribute
+	 *         attribute.
+	 */
+	private boolean isEnabled(Repository db, Attribute attribute) {
+		if (attribute == null) {
+			return false;
+		}
+		return isEnabled(db) && ConfigConstants.CONFIG_SECTION_LFS
+				.equals(attribute.getValue());
+	}
+
+	@Override
+	public LfsInstallCommand getInstallCommand() {
+		return new InstallBuiltinLfsCommand();
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
index 4a98286..217e46f 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
@@ -55,6 +55,7 @@
 import org.eclipse.jgit.lfs.errors.CorruptMediaFile;
 import org.eclipse.jgit.lfs.internal.AtomicObjectOutputStream;
 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FileUtils;
 
@@ -90,11 +91,12 @@
 	 * Registers this filter by calling
 	 * {@link FilterCommandRegistry#register(String, FilterCommandFactory)}
 	 */
-	public final static void register() {
-		FilterCommandRegistry.register(
-				org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
-						+ "lfs/clean", //$NON-NLS-1$
-				FACTORY);
+	static void register() {
+		FilterCommandRegistry
+				.register(org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
+						+ Constants.ATTR_FILTER_DRIVER_PREFIX
+						+ org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_CLEAN,
+						FACTORY);
 	}
 
 	// Used to compute the hash for the original content
@@ -127,7 +129,7 @@
 	public CleanFilter(Repository db, InputStream in, OutputStream out)
 			throws IOException {
 		super(in, out);
-		lfsUtil = new Lfs(FileUtils.toPath(db.getDirectory()).resolve("lfs")); //$NON-NLS-1$
+		lfsUtil = new Lfs(db);
 		Files.createDirectories(lfsUtil.getLfsTmpDir());
 		tmpFile = lfsUtil.createTmpFile();
 		this.aOut = new AtomicObjectOutputStream(tmpFile.toAbsolutePath());
@@ -165,6 +167,7 @@
 				}
 				LfsPointer lfsPointer = new LfsPointer(loid, size);
 				lfsPointer.encode(out);
+				in.close();
 				out.close();
 				return -1;
 			}
@@ -172,6 +175,7 @@
 			if (aOut != null) {
 				aOut.abort();
 			}
+			in.close();
 			out.close();
 			throw e;
 		}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/InstallBuiltinLfsCommand.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/InstallBuiltinLfsCommand.java
new file mode 100644
index 0000000..028b19b
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/InstallBuiltinLfsCommand.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.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.lfs;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lfs.internal.LfsText;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.LfsFactory.LfsInstallCommand;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Installs all required LFS properties for the current user, analogous to 'git
+ * lfs install', but defaulting to using JGit builtin hooks.
+ *
+ * @since 4.11
+ */
+public class InstallBuiltinLfsCommand implements LfsInstallCommand {
+
+	private static final String[] ARGS_USER = new String[] { "lfs", "install" }; //$NON-NLS-1$//$NON-NLS-2$
+
+	private static final String[] ARGS_LOCAL = new String[] { "lfs", "install", //$NON-NLS-1$//$NON-NLS-2$
+			"--local" }; //$NON-NLS-1$
+
+	private Repository repository;
+
+	/** {@inheritDoc} */
+	@Override
+	public Void call() throws Exception {
+		StoredConfig cfg = null;
+		if (repository == null) {
+			cfg = loadUserConfig();
+		} else {
+			cfg = repository.getConfig();
+		}
+
+		cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, true);
+		cfg.setBoolean(ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_REQUIRED, true);
+
+		cfg.save();
+
+		// try to run git lfs install, we really don't care if it is present
+		// and/or works here (yet).
+		ProcessBuilder builder = FS.DETECTED.runInShell("git", //$NON-NLS-1$
+				repository == null ? ARGS_USER : ARGS_LOCAL);
+		if (repository != null) {
+			builder.directory(repository.isBare() ? repository.getDirectory()
+					: repository.getWorkTree());
+		}
+		FS.DETECTED.runProcess(builder, null, null, (String) null);
+
+		return null;
+	}
+
+	/**
+	 * Set the repository to install LFS for
+	 *
+	 * @param repo
+	 *            the repository to install LFS into locally instead of the user
+	 *            configuration
+	 * @return this command
+	 */
+	@Override
+	public LfsInstallCommand setRepository(Repository repo) {
+		this.repository = repo;
+		return this;
+	}
+
+	private StoredConfig loadUserConfig() throws IOException {
+		FileBasedConfig c = SystemReader.getInstance().openUserConfig(null,
+				FS.DETECTED);
+		try {
+			c.load();
+		} catch (ConfigInvalidException e1) {
+			throw new IOException(MessageFormat
+					.format(LfsText.get().userConfigInvalid, c.getFile()
+							.getAbsolutePath(), e1),
+					e1);
+		}
+
+		return c;
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java
index 138996d..40d8342 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java
@@ -47,6 +47,8 @@
 import java.nio.file.Path;
 
 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
 
 /**
  * Class which represents the lfs folder hierarchy inside a {@code .git} folder
@@ -66,12 +68,26 @@
 	 * @param root
 	 *            the path to the LFS media directory. Will be
 	 *            {@code "<repo>/.git/lfs"}
+	 * @deprecated use {@link #Lfs(Repository)} instead.
 	 */
+	@Deprecated
 	public Lfs(Path root) {
 		this.root = root;
 	}
 
 	/**
+	 * Constructor for Lfs.
+	 *
+	 * @param db
+	 *            the associated repo
+	 *
+	 * @since 4.11
+	 */
+	public Lfs(Repository db) {
+		this.root = db.getDirectory().toPath().resolve(Constants.LFS);
+	}
+
+	/**
 	 * Get the LFS root directory
 	 *
 	 * @return the path to the LFS directory
@@ -118,7 +134,7 @@
 	public Path getMediaFile(AnyLongObjectId id) {
 		String idStr = id.name();
 		return getLfsObjDir().resolve(idStr.substring(0, 2))
-				.resolve(idStr.substring(2));
+				.resolve(idStr.substring(2, 4)).resolve(idStr);
 	}
 
 	/**
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobFilter.java
new file mode 100644
index 0000000..a7d218f
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobFilter.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.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.lfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
+
+/**
+ * Provides transparently either a stream to the blob or a LFS media file if
+ * managed by LFS.
+ *
+ * @since 4.11
+ */
+public class LfsBlobFilter {
+
+	/**
+	 * In case the given {@link ObjectLoader} points to a LFS pointer file
+	 * replace the loader with one pointing to the LFS media file contents.
+	 * Missing LFS files are downloaded on the fly - same logic as the smudge
+	 * filter.
+	 *
+	 * @param db
+	 *            the repo
+	 * @param loader
+	 *            the loader for the blob
+	 * @return either the original loader, or a loader for the LFS media file if
+	 *         managed by LFS. Files are downloaded on demand if required.
+	 * @throws IOException
+	 *             in case of an error
+	 */
+	public static ObjectLoader smudgeLfsBlob(Repository db, ObjectLoader loader)
+			throws IOException {
+		if (loader.getSize() > LfsPointer.SIZE_THRESHOLD) {
+			return loader;
+		}
+
+		try (InputStream is = loader.openStream()) {
+			LfsPointer ptr = LfsPointer.parseLfsPointer(is);
+			if (ptr != null) {
+				Lfs lfs = new Lfs(db);
+				AnyLongObjectId oid = ptr.getOid();
+				Path mediaFile = lfs.getMediaFile(oid);
+				if (!Files.exists(mediaFile)) {
+					SmudgeFilter.downloadLfsResource(lfs, db, ptr);
+				}
+
+				return new LfsBlobLoader(mediaFile);
+			}
+		}
+
+		return loader;
+	}
+
+	/**
+	 * Run the LFS clean filter on the given stream and return a stream to the
+	 * LFS pointer file buffer. Used when inserting objects.
+	 *
+	 * @param db
+	 *            the {@link Repository}
+	 * @param originalContent
+	 *            the {@link InputStream} to the original content
+	 * @return a {@link TemporaryBuffer} representing the LFS pointer. The
+	 *         caller is responsible to destroy the buffer.
+	 * @throws IOException
+	 *             in case of any error.
+	 */
+	public static TemporaryBuffer cleanLfsBlob(Repository db,
+			InputStream originalContent) throws IOException {
+		LocalFile buffer = new TemporaryBuffer.LocalFile(null);
+		CleanFilter f = new CleanFilter(db, originalContent, buffer);
+		try {
+			while (f.run() != -1) {
+				// loop as long as f.run() tells there is work to do
+			}
+		} catch (IOException e) {
+			buffer.destroy();
+			throw e;
+		}
+		return buffer;
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobLoader.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobLoader.java
new file mode 100644
index 0000000..697ae47
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsBlobLoader.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.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.lfs;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * An {@link ObjectLoader} implementation that reads a media file from the LFS
+ * storage.
+ *
+ * @since 4.11
+ */
+public class LfsBlobLoader extends ObjectLoader {
+
+	private Path mediaFile;
+
+	private BasicFileAttributes attributes;
+
+	private byte[] cached;
+
+	/**
+	 * Create a loader for the LFS media file at the given path.
+	 *
+	 * @param mediaFile
+	 *            path to the file
+	 * @throws IOException
+	 *             in case of an error reading attributes
+	 */
+	public LfsBlobLoader(Path mediaFile) throws IOException {
+		this.mediaFile = mediaFile;
+		this.attributes = Files.readAttributes(mediaFile,
+				BasicFileAttributes.class);
+	}
+
+	@Override
+	public int getType() {
+		return Constants.OBJ_BLOB;
+	}
+
+	@Override
+	public long getSize() {
+		return attributes.size();
+	}
+
+	@Override
+	public byte[] getCachedBytes() throws LargeObjectException {
+		if (getSize() > PackConfig.DEFAULT_BIG_FILE_THRESHOLD) {
+			throw new LargeObjectException();
+		}
+
+		if (cached == null) {
+			try {
+				cached = IO.readFully(mediaFile.toFile());
+			} catch (IOException ioe) {
+				throw new LargeObjectException(ioe);
+			}
+		}
+		return cached;
+	}
+
+	@Override
+	public ObjectStream openStream()
+			throws MissingObjectException, IOException {
+		return new ObjectStream.Filter(getType(), getSize(),
+				Files.newInputStream(mediaFile));
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
index 3604531..0e3830c 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
@@ -64,7 +64,7 @@
  *
  * @since 4.6
  */
-public class LfsPointer {
+public class LfsPointer implements Comparable<LfsPointer> {
 	/**
 	 * The version of the LfsPointer file format
 	 */
@@ -77,6 +77,13 @@
 	public static final String VERSION_LEGACY = "https://hawser.github.com/spec/v1"; //$NON-NLS-1$
 
 	/**
+	 * Don't inspect files that are larger than this threshold to avoid
+	 * excessive reading. No pointer file should be larger than this.
+	 * @since 4.11
+	 */
+	public static final int SIZE_THRESHOLD = 200;
+
+	/**
 	 * The name of the hash function as used in the pointer files. This will
 	 * evaluate to "sha256"
 	 */
@@ -185,5 +192,18 @@
 		return "LfsPointer: oid=" + oid.name() + ", size=" //$NON-NLS-1$ //$NON-NLS-2$
 				+ size;
 	}
+
+	/**
+	 * @since 4.11
+	 */
+	@Override
+	public int compareTo(LfsPointer o) {
+		int x = getOid().compareTo(o.getOid());
+		if (x != 0) {
+			return x;
+		}
+
+		return (int) (getSize() - o.getSize());
+	}
 }
 
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java
new file mode 100644
index 0000000..6115e39
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPrePushHook.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.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.lfs;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lfs.Protocol.OPERATION_UPLOAD;
+import static org.eclipse.jgit.lfs.internal.LfsConnectionFactory.toRequest;
+import static org.eclipse.jgit.transport.http.HttpConnection.HTTP_OK;
+import static org.eclipse.jgit.util.HttpSupport.METHOD_POST;
+import static org.eclipse.jgit.util.HttpSupport.METHOD_PUT;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.jgit.api.errors.AbortedByHookException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.hooks.PrePushHook;
+import org.eclipse.jgit.lfs.Protocol.ObjectInfo;
+import org.eclipse.jgit.lfs.errors.CorruptMediaFile;
+import org.eclipse.jgit.lfs.internal.LfsConnectionFactory;
+import org.eclipse.jgit.lfs.internal.LfsText;
+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.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.http.HttpConnection;
+
+import com.google.gson.Gson;
+import com.google.gson.stream.JsonReader;
+
+/**
+ * Pre-push hook that handles uploading LFS artefacts.
+ *
+ * @since 4.11
+ */
+public class LfsPrePushHook extends PrePushHook {
+
+	private static final String EMPTY = ""; //$NON-NLS-1$
+	private Collection<RemoteRefUpdate> refs;
+
+	/**
+	 * @param repo
+	 *            the repository
+	 * @param outputStream
+	 *            not used by this implementation
+	 */
+	public LfsPrePushHook(Repository repo, PrintStream outputStream) {
+		super(repo, outputStream);
+	}
+
+	@Override
+	public void setRefs(Collection<RemoteRefUpdate> toRefs) {
+		this.refs = toRefs;
+	}
+
+	@Override
+	public String call() throws IOException, AbortedByHookException {
+		Set<LfsPointer> toPush = findObjectsToPush();
+		if (toPush.isEmpty()) {
+			return EMPTY;
+		}
+		HttpConnection api = LfsConnectionFactory.getLfsConnection(
+				getRepository(), METHOD_POST, OPERATION_UPLOAD);
+		Map<String, LfsPointer> oid2ptr = requestBatchUpload(api, toPush);
+		uploadContents(api, oid2ptr);
+		return EMPTY;
+
+	}
+
+	private Set<LfsPointer> findObjectsToPush() throws IOException,
+			MissingObjectException, IncorrectObjectTypeException {
+		Set<LfsPointer> toPush = new TreeSet<>();
+
+		try (ObjectWalk walk = new ObjectWalk(getRepository())) {
+			for (RemoteRefUpdate up : refs) {
+				walk.setRewriteParents(false);
+				excludeRemoteRefs(walk);
+				walk.markStart(walk.parseCommit(up.getNewObjectId()));
+				while (walk.next() != null) {
+					// walk all commits to populate objects
+				}
+				findLfsPointers(toPush, walk);
+			}
+		}
+		return toPush;
+	}
+
+	private static void findLfsPointers(Set<LfsPointer> toPush, ObjectWalk walk)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException {
+		RevObject obj;
+		ObjectReader r = walk.getObjectReader();
+		while ((obj = walk.nextObject()) != null) {
+			if (obj.getType() == Constants.OBJ_BLOB
+					&& getObjectSize(r, obj) < LfsPointer.SIZE_THRESHOLD) {
+				LfsPointer ptr = loadLfsPointer(r, obj);
+				if (ptr != null) {
+					toPush.add(ptr);
+				}
+			}
+		}
+	}
+
+	private static long getObjectSize(ObjectReader r, RevObject obj)
+			throws IOException {
+		return r.getObjectSize(obj.getId(), Constants.OBJ_BLOB);
+	}
+
+	private static LfsPointer loadLfsPointer(ObjectReader r, AnyObjectId obj)
+			throws IOException {
+		try (InputStream is = r.open(obj, Constants.OBJ_BLOB).openStream()) {
+			return LfsPointer.parseLfsPointer(is);
+		}
+	}
+
+	private void excludeRemoteRefs(ObjectWalk walk) throws IOException {
+		RefDatabase refDatabase = getRepository().getRefDatabase();
+		Map<String, Ref> remoteRefs = refDatabase.getRefs(remote());
+		for (Ref r : remoteRefs.values()) {
+			ObjectId oid = r.getPeeledObjectId();
+			if (oid == null) {
+				oid = r.getObjectId();
+			}
+			if (oid == null) {
+				// ignore (e.g. symbolic, ...)
+				continue;
+			}
+			RevObject o = walk.parseAny(oid);
+			if (o.getType() == Constants.OBJ_COMMIT
+					|| o.getType() == Constants.OBJ_TAG) {
+				walk.markUninteresting(o);
+			}
+		}
+	}
+
+	private String remote() {
+		String remoteName = getRemoteName() == null
+				? Constants.DEFAULT_REMOTE_NAME
+				: getRemoteName();
+		return Constants.R_REMOTES + remoteName;
+	}
+
+	private Map<String, LfsPointer> requestBatchUpload(HttpConnection api,
+			Set<LfsPointer> toPush) throws IOException {
+		LfsPointer[] res = toPush.toArray(new LfsPointer[toPush.size()]);
+		Map<String, LfsPointer> oidStr2ptr = new HashMap<>();
+		for (LfsPointer p : res) {
+			oidStr2ptr.put(p.getOid().name(), p);
+		}
+		Gson gson = Protocol.gson();
+		api.getOutputStream().write(
+				gson.toJson(toRequest(OPERATION_UPLOAD, res)).getBytes(UTF_8));
+		int responseCode = api.getResponseCode();
+		if (responseCode != HTTP_OK) {
+			throw new IOException(
+					MessageFormat.format(LfsText.get().serverFailure,
+							api.getURL(), Integer.valueOf(responseCode)));
+		}
+		return oidStr2ptr;
+	}
+
+	private void uploadContents(HttpConnection api,
+			Map<String, LfsPointer> oid2ptr) throws IOException {
+		try (JsonReader reader = new JsonReader(
+				new InputStreamReader(api.getInputStream()))) {
+			for (Protocol.ObjectInfo o : parseObjects(reader)) {
+				if (o.actions == null) {
+					continue;
+				}
+				LfsPointer ptr = oid2ptr.get(o.oid);
+				if (ptr == null) {
+					// received an object we didn't request
+					continue;
+				}
+				Protocol.Action uploadAction = o.actions.get(OPERATION_UPLOAD);
+				if (uploadAction == null || uploadAction.href == null) {
+					continue;
+				}
+
+				Lfs lfs = new Lfs(getRepository());
+				Path path = lfs.getMediaFile(ptr.getOid());
+				if (!Files.exists(path)) {
+					throw new IOException(MessageFormat
+							.format(LfsText.get().missingLocalObject, path));
+				}
+				uploadFile(o, uploadAction, path);
+			}
+		}
+	}
+
+	private List<ObjectInfo> parseObjects(JsonReader reader) {
+		Gson gson = new Gson();
+		Protocol.Response resp = gson.fromJson(reader, Protocol.Response.class);
+		return resp.objects;
+	}
+
+	private void uploadFile(Protocol.ObjectInfo o,
+			Protocol.Action uploadAction, Path path)
+			throws IOException, CorruptMediaFile {
+		HttpConnection contentServer = LfsConnectionFactory
+				.getLfsContentConnection(getRepository(), uploadAction,
+						METHOD_PUT);
+		contentServer.setDoOutput(true);
+		try (OutputStream out = contentServer
+				.getOutputStream()) {
+			long size = Files.copy(path, out);
+			if (size != o.size) {
+				throw new CorruptMediaFile(path, o.size, size);
+			}
+		}
+		int responseCode = contentServer.getResponseCode();
+		if (responseCode != HTTP_OK) {
+			throw new IOException(MessageFormat.format(
+					LfsText.get().serverFailure, contentServer.getURL(),
+					Integer.valueOf(responseCode)));
+		}
+	}
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Protocol.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Protocol.java
new file mode 100644
index 0000000..d88742e
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Protocol.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016, Christian Halstrick <christian.halstrick@sap.com>
+ * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@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.lfs;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * This interface describes the network protocol used between lfs client and lfs
+ * server
+ *
+ * @since 4.11
+ */
+public interface Protocol {
+	/** A request sent to an LFS server */
+	class Request {
+		/** The operation of this request */
+		public String operation;
+
+		/** The objects of this request */
+		public List<ObjectSpec> objects;
+	}
+
+	/** A response received from an LFS server */
+	class Response {
+		public List<ObjectInfo> objects;
+	}
+
+	/**
+	 * MetaData of an LFS object. Needs to be specified when requesting objects
+	 * from the LFS server and is also returned in the response
+	 */
+	class ObjectSpec {
+		public String oid; // the objectid
+
+		public long size; // the size of the object
+	}
+
+	/**
+	 * Describes in a response all actions the LFS server offers for a single
+	 * object
+	 */
+	class ObjectInfo extends ObjectSpec {
+		public Map<String, Action> actions; // Maps operation to action
+
+		public Error error;
+	}
+
+	/**
+	 * Describes in a Response a single action the client can execute on a
+	 * single object
+	 */
+	class Action {
+		public String href;
+
+		public Map<String, String> header;
+	}
+
+	/**
+	 * An action with an additional expiration timestamp
+	 *
+	 * @since 4.11
+	 */
+	class ExpiringAction extends Action {
+		/**
+		 * Absolute date/time in format "yyyy-MM-dd'T'HH:mm:ss.SSSX"
+		 */
+		public String expiresAt;
+
+		/**
+		 * Validity time in milliseconds (preferred over expiresAt as specified:
+		 * https://github.com/git-lfs/git-lfs/blob/master/docs/api/authentication.md)
+		 */
+		public String expiresIn;
+	}
+
+	/** Describes an error to be returned by the LFS batch API */
+	class Error {
+		public int code;
+
+		public String message;
+	}
+
+	/**
+	 * The "download" operation
+	 */
+	String OPERATION_DOWNLOAD = "download"; //$NON-NLS-1$
+
+	/**
+	 * The "upload" operation
+	 */
+	String OPERATION_UPLOAD = "upload"; //$NON-NLS-1$
+
+	/**
+	 * The contenttype used in LFS requests
+	 */
+	String CONTENTTYPE_VND_GIT_LFS_JSON = "application/vnd.git-lfs+json; charset=utf-8"; //$NON-NLS-1$
+
+	/**
+	 * Authorization header when auto-discovering via SSH.
+	 */
+	String HDR_AUTH = "Authorization"; //$NON-NLS-1$
+
+	/**
+	 * Prefix of authentication token obtained through SSH.
+	 */
+	String HDR_AUTH_SSH_PREFIX = "Ssh: "; //$NON-NLS-1$
+
+	/**
+	 * Path to the LFS info servlet.
+	 */
+	String INFO_LFS_ENDPOINT = "/info/lfs"; //$NON-NLS-1$
+
+	/**
+	 * Path to the LFS objects servlet.
+	 */
+	String OBJECTS_LFS_ENDPOINT = "/objects/batch"; //$NON-NLS-1$
+
+	/**
+	 * @return a {@link Gson} instance suitable for handling this
+	 *         {@link Protocol}
+	 *
+	 * @since 4.11
+	 */
+	public static Gson gson() {
+		return new GsonBuilder()
+				.setFieldNamingPolicy(
+						FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+				.disableHtmlEscaping().create();
+	}
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
index 941edec..e2ab309 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
@@ -44,16 +44,30 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.eclipse.jgit.attributes.FilterCommand;
 import org.eclipse.jgit.attributes.FilterCommandFactory;
 import org.eclipse.jgit.attributes.FilterCommandRegistry;
+import org.eclipse.jgit.lfs.internal.LfsConnectionFactory;
+import org.eclipse.jgit.lfs.internal.LfsText;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
 import org.eclipse.jgit.lfs.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.transport.http.HttpConnection;
+import org.eclipse.jgit.util.HttpSupport;
+
+import com.google.gson.Gson;
+import com.google.gson.stream.JsonReader;
 
 /**
  * Built-in LFS smudge filter
@@ -62,13 +76,17 @@
  * and this filter is configured for that content, then this filter will replace
  * the content of LFS pointer files with the original content. This happens e.g.
  * when a checkout needs to update a working tree file which is under LFS
- * control. This implementation expects that the origin content is already
- * available in the .git/lfs/objects folder. This implementation will not
- * contact any LFS servers in order to get the missing content.
+ * control.
  *
  * @since 4.6
  */
 public class SmudgeFilter extends FilterCommand {
+
+	/**
+	 * Max number of bytes to copy in a single {@link #run()} call.
+	 */
+	private static final int MAX_COPY_BYTES = 1024 * 1024 * 256;
+
 	/**
 	 * The factory is responsible for creating instances of
 	 * {@link org.eclipse.jgit.lfs.SmudgeFilter}
@@ -82,52 +100,180 @@
 	};
 
 	/**
-	 * Registers this filter in JGit by calling
+	 * Register this filter in JGit
 	 */
-	public final static void register() {
-		FilterCommandRegistry.register(
-				org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
-						+ "lfs/smudge", //$NON-NLS-1$
-				FACTORY);
+	static void register() {
+		FilterCommandRegistry
+				.register(org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
+						+ Constants.ATTR_FILTER_DRIVER_PREFIX
+						+ org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE,
+						FACTORY);
 	}
 
-	private Lfs lfs;
-
 	/**
 	 * Constructor for SmudgeFilter.
 	 *
 	 * @param db
 	 *            a {@link org.eclipse.jgit.lib.Repository} object.
 	 * @param in
-	 *            a {@link java.io.InputStream} object.
+	 *            a {@link java.io.InputStream} object. The stream is closed in
+	 *            any case.
 	 * @param out
 	 *            a {@link java.io.OutputStream} object.
 	 * @throws java.io.IOException
+	 *             in case of an error
 	 */
 	public SmudgeFilter(Repository db, InputStream in, OutputStream out)
 			throws IOException {
 		super(in, out);
-		lfs = new Lfs(FileUtils.toPath(db.getDirectory()).resolve(Constants.LFS));
-		LfsPointer res = LfsPointer.parseLfsPointer(in);
-		if (res != null) {
-			Path mediaFile = lfs.getMediaFile(res.getOid());
-			if (Files.exists(mediaFile)) {
+		try {
+			Lfs lfs = new Lfs(db);
+			LfsPointer res = LfsPointer.parseLfsPointer(in);
+			if (res != null) {
+				AnyLongObjectId oid = res.getOid();
+				Path mediaFile = lfs.getMediaFile(oid);
+				if (!Files.exists(mediaFile)) {
+					downloadLfsResource(lfs, db, res);
+				}
 				this.in = Files.newInputStream(mediaFile);
 			}
+		} finally {
+			in.close(); // make sure the swapped stream is closed properly.
 		}
 	}
 
+	/**
+	 * Download content which is hosted on a LFS server
+	 *
+	 * @param lfs
+	 *            local {@link Lfs} storage.
+	 * @param db
+	 *            the repository to work with
+	 * @param res
+	 *            the objects to download
+	 * @return the paths of all mediafiles which have been downloaded
+	 * @throws IOException
+	 * @since 4.11
+	 */
+	public static Collection<Path> downloadLfsResource(Lfs lfs, Repository db,
+			LfsPointer... res) throws IOException {
+		Collection<Path> downloadedPaths = new ArrayList<>();
+		Map<String, LfsPointer> oidStr2ptr = new HashMap<>();
+		for (LfsPointer p : res) {
+			oidStr2ptr.put(p.getOid().name(), p);
+		}
+		HttpConnection lfsServerConn = LfsConnectionFactory.getLfsConnection(db,
+				HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD);
+		Gson gson = Protocol.gson();
+		lfsServerConn.getOutputStream()
+				.write(gson
+						.toJson(LfsConnectionFactory
+								.toRequest(Protocol.OPERATION_DOWNLOAD, res))
+						.getBytes(StandardCharsets.UTF_8));
+		int responseCode = lfsServerConn.getResponseCode();
+		if (responseCode != HttpConnection.HTTP_OK) {
+			throw new IOException(
+					MessageFormat.format(LfsText.get().serverFailure,
+							lfsServerConn.getURL(),
+							Integer.valueOf(responseCode)));
+		}
+		try (JsonReader reader = new JsonReader(
+				new InputStreamReader(lfsServerConn.getInputStream()))) {
+			Protocol.Response resp = gson.fromJson(reader,
+					Protocol.Response.class);
+			for (Protocol.ObjectInfo o : resp.objects) {
+				if (o.error != null) {
+					throw new IOException(
+							MessageFormat.format(LfsText.get().protocolError,
+									Integer.valueOf(o.error.code),
+									o.error.message));
+				}
+				if (o.actions == null) {
+					continue;
+				}
+				LfsPointer ptr = oidStr2ptr.get(o.oid);
+				if (ptr == null) {
+					// received an object we didn't request
+					continue;
+				}
+				if (ptr.getSize() != o.size) {
+					throw new IOException(MessageFormat.format(
+							LfsText.get().inconsistentContentLength,
+							lfsServerConn.getURL(), Long.valueOf(ptr.getSize()),
+							Long.valueOf(o.size)));
+				}
+				Protocol.Action downloadAction = o.actions
+						.get(Protocol.OPERATION_DOWNLOAD);
+				if (downloadAction == null || downloadAction.href == null) {
+					continue;
+				}
+
+				HttpConnection contentServerConn = LfsConnectionFactory
+						.getLfsContentConnection(db, downloadAction,
+								HttpSupport.METHOD_GET);
+
+				responseCode = contentServerConn.getResponseCode();
+				if (responseCode != HttpConnection.HTTP_OK) {
+					throw new IOException(
+							MessageFormat.format(LfsText.get().serverFailure,
+									contentServerConn.getURL(),
+									Integer.valueOf(responseCode)));
+				}
+				Path path = lfs.getMediaFile(ptr.getOid());
+				path.getParent().toFile().mkdirs();
+				try (InputStream contentIn = contentServerConn
+						.getInputStream()) {
+					long bytesCopied = Files.copy(contentIn, path);
+					if (bytesCopied != o.size) {
+						throw new IOException(MessageFormat.format(
+								LfsText.get().wrongAmoutOfDataReceived,
+								contentServerConn.getURL(),
+								Long.valueOf(bytesCopied),
+								Long.valueOf(o.size)));
+					}
+					downloadedPaths.add(path);
+				}
+			}
+		}
+		return downloadedPaths;
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public int run() throws IOException {
-		int b;
-		if (in != null) {
-			while ((b = in.read()) != -1) {
-				out.write(b);
+		try {
+			int totalRead = 0;
+			int length = 0;
+			if (in != null) {
+				byte[] buf = new byte[8192];
+				while ((length = in.read(buf)) != -1) {
+					out.write(buf, 0, length);
+					totalRead += length;
+
+					// when threshold reached, loop back to the caller.
+					// otherwise we could only support files up to 2GB (int
+					// return type) properly. we will be called again as long as
+					// we don't return -1 here.
+					if (totalRead >= MAX_COPY_BYTES) {
+						// leave streams open - we need them in the next call.
+						return totalRead;
+					}
+				}
 			}
-			in.close();
+
+			if (totalRead == 0 && length == -1) {
+				// we're totally done :) cleanup all streams
+				in.close();
+				out.close();
+				return length;
+			}
+
+			return totalRead;
+		} catch (IOException e) {
+			in.close(); // clean up - we swapped this stream.
+			out.close();
+			throw e;
 		}
-		out.close();
-		return -1;
 	}
+
 }
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsConfigInvalidException.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsConfigInvalidException.java
new file mode 100644
index 0000000..5320af0
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsConfigInvalidException.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016, Christian Halstrick <christian.halstrick@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.lfs.errors;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a LFS configuration problem has been detected (i.e. unable to
+ * find the remote LFS repository URL).
+ *
+ * @since 4.11
+ */
+public class LfsConfigInvalidException extends IOException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructor for LfsConfigInvalidException.
+	 *
+	 * @param msg
+	 *            the error description
+	 */
+	public LfsConfigInvalidException(String msg) {
+		super(msg);
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
new file mode 100644
index 0000000..5d8268b
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2017, Markus Duft <markus.duft@ssi-schaefer.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.lfs.internal;
+
+import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
+import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
+import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ProxySelector;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.lfs.LfsPointer;
+import org.eclipse.jgit.lfs.Protocol;
+import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.HttpConfig;
+import org.eclipse.jgit.transport.HttpTransport;
+import org.eclipse.jgit.transport.RemoteSession;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.http.HttpConnection;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.HttpSupport;
+import org.eclipse.jgit.util.io.MessageWriter;
+import org.eclipse.jgit.util.io.StreamCopyThread;
+
+/**
+ * Provides means to get a valid LFS connection for a given repository.
+ */
+public class LfsConnectionFactory {
+
+	private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$
+	private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$
+	private static final Map<String, AuthCache> sshAuthCache = new TreeMap<>();
+
+	/**
+	 * Determine URL of LFS server by looking into config parameters lfs.url,
+	 * lfs.[remote].url or remote.[remote].url. The LFS server URL is computed
+	 * from remote.[remote].url by appending "/info/lfs". In case there is no
+	 * URL configured, a SSH remote URI can be used to auto-detect the LFS URI
+	 * by using the remote "git-lfs-authenticate" command.
+	 *
+	 * @param db
+	 *            the repository to work with
+	 * @param method
+	 *            the method (GET,PUT,...) of the request this connection will
+	 *            be used for
+	 * @param purpose
+	 *            the action, e.g. Protocol.OPERATION_DOWNLOAD
+	 * @return the url for the lfs server. e.g.
+	 *         "https://github.com/github/git-lfs.git/info/lfs"
+	 * @throws IOException
+	 */
+	public static HttpConnection getLfsConnection(Repository db, String method,
+			String purpose) throws IOException {
+		StoredConfig config = db.getConfig();
+		Map<String, String> additionalHeaders = new TreeMap<>();
+		String lfsUrl = getLfsUrl(db, purpose, additionalHeaders);
+		URL url = new URL(lfsUrl + Protocol.OBJECTS_LFS_ENDPOINT);
+		HttpConnection connection = HttpTransport.getConnectionFactory().create(
+				url, HttpSupport.proxyFor(ProxySelector.getDefault(), url));
+		connection.setDoOutput(true);
+		if (url.getProtocol().equals(SCHEME_HTTPS)
+				&& !config.getBoolean(HttpConfig.HTTP,
+						HttpConfig.SSL_VERIFY_KEY, true)) {
+			HttpSupport.disableSslVerify(connection);
+		}
+		connection.setRequestMethod(method);
+		connection.setRequestProperty(HDR_ACCEPT,
+				Protocol.CONTENTTYPE_VND_GIT_LFS_JSON);
+		connection.setRequestProperty(HDR_CONTENT_TYPE,
+				Protocol.CONTENTTYPE_VND_GIT_LFS_JSON);
+		additionalHeaders
+				.forEach((k, v) -> connection.setRequestProperty(k, v));
+		return connection;
+	}
+
+	private static String getLfsUrl(Repository db, String purpose,
+			Map<String, String> additionalHeaders)
+			throws LfsConfigInvalidException {
+		StoredConfig config = db.getConfig();
+		String lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS,
+				null,
+				ConfigConstants.CONFIG_KEY_URL);
+		if (lfsUrl == null) {
+			String remoteUrl = null;
+			for (String remote : db.getRemoteNames()) {
+				lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS,
+						remote,
+						ConfigConstants.CONFIG_KEY_URL);
+				// This could be done better (more precise logic), but according
+				// to https://github.com/git-lfs/git-lfs/issues/1759 git-lfs
+				// generally only supports 'origin' in an integrated workflow.
+				if (lfsUrl == null && (remote.equals(
+						org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME))) {
+					remoteUrl = config.getString(
+							ConfigConstants.CONFIG_KEY_REMOTE, remote,
+							ConfigConstants.CONFIG_KEY_URL);
+				}
+				break;
+			}
+			if (lfsUrl == null && remoteUrl != null) {
+				lfsUrl = discoverLfsUrl(db, purpose, additionalHeaders,
+						remoteUrl);
+			} else {
+				lfsUrl = lfsUrl + Protocol.INFO_LFS_ENDPOINT;
+			}
+		}
+		if (lfsUrl == null) {
+			throw new LfsConfigInvalidException(LfsText.get().lfsNoDownloadUrl);
+		}
+		return lfsUrl;
+	}
+
+	private static String discoverLfsUrl(Repository db, String purpose,
+			Map<String, String> additionalHeaders, String remoteUrl) {
+		try {
+			URIish u = new URIish(remoteUrl);
+			if (SCHEME_SSH.equals(u.getScheme())) {
+				Protocol.ExpiringAction action = getSshAuthentication(
+						db, purpose, remoteUrl, u);
+				additionalHeaders.putAll(action.header);
+				return action.href;
+			} else {
+				return remoteUrl + Protocol.INFO_LFS_ENDPOINT;
+			}
+		} catch (Exception e) {
+			return null; // could not discover
+		}
+	}
+
+	private static Protocol.ExpiringAction getSshAuthentication(
+			Repository db, String purpose, String remoteUrl, URIish u)
+			throws IOException {
+		AuthCache cached = sshAuthCache.get(remoteUrl);
+		Protocol.ExpiringAction action = null;
+		if (cached != null && cached.validUntil > System.currentTimeMillis()) {
+			action = cached.cachedAction;
+		}
+
+		if (action == null) {
+			// discover and authenticate; git-lfs does "ssh
+			// -p <port> -- <host> git-lfs-authenticate
+			// <project> <upload/download>"
+			String json = runSshCommand(u.setPath(""), //$NON-NLS-1$
+					db.getFS(),
+					"git-lfs-authenticate " + extractProjectName(u) + " " //$NON-NLS-1$//$NON-NLS-2$
+							+ purpose);
+
+			action = Protocol.gson().fromJson(json,
+					Protocol.ExpiringAction.class);
+
+			// cache the result as long as possible.
+			AuthCache c = new AuthCache(action);
+			sshAuthCache.put(remoteUrl, c);
+		}
+		return action;
+	}
+
+	/**
+	 * Create a connection for the specified
+	 * {@link org.eclipse.jgit.lfs.Protocol.Action}.
+	 *
+	 * @param repo
+	 *            the repo to fetch required configuration from
+	 * @param action
+	 *            the action for which to create a connection
+	 * @param method
+	 *            the target method (GET or PUT)
+	 * @return a connection. output mode is not set.
+	 * @throws IOException
+	 *             in case of any error.
+	 */
+	public static @NonNull HttpConnection getLfsContentConnection(
+			Repository repo, Protocol.Action action, String method)
+			throws IOException {
+		URL contentUrl = new URL(action.href);
+		HttpConnection contentServerConn = HttpTransport.getConnectionFactory()
+				.create(contentUrl, HttpSupport
+						.proxyFor(ProxySelector.getDefault(), contentUrl));
+		contentServerConn.setRequestMethod(method);
+		action.header
+				.forEach((k, v) -> contentServerConn.setRequestProperty(k, v));
+		if (contentUrl.getProtocol().equals(SCHEME_HTTPS)
+				&& !repo.getConfig().getBoolean(HttpConfig.HTTP,
+						HttpConfig.SSL_VERIFY_KEY, true)) {
+			HttpSupport.disableSslVerify(contentServerConn);
+		}
+
+		contentServerConn.setRequestProperty(HDR_ACCEPT_ENCODING,
+				ENCODING_GZIP);
+
+		return contentServerConn;
+	}
+
+	private static String extractProjectName(URIish u) {
+		String path = u.getPath().substring(1);
+		if (path.endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT)) {
+			return path.substring(0, path.length() - 4);
+		} else {
+			return path;
+		}
+	}
+
+	private static String runSshCommand(URIish sshUri, FS fs, String command)
+			throws IOException {
+		RemoteSession session = null;
+		Process process = null;
+		StreamCopyThread errorThread = null;
+		try (MessageWriter stderr = new MessageWriter()) {
+			session = SshSessionFactory.getInstance().getSession(sshUri, null,
+					fs, 5_000);
+			process = session.exec(command, 0);
+			errorThread = new StreamCopyThread(process.getErrorStream(),
+					stderr.getRawStream());
+			errorThread.start();
+			try (BufferedReader reader = new BufferedReader(
+					new InputStreamReader(process.getInputStream(),
+							org.eclipse.jgit.lib.Constants.CHARSET))) {
+				return reader.readLine();
+			}
+		} finally {
+			if (process != null) {
+				process.destroy();
+			}
+			if (errorThread != null) {
+				try {
+					errorThread.halt();
+				} catch (InterruptedException e) {
+					// Stop waiting and return anyway.
+				} finally {
+					errorThread = null;
+				}
+			}
+			if (session != null) {
+				SshSessionFactory.getInstance().releaseSession(session);
+			}
+		}
+	}
+
+	/**
+	 * @param operation
+	 *            the operation to perform, e.g. Protocol.OPERATION_DOWNLOAD
+	 * @param resources
+	 *            the LFS resources affected
+	 * @return a request that can be serialized to JSON
+	 */
+	public static Protocol.Request toRequest(String operation,
+			LfsPointer... resources) {
+		Protocol.Request req = new Protocol.Request();
+		req.operation = operation;
+		if (resources != null) {
+			req.objects = new LinkedList<>();
+			for (LfsPointer res : resources) {
+				Protocol.ObjectSpec o = new Protocol.ObjectSpec();
+				o.oid = res.getOid().getName();
+				o.size = res.getSize();
+				req.objects.add(o);
+			}
+		}
+		return req;
+	}
+
+	private static final class AuthCache {
+		private static final long AUTH_CACHE_EAGER_TIMEOUT = 100;
+
+		private static final SimpleDateFormat ISO_FORMAT = new SimpleDateFormat(
+				"yyyy-MM-dd'T'HH:mm:ss.SSSX"); //$NON-NLS-1$
+
+		/**
+		 * Creates a cache entry for an authentication response.
+		 * <p>
+		 * The timeout of the cache token is extracted from the given action. If
+		 * no timeout can be determined, the token will be used only once.
+		 *
+		 * @param action
+		 */
+		public AuthCache(Protocol.ExpiringAction action) {
+			this.cachedAction = action;
+			try {
+				if (action.expiresIn != null && !action.expiresIn.isEmpty()) {
+					this.validUntil = System.currentTimeMillis()
+							+ Long.parseLong(action.expiresIn);
+				} else if (action.expiresAt != null
+						&& !action.expiresAt.isEmpty()) {
+					this.validUntil = ISO_FORMAT.parse(action.expiresAt)
+							.getTime() - AUTH_CACHE_EAGER_TIMEOUT;
+				} else {
+					this.validUntil = System.currentTimeMillis();
+				}
+			} catch (Exception e) {
+				this.validUntil = System.currentTimeMillis();
+			}
+		}
+
+		long validUntil;
+
+		Protocol.ExpiringAction cachedAction;
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
index ae5548c..d7d0fe1 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
@@ -62,13 +62,20 @@
 	// @formatter:off
 	/***/ public String corruptLongObject;
 	/***/ public String inconsistentMediafileLength;
+	/***/ public String inconsistentContentLength;
 	/***/ public String incorrectLONG_OBJECT_ID_LENGTH;
 	/***/ public String invalidLongId;
 	/***/ public String invalidLongIdLength;
+	/***/ public String lfsUnavailable;
+	/***/ public String protocolError;
 	/***/ public String requiredHashFunctionNotAvailable;
 	/***/ public String repositoryNotFound;
 	/***/ public String repositoryReadOnly;
-	/***/ public String lfsUnavailable;
 	/***/ public String lfsUnathorized;
 	/***/ public String lfsFailedToGetRepository;
+	/***/ public String lfsNoDownloadUrl;
+	/***/ public String serverFailure;
+	/***/ public String wrongAmoutOfDataReceived;
+	/***/ public String userConfigInvalid;
+	/***/ public String missingLocalObject;
 }
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
index d5b96ab..835d7be 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
@@ -56,7 +56,7 @@
 @SuppressWarnings("nls")
 public final class Constants {
 	/**
-	 * lfs folder
+	 * lfs folder/section/filter name
 	 *
 	 * @since 4.6
 	 */
@@ -108,6 +108,13 @@
 	public static final String VERIFY = "verify";
 
 	/**
+	 * Prefix for all LFS related filters.
+	 *
+	 * @since 4.11
+	 */
+	public static final String ATTR_FILTER_DRIVER_PREFIX = "lfs/";
+
+	/**
 	 * Create a new digest function for objects.
 	 *
 	 * @return a new digest object.
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 e3f229a..5b52f33 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.10.1.qualifier"
+      version="4.11.3.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -79,4 +79,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="com.jcraft.jzlib"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index da36437..0d500e5 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 0d67440..516eae7 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.10.1.qualifier"
+      version="4.11.3.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 06ce041..a70a9f3 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 0664991..a05fcbb 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.10.1.qualifier"
+      version="4.11.3.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 b0dc93c..f1f0323 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 0cd35e0..d5ef294 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.10.1.qualifier"
+      version="4.11.3.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 ee9b28f..061209b 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 7434e2c..0420997 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.10.1.qualifier"
+      version="4.11.3.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.10.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="4.10.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="4.11.3" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="4.11.3" 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 1695b26..dd7d6a4 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 14c2c1c..21ee5bb 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.10.1.qualifier"
+      version="4.11.3.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 073ae32..1e783b6 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 e8ddf11..8ef0fc3 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 66daef8..1dac220 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.10.1.qualifier"
+      version="4.11.3.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 fe3818a..770adf8 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 b850297..5081a9e 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.10.1.qualifier
+Bundle-Version: 4.11.3.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 e3d9e4d..0c1d5bb 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,7 +1,7 @@
 <?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="1512295767">
+<target name="jgit-4.5" sequenceNumber="1535176347">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/>
@@ -27,42 +27,45 @@
       <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
       <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/>
       <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/>
-      <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/>
-      <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
+      <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
+      <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
       <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
       <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.6.v20170210-0925"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20170210-0925"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20170210-0925"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20180410-1551"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20180410-1551"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
       <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
       <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
       <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v201505072020"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v201505072020"/>
+      <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+      <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
       <unit id="javaewah" version="1.1.6.v20160919-1400"/>
       <unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
       <unit id="org.objenesis" version="1.0.0.v201505121915"/>
       <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
       <unit id="org.mockito" version="1.8.4.v201303031500"/>
       <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
-      <unit id="com.google.gson" version="2.2.4.v201311231704"/>
+      <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
+      <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
       <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
       <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
       <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
-      <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
-      <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
+      <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/>
+      <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/>
       <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
       <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/downloads/drops/R20170919201930/repository"/>
+      <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+      <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/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 75101f6..c85343c 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,7 +1,7 @@
 target "jgit-4.5" with source configurePhase
 
 include "projects/jetty-9.4.8.tpd"
-include "orbit/R20170919201930-Oxygen.tpd"
+include "orbit/R20180606145124-Photon.tpd"
 
 location "http://download.eclipse.org/releases/mars/" {
 	org.eclipse.osgi lazy
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 4c79441..e05c410 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,7 +1,7 @@
 <?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="1512295762">
+<target name="jgit-4.6" sequenceNumber="1535176335">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/>
@@ -27,42 +27,45 @@
       <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
       <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/>
       <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/>
-      <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/>
-      <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
+      <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
+      <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
       <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
       <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.6.v20170210-0925"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20170210-0925"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20170210-0925"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20180410-1551"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20180410-1551"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
       <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
       <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
       <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v201505072020"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v201505072020"/>
+      <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+      <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
       <unit id="javaewah" version="1.1.6.v20160919-1400"/>
       <unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
       <unit id="org.objenesis" version="1.0.0.v201505121915"/>
       <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
       <unit id="org.mockito" version="1.8.4.v201303031500"/>
       <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
-      <unit id="com.google.gson" version="2.2.4.v201311231704"/>
+      <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
+      <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
       <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
       <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
       <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
-      <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
-      <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
+      <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/>
+      <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/>
       <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
       <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/downloads/drops/R20170919201930/repository"/>
+      <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+      <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/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 7de5870..3c2a910 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,7 +1,7 @@
 target "jgit-4.6" with source configurePhase
 
 include "projects/jetty-9.4.8.tpd"
-include "orbit/R20170919201930-Oxygen.tpd"
+include "orbit/R20180606145124-Photon.tpd"
 
 location "http://download.eclipse.org/releases/neon/" {
 	org.eclipse.osgi lazy
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 9b1c15b..20b0a29 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,7 +1,7 @@
 <?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="1512295741">
+<target name="jgit-4.7" sequenceNumber="1535176314">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/>
@@ -27,42 +27,45 @@
       <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
       <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/>
       <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/>
-      <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/>
-      <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
+      <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
+      <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
       <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
       <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.6.v20170210-0925"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20170210-0925"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20170210-0925"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.5.2.v20180410-1551"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.2.v20180410-1551"/>
       <unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
       <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
       <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/>
       <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/>
-      <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/>
-      <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/>
-      <unit id="org.hamcrest.library" version="1.3.0.v201505072020"/>
-      <unit id="org.hamcrest.library.source" version="1.3.0.v201505072020"/>
+      <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/>
+      <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/>
+      <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/>
       <unit id="javaewah" version="1.1.6.v20160919-1400"/>
       <unit id="javaewah.source" version="1.1.6.v20160919-1400"/>
       <unit id="org.objenesis" version="1.0.0.v201505121915"/>
       <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
       <unit id="org.mockito" version="1.8.4.v201303031500"/>
       <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
-      <unit id="com.google.gson" version="2.2.4.v201311231704"/>
+      <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
+      <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
       <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
       <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
       <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
-      <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
-      <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
+      <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/>
+      <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/>
       <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
       <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/downloads/drops/R20170919201930/repository"/>
+      <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+      <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/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 1def86c..4e543ff 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,7 +1,7 @@
 target "jgit-4.7" with source configurePhase
 
 include "projects/jetty-9.4.8.tpd"
-include "orbit/R20170919201930-Oxygen.tpd"
+include "orbit/R20180606145124-Photon.tpd"
 
 location "http://download.eclipse.org/releases/oxygen/" {
 	org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
index 5180e41..b84fc20 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.8" sequenceNumber="1535124646">
+<target name="jgit-4.8" sequenceNumber="1535176301">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.4.8.v20171121"/>
@@ -27,8 +27,8 @@
       <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/>
       <unit id="org.apache.commons.codec" version="1.9.0.v20170208-1614"/>
       <unit id="org.apache.commons.codec.source" version="1.9.0.v20170208-1614"/>
-      <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/>
-      <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
+      <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/>
+      <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/>
       <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
       <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.4.6.v20170210-0925"/>
@@ -49,19 +49,22 @@
       <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
       <unit id="org.mockito" version="1.8.4.v201303031500"/>
       <unit id="org.mockito.source" version="1.8.4.v201303031500"/>
-      <unit id="com.google.gson" version="2.2.4.v201311231704"/>
+      <unit id="com.google.gson" version="2.8.2.v20180104-1110"/>
+      <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/>
       <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/>
       <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/>
       <unit id="org.junit" version="4.12.0.v201504281640"/>
       <unit id="org.junit.source" version="4.12.0.v201504281640"/>
       <unit id="javax.servlet" version="3.1.0.v201410161800"/>
       <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
-      <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
-      <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
+      <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/>
+      <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/>
       <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
       <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"/>
+      <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/>
+      <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/>
       <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180606145124/repository"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170919201930-Oxygen.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180206163158-Oxygen.tpd
similarity index 92%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170919201930-Oxygen.tpd
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180206163158-Oxygen.tpd
index 4e9cc13..91c66db 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20170919201930-Oxygen.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180206163158-Oxygen.tpd
@@ -1,7 +1,7 @@
-target "R20170919201930-Oxygen" with source configurePhase
+target "R20180206163158-Oxygen" with source configurePhase
 // see http://download.eclipse.org/tools/orbit/downloads/
 
-location "http://download.eclipse.org/tools/orbit/downloads/drops/R20170919201930/repository" {
+location "http://download.eclipse.org/tools/orbit/downloads/drops/R20180206163158/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.codec [1.9.0.v20170208-1614,1.9.0.v20170208-1614]
@@ -41,4 +41,6 @@
 	org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
 	org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
 	org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
+	com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+	com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
 }
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd
index 86c888a..f4ec50d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20180606145124-Photon.tpd
@@ -6,8 +6,8 @@
 	org.apache.ant.source [1.9.6.v201510161327,1.9.6.v201510161327]
 	org.apache.commons.codec [1.9.0.v20170208-1614,1.9.0.v20170208-1614]
 	org.apache.commons.codec.source [1.9.0.v20170208-1614,1.9.0.v20170208-1614]
-	org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400]
-	org.apache.commons.compress.source [1.6.0.v201310281400,1.6.0.v201310281400]
+	org.apache.commons.compress [1.15.0.v20180119-1613,1.15.0.v20180119-1613]
+	org.apache.commons.compress.source [1.15.0.v20180119-1613,1.15.0.v20180119-1613s]
 	org.apache.commons.logging [1.1.1.v201101211721,1.1.1.v201101211721]
 	org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721]
 	org.apache.httpcomponents.httpcore [4.4.6.v20170210-0925,4.4.6.v20170210-0925]
@@ -28,17 +28,20 @@
 	org.objenesis.source [1.0.0.v201505121915,1.0.0.v201505121915]
 	org.mockito [1.8.4.v201303031500,1.8.4.v201303031500]
 	org.mockito.source [1.8.4.v201303031500,1.8.4.v201303031500]
-	com.google.gson [2.2.4.v201311231704,2.2.4.v201311231704]
+	com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
+	com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
 	com.jcraft.jsch [0.1.54.v20170116-1932,0.1.54.v20170116-1932]
 	com.jcraft.jsch.source [0.1.54.v20170116-1932,0.1.54.v20170116-1932]
 	org.junit [4.12.0.v201504281640,4.12.0.v201504281640]
 	org.junit.source [4.12.0.v201504281640,4.12.0.v201504281640]
 	javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
 	javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
-	org.tukaani.xz [1.3.0.v201308270617,1.3.0.v201308270617]
-	org.tukaani.xz.source [1.3.0.v201308270617,1.3.0.v201308270617]
+	org.tukaani.xz [1.6.0.v20170629-1752,1.6.0.v20170629-1752]
+	org.tukaani.xz.source [1.6.0.v20170629-1752,1.6.0.v20170629-1752]
 	org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
 	org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
 	org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
 	org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
+	com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+	com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
 }
\ No newline at end of file
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 2d26e79..1bf9f08 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index e12a212..fb0b1d7 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,13 +53,13 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>4.10.1-SNAPSHOT</version>
+  <version>4.11.3-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
 
   <properties>
-    <tycho-version>1.0.0</tycho-version>
+    <tycho-version>1.1.0</tycho-version>
     <tycho-extras-version>${tycho-version}</tycho-extras-version>
     <target-platform>jgit-4.6</target-platform>
   </properties>
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 5518017..aca0d33 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,28 +3,28 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.pgm.test
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.diff;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="4.10.1",
- org.eclipse.jgit.junit;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.merge;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.pgm;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.pgm.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.pgm.opt;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.api.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.diff;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.dircache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.11.3",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.merge;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.pgm;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.io;version="[4.11.3,4.12.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index f4d6171..08b6370 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
@@ -84,6 +84,11 @@
       <version>${project.version}</version>
     </dependency>
 
+    <dependency>
+      <groupId>org.tukaani</groupId>
+      <artifactId>xz</artifactId>
+      <optional>true</optional>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
index 3b352ce..e5c85eb 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
@@ -42,6 +42,7 @@
  */
 package org.eclipse.jgit.pgm;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
@@ -611,7 +612,7 @@
 
 	private BufferedReader readFromProcess(Process proc) throws Exception {
 		return new BufferedReader(
-				new InputStreamReader(proc.getInputStream(), "UTF-8"));
+				new InputStreamReader(proc.getInputStream(), UTF_8));
 	}
 
 	private void grepForEntry(String name, String mode, String... cmdline)
@@ -633,9 +634,8 @@
 	}
 
 	private void assertMagic(long offset, byte[] magicBytes, File file) throws Exception {
-		BufferedInputStream in = new BufferedInputStream(
-				new FileInputStream(file));
-		try {
+		try (BufferedInputStream in = new BufferedInputStream(
+				new FileInputStream(file))) {
 			if (offset > 0) {
 				long skipped = in.skip(offset);
 				assertEquals(offset, skipped);
@@ -644,8 +644,6 @@
 			byte[] actual = new byte[magicBytes.length];
 			in.read(actual);
 			assertArrayEquals(magicBytes, actual);
-		} finally {
-			in.close();
 		}
 	}
 
@@ -686,23 +684,19 @@
 	private void writeRaw(String filename, byte[] data)
 			throws IOException {
 		File path = new File(db.getWorkTree(), filename);
-		OutputStream out = new FileOutputStream(path);
-		try {
+		try (OutputStream out = new FileOutputStream(path)) {
 			out.write(data);
-		} finally {
-			out.close();
 		}
 	}
 
 	private static String[] listZipEntries(byte[] zipData) throws IOException {
 		List<String> l = new ArrayList<>();
-		ZipInputStream in = new ZipInputStream(
-				new ByteArrayInputStream(zipData));
-
-		ZipEntry e;
-		while ((e = in.getNextEntry()) != null)
-			l.add(e.getName());
-		in.close();
+		try (ZipInputStream in = new ZipInputStream(
+				new ByteArrayInputStream(zipData))) {
+			ZipEntry e;
+			while ((e = in.getNextEntry()) != null)
+				l.add(e.getName());
+		}
 		return l.toArray(new String[l.size()]);
 	}
 
@@ -725,22 +719,22 @@
 	private String[] listTarEntries(byte[] tarData) throws Exception {
 		List<String> l = new ArrayList<>();
 		Process proc = spawnAssumingCommandPresent("tar", "tf", "-");
-		BufferedReader reader = readFromProcess(proc);
-		OutputStream out = proc.getOutputStream();
+		try (BufferedReader reader = readFromProcess(proc)) {
+			OutputStream out = proc.getOutputStream();
 
-		// Dump tarball to tar stdin in background
-		Future<?> writing = writeAsync(out, tarData);
+			// Dump tarball to tar stdin in background
+			Future<?> writing = writeAsync(out, tarData);
 
-		try {
-			String line;
-			while ((line = reader.readLine()) != null)
-				l.add(line);
+			try {
+				String line;
+				while ((line = reader.readLine()) != null)
+					l.add(line);
 
-			return l.toArray(new String[l.size()]);
-		} finally {
-			writing.get();
-			reader.close();
-			proc.destroy();
+				return l.toArray(new String[l.size()]);
+			} finally {
+				writing.get();
+				proc.destroy();
+			}
 		}
 	}
 
@@ -756,7 +750,7 @@
 			// found!
 			List<String> l = new ArrayList<>();
 			BufferedReader reader = new BufferedReader(
-					new InputStreamReader(in, "UTF-8"));
+					new InputStreamReader(in, UTF_8));
 			String line;
 			while ((line = reader.readLine()) != null)
 				l.add(line);
@@ -771,20 +765,20 @@
 			throws Exception {
 		List<String> l = new ArrayList<>();
 		Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path);
-		BufferedReader reader = readFromProcess(proc);
-		OutputStream out = proc.getOutputStream();
-		Future<?> writing = writeAsync(out, tarData);
+		try (BufferedReader reader = readFromProcess(proc)) {
+			OutputStream out = proc.getOutputStream();
+			Future<?> writing = writeAsync(out, tarData);
 
-		try {
-			String line;
-			while ((line = reader.readLine()) != null)
-				l.add(line);
+			try {
+				String line;
+				while ((line = reader.readLine()) != null)
+					l.add(line);
 
-			return l.toArray(new String[l.size()]);
-		} finally {
-			writing.get();
-			reader.close();
-			proc.destroy();
+				return l.toArray(new String[l.size()]);
+			} finally {
+				writing.get();
+				proc.destroy();
+			}
 		}
 	}
 }
diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
index 06ddbab..13c32a6 100644
--- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index d1e2a3f..17d3c40 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.pgm
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-Localization: plugin
@@ -28,49 +28,49 @@
  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.10.1,4.11.0)",
- org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.archive;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.awtui;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.blame;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.diff;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.gitrepo;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.ketch;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.io;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.server;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs.server.s3;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.merge;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.notes;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revplot;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.pack;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.api;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.api.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.archive;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.awtui;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.blame;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.diff;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.dircache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.gitrepo;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.ketch;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.io;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.server;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.merge;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.notes;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revplot;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.pack;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.io;version="[4.11.3,4.12.0)",
  org.kohsuke.args4j;version="[2.33.0,3.0.0)",
  org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)"
-Export-Package: org.eclipse.jgit.console;version="4.10.1";
+Export-Package: org.eclipse.jgit.console;version="4.11.3";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="4.10.1";
+ org.eclipse.jgit.pgm;version="4.11.3";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.pgm.opt,
@@ -81,11 +81,11 @@
    org.eclipse.jgit.treewalk,
    javax.swing,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="4.10.1";
+ org.eclipse.jgit.pgm.debug;version="4.11.3";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="4.10.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="4.10.1";
+ org.eclipse.jgit.pgm.internal;version="4.11.3";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.11.3";
   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 7d8d4ba..ff59edb 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.10.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.10.1.qualifier";roots="."
+Bundle-Version: 4.11.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.11.3.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 165733e..280a5c5 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java
index 086c976..cce889b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AmazonS3Client.java
@@ -89,8 +89,7 @@
 		if ("get".equals(op)) { //$NON-NLS-1$
 			final URLConnection c = s3.get(bucket, key);
 			int len = c.getContentLength();
-			final InputStream in = c.getInputStream();
-			try {
+			try (InputStream in = c.getInputStream()) {
 				outw.flush();
 				final byte[] tmp = new byte[2048];
 				while (len > 0) {
@@ -103,8 +102,6 @@
 					len -= n;
 				}
 				outs.flush();
-			} finally {
-				in.close();
 			}
 
 		} else if ("ls".equals(op) || "list".equals(op)) { //$NON-NLS-1$//$NON-NLS-2$
@@ -115,13 +112,12 @@
 			s3.delete(bucket, key);
 
 		} else if ("put".equals(op)) { //$NON-NLS-1$
-			final OutputStream os = s3.beginPut(bucket, key, null, null);
-			final byte[] tmp = new byte[2048];
-			int n;
-			while ((n = ins.read(tmp)) > 0)
-				os.write(tmp, 0, n);
-			os.close();
-
+			try (OutputStream os = s3.beginPut(bucket, key, null, null)) {
+				final byte[] tmp = new byte[2048];
+				int n;
+				while ((n = ins.read(tmp)) > 0)
+					os.write(tmp, 0, n);
+			}
 		} else {
 			throw die(MessageFormat.format(CLIText.get().unsupportedOperation, op));
 		}
@@ -129,13 +125,10 @@
 
 	private Properties properties() {
 		try {
-			final InputStream in = new FileInputStream(propertyFile);
-			try {
+			try (InputStream in = new FileInputStream(propertyFile)) {
 				final Properties p = new Properties();
 				p.load(in);
 				return p;
-			} finally {
-				in.close();
 			}
 		} catch (FileNotFoundException e) {
 			throw die(MessageFormat.format(CLIText.get().noSuchFile, propertyFile), e);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 1f80301..6ff39fa 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -57,6 +57,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.TextProgressMonitor;
 import org.eclipse.jgit.pgm.internal.CLIText;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
@@ -90,7 +91,8 @@
 		}
 
 		try (Git git = new Git(db)) {
-			CheckoutCommand command = git.checkout();
+			CheckoutCommand command = git.checkout()
+					.setProgressMonitor(new TextProgressMonitor(errw));
 			if (paths.size() > 0) {
 				command.setStartPoint(name);
 				if (paths.size() == 1 && paths.get(0).equals(".")) { //$NON-NLS-1$
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
index 7bab692..81aeef8 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
@@ -43,6 +43,8 @@
 
 package org.eclipse.jgit.pgm;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -149,7 +151,7 @@
 		final BufferedReader cIn;
 		try {
 			final InputStream in = cUrl.openStream();
-			cIn = new BufferedReader(new InputStreamReader(in, "UTF-8")); //$NON-NLS-1$
+			cIn = new BufferedReader(new InputStreamReader(in, UTF_8));
 		} catch (IOException err) {
 			// If we cannot read from the service list, go to the next.
 			//
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 b29b097..a376bc0 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
@@ -65,8 +65,7 @@
 import org.eclipse.jgit.awtui.AwtAuthenticator;
 import org.eclipse.jgit.awtui.AwtCredentialsProvider;
 import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.lfs.CleanFilter;
-import org.eclipse.jgit.lfs.SmudgeFilter;
+import org.eclipse.jgit.lfs.BuiltinLFS;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryBuilder;
 import org.eclipse.jgit.pgm.internal.CLIText;
@@ -111,8 +110,7 @@
 	 */
 	public Main() {
 		HttpTransport.setConnectionFactory(new HttpClientConnectionFactory());
-		CleanFilter.register();
-		SmudgeFilter.register();
+		BuiltinLFS.register();
 		gcExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
 			private final ThreadFactory baseFactory = Executors
 					.defaultThreadFactory();
@@ -134,6 +132,9 @@
 	 * @throws java.lang.Exception
 	 */
 	public static void main(final String[] argv) throws Exception {
+		// make sure built-in filters are registered
+		BuiltinLFS.register();
+
 		new Main().run(argv);
 	}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
index 5f8ebdb..0e1b398 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
@@ -160,11 +160,8 @@
 			else
 				rb.findGitDir(dir);
 
-			Repository repo = rb.build();
-			try {
+			try (Repository repo = rb.build()) {
 				run(repo);
-			} finally {
-				repo.close();
 			}
 		}
 	}
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
index 64f7498..794592d 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index c215c90..80627b5 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,63 +3,63 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.test
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.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)",
  com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.api;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.api.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.attributes;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.awtui;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.blame;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.diff;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.dircache;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.events;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.fnmatch;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.gitrepo;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.hooks;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.ignore;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.ignore.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.fsck;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.io;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.junit;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lfs;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.merge;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.notes;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.patch;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.pgm;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.pgm.internal;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revplot;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.file;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.storage.pack;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.submodule;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.http;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport.resolver;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util.io;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util.sha1;version="[4.10.1,4.11.0)",
+ org.eclipse.jgit.api;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.api.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.attributes;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.awtui;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.blame;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.diff;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.dircache;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.events;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.fnmatch;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.gitrepo;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.hooks;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.ignore;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.fsck;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.io;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.junit;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lfs;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.merge;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.notes;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.patch;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.pgm;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revplot;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.file;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.storage.pack;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.submodule;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.http;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.io;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util.sha1;version="[4.11.3,4.12.0)",
  org.junit;version="[4.12,5.0.0)",
  org.junit.experimental.theories;version="[4.12,5.0.0)",
  org.junit.rules;version="[4.12,5.0.0)",
  org.junit.runner;version="[4.12,5.0.0)",
  org.junit.runners;version="[4.12,5.0.0)",
- org.slf4j;version="[1.7.2,2.0.0)"
+ org.slf4j;version="[1.7.0,2.0.0)"
 Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.hamcrest.library;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java
index 03b34ac..438d2d6 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java
@@ -187,10 +187,10 @@
 					"--no-index", "-v", "-n", "--stdin" };
 			Process proc = Runtime.getRuntime().exec(command, new String[0],
 					gitDir);
-			OutputStream out = proc.getOutputStream();
-			out.write((path + "\n").getBytes(UTF_8));
-			out.flush();
-			out.close();
+			try (OutputStream out = proc.getOutputStream()) {
+				out.write((path + "\n").getBytes(UTF_8));
+				out.flush();
+			}
 			return proc;
 		}
 
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
index 586505c..3f9ef12 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
@@ -202,28 +202,28 @@
 		}
 
 		void read() throws IOException, InterruptedException {
-			final BufferedReader in = new BufferedReader(new InputStreamReader(
-					proc.getInputStream(), "ISO-8859-1"));
-			String commitId = null;
-			TemporaryBuffer buf = null;
-			for (;;) {
-				String line = in.readLine();
-				if (line == null)
-					break;
-				if (line.startsWith("commit ")) {
-					if (buf != null) {
-						buf.close();
-						onCommit(commitId, buf.toByteArray());
-						buf.destroy();
+			try (BufferedReader in = new BufferedReader(
+					new InputStreamReader(proc.getInputStream(), ISO_8859_1))) {
+				String commitId = null;
+				TemporaryBuffer buf = null;
+				for (;;) {
+					String line = in.readLine();
+					if (line == null)
+						break;
+					if (line.startsWith("commit ")) {
+						if (buf != null) {
+							buf.close();
+							onCommit(commitId, buf.toByteArray());
+							buf.destroy();
+						}
+						commitId = line.substring("commit ".length());
+						buf = new TemporaryBuffer.LocalFile(null);
+					} else if (buf != null) {
+						buf.write(line.getBytes(ISO_8859_1));
+						buf.write('\n');
 					}
-					commitId = line.substring("commit ".length());
-					buf = new TemporaryBuffer.LocalFile(null);
-				} else if (buf != null) {
-					buf.write(line.getBytes(ISO_8859_1));
-					buf.write('\n');
 				}
 			}
-			in.close();
 			assertEquals(0, proc.waitFor());
 			proc = null;
 		}
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 900af56..fddeeec 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index aafda01..911f659 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -63,8 +63,7 @@
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lfs.CleanFilter;
-import org.eclipse.jgit.lfs.SmudgeFilter;
+import org.eclipse.jgit.lfs.BuiltinLFS;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
@@ -92,8 +91,7 @@
 
 	@Override
 	public void setUp() throws Exception {
-		CleanFilter.register();
-		SmudgeFilter.register();
+		BuiltinLFS.register();
 		super.setUp();
 	}
 
@@ -120,9 +118,9 @@
 	public void testAddExistingSingleFile() throws IOException, GitAPIException {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("a.txt").call();
@@ -491,9 +489,9 @@
 			GitAPIException {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("row1\r\nrow2");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("row1\r\nrow2");
+		}
 
 		try (Git git = new Git(db)) {
 			db.getConfig().setString("core", null, "autocrlf", "false");
@@ -521,9 +519,9 @@
 			data.append("row1\r\nrow2");
 		}
 		String crData = data.toString();
-		PrintWriter writer = new PrintWriter(file);
-		writer.print(crData);
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print(crData);
+		}
 		String lfData = data.toString().replaceAll("\r", "");
 		try (Git git = new Git(db)) {
 			db.getConfig().setString("core", null, "autocrlf", "false");
@@ -546,9 +544,9 @@
 			GitAPIException {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("row1\r\nrow2\u0000");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("row1\r\nrow2\u0000");
+		}
 
 		try (Git git = new Git(db)) {
 			db.getConfig().setString("core", null, "autocrlf", "false");
@@ -572,9 +570,9 @@
 		FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
 		File file = new File(db.getWorkTree(), "sub/a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("sub/a.txt").call();
@@ -590,18 +588,18 @@
 			GitAPIException {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			DirCache dc = git.add().addFilepattern("a.txt").call();
 
 			dc.getEntry(0).getObjectId();
 
-			writer = new PrintWriter(file);
-			writer.print("other content");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("other content");
+			}
 
 			dc = git.add().addFilepattern("a.txt").call();
 
@@ -615,9 +613,9 @@
 	public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			DirCache dc = git.add().addFilepattern("a.txt").call();
@@ -626,9 +624,9 @@
 
 			git.commit().setMessage("commit a.txt").call();
 
-			writer = new PrintWriter(file);
-			writer.print("other content");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("other content");
+			}
 
 			dc = git.add().addFilepattern("a.txt").call();
 
@@ -642,9 +640,9 @@
 	public void testAddRemovedFile() throws Exception {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			DirCache dc = git.add().addFilepattern("a.txt").call();
@@ -665,9 +663,9 @@
 	public void testAddRemovedCommittedFile() throws Exception {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			DirCache dc = git.add().addFilepattern("a.txt").call();
@@ -692,15 +690,15 @@
 
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File file2 = new File(db.getWorkTree(), "b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		ObjectInserter newObjectInserter = db.newObjectInserter();
 		DirCache dc = db.lockDirCache();
@@ -709,14 +707,14 @@
 		addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
 		addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
 
-		writer = new PrintWriter(file);
-		writer.print("other content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("other content");
+		}
 		addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
 
-		writer = new PrintWriter(file);
-		writer.print("our content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("our content");
+		}
 		addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
 				.getObjectId();
 
@@ -745,15 +743,15 @@
 	public void testAddTwoFiles() throws Exception  {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File file2 = new File(db.getWorkTree(), "b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
@@ -769,15 +767,15 @@
 		FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
 		File file = new File(db.getWorkTree(), "sub/a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File file2 = new File(db.getWorkTree(), "sub/b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("sub").call();
@@ -793,21 +791,21 @@
 		FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
 		File file = new File(db.getWorkTree(), "sub/a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File ignoreFile = new File(db.getWorkTree(), ".gitignore");
 		FileUtils.createNewFile(ignoreFile);
-		writer = new PrintWriter(ignoreFile);
-		writer.print("sub/b.txt");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(ignoreFile)) {
+			writer.print("sub/b.txt");
+		}
 
 		File file2 = new File(db.getWorkTree(), "sub/b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("sub").call();
@@ -823,15 +821,15 @@
 		FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
 		File file = new File(db.getWorkTree(), "sub/a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File file2 = new File(db.getWorkTree(), "sub/b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern(".").call();
@@ -851,15 +849,15 @@
 		FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
 		File file = new File(db.getWorkTree(), "sub/a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File file2 = new File(db.getWorkTree(), "sub/b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("sub").call();
@@ -874,14 +872,14 @@
 			// new unstaged file sub/c.txt
 			File file3 = new File(db.getWorkTree(), "sub/c.txt");
 			FileUtils.createNewFile(file3);
-			writer = new PrintWriter(file3);
-			writer.print("content c");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file3)) {
+				writer.print("content c");
+			}
 
 			// file sub/a.txt is modified
-			writer = new PrintWriter(file);
-			writer.print("modified content");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("modified content");
+			}
 
 			// file sub/b.txt is deleted
 			FileUtils.delete(file2);
@@ -906,15 +904,15 @@
 		FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
 		File file = new File(db.getWorkTree(), "sub/a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		File file2 = new File(db.getWorkTree(), "sub/b.txt");
 		FileUtils.createNewFile(file2);
-		writer = new PrintWriter(file2);
-		writer.print("content b");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file2)) {
+			writer.print("content b");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("sub").call();
@@ -929,14 +927,14 @@
 			// new unstaged file sub/c.txt
 			File file3 = new File(db.getWorkTree(), "sub/c.txt");
 			FileUtils.createNewFile(file3);
-			writer = new PrintWriter(file3);
-			writer.print("content c");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file3)) {
+				writer.print("content c");
+			}
 
 			// file sub/a.txt is modified
-			writer = new PrintWriter(file);
-			writer.print("modified content");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("modified content");
+			}
 
 			FileUtils.delete(file2);
 
@@ -1250,9 +1248,9 @@
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("nested-repo").call();
-
+			// with gitlinks ignored, we treat this as a normal directory
 			assertEquals(
-					"[nested-repo, mode:160000]",
+					"[nested-repo/README1.md, mode:100644][nested-repo/README2.md, mode:100644]",
 					indexState(0));
 		}
 	}
@@ -1260,10 +1258,11 @@
 	private static DirCacheEntry addEntryToBuilder(String path, File file,
 			ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage)
 			throws IOException {
-		FileInputStream inputStream = new FileInputStream(file);
-		ObjectId id = newObjectInserter.insert(
+		ObjectId id;
+		try (FileInputStream inputStream = new FileInputStream(file)) {
+			id = newObjectInserter.insert(
 				Constants.OBJ_BLOB, file.length(), inputStream);
-		inputStream.close();
+		}
 		DirCacheEntry entry = new DirCacheEntry(path, stage);
 		entry.setObjectId(id);
 		entry.setFileMode(FileMode.REGULAR_FILE);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
index 1201d9f..71df59e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
@@ -74,8 +74,7 @@
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lfs.CleanFilter;
-import org.eclipse.jgit.lfs.SmudgeFilter;
+import org.eclipse.jgit.lfs.BuiltinLFS;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
@@ -102,8 +101,7 @@
 	@Override
 	@Before
 	public void setUp() throws Exception {
-		CleanFilter.register();
-		SmudgeFilter.register();
+		BuiltinLFS.register();
 		super.setUp();
 		git = new Git(db);
 		// commit something
@@ -172,15 +170,12 @@
 	@Test
 	public void testCheckoutWithNonDeletedFiles() throws Exception {
 		File testFile = writeTrashFile("temp", "");
-		FileInputStream fis = new FileInputStream(testFile);
-		try {
+		try (FileInputStream fis = new FileInputStream(testFile)) {
 			FileUtils.delete(testFile);
 			return;
 		} catch (IOException e) {
 			// the test makes only sense if deletion of
 			// a file with open stream fails
-		} finally {
-			fis.close();
 		}
 		FileUtils.delete(testFile);
 		CheckoutCommand co = git.checkout();
@@ -194,15 +189,12 @@
 		git.checkout().setName("master").call();
 		assertTrue(testFile.exists());
 		// lock the file so it can't be deleted (in Windows, that is)
-		fis = new FileInputStream(testFile);
-		try {
+		try (FileInputStream fis = new FileInputStream(testFile)) {
 			assertEquals(Status.NOT_TRIED, co.getResult().getStatus());
 			co.setName("test").call();
 			assertTrue(testFile.exists());
 			assertEquals(Status.NONDELETED, co.getResult().getStatus());
 			assertTrue(co.getResult().getUndeletedList().contains("Test.txt"));
-		} finally {
-			fis.close();
 		}
 	}
 
@@ -824,7 +816,7 @@
 	}
 
 	private File writeTempFile(String body) throws IOException {
-		File f = File.createTempFile("AddCommandTest_", "");
+		File f = File.createTempFile("CheckoutCommandTest_", "");
 		JGitTestUtil.write(f, body);
 		return f;
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
index 85436db..065b5b4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
@@ -48,10 +48,12 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.Set;
 import java.util.TreeSet;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Repository;
@@ -238,8 +240,9 @@
 		command.setPath(path);
 		String uri = db.getDirectory().toURI().toString();
 		command.setURI(uri);
-		Repository repo = command.call();
-		repo.close();
+		try (Repository repo = command.call()) {
+			// Unused
+		}
 
 		Status beforeCleanStatus = git.status().call();
 		assertTrue(beforeCleanStatus.getAdded().contains(DOT_GIT_MODULES));
@@ -296,4 +299,18 @@
 		// The inner repository should be cleaned this time
 		assertTrue(forceCleanedFiles.contains(innerRepoName + "/"));
 	}
+
+	@Test
+	// To proof Bug 514434. No assertions, but before the bugfix
+	// this test was throwing Exceptions
+	public void testFilesShouldBeCleanedInSubSubFolders()
+			throws IOException, NoFilepatternException, GitAPIException {
+		writeTrashFile(".gitignore",
+				"/ignored-dir\n/sub-noclean/Ignored.txt\n/this_is_ok\n/this_is/not_ok\n");
+		git.add().addFilepattern(".gitignore").call();
+		git.commit().setMessage("adding .gitignore").call();
+		writeTrashFile("this_is_ok/more/subdirs/file.txt", "1");
+		writeTrashFile("this_is/not_ok/more/subdirs/file.txt", "2");
+		git.clean().setCleanDirectories(true).setIgnore(false).call();
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index e687a6c..0d7009d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -601,17 +601,17 @@
 
 		SubmoduleWalk walk = SubmoduleWalk.forIndex(git2.getRepository());
 		assertTrue(walk.next());
-		Repository clonedSub1 = walk.getRepository();
-		assertNotNull(clonedSub1);
-		assertEquals(
-				new File(git2.getRepository().getWorkTree(), walk.getPath()),
-				clonedSub1.getWorkTree());
-		assertEquals(new File(new File(git2.getRepository().getDirectory(),
-				"modules"), walk.getPath()),
-				clonedSub1.getDirectory());
-		status = new SubmoduleStatusCommand(clonedSub1);
-		statuses = status.call();
-		clonedSub1.close();
+		try (Repository clonedSub1 = walk.getRepository()) {
+			assertNotNull(clonedSub1);
+			assertEquals(new File(git2.getRepository().getWorkTree(),
+					walk.getPath()), clonedSub1.getWorkTree());
+			assertEquals(
+					new File(new File(git2.getRepository().getDirectory(),
+							"modules"), walk.getPath()),
+					clonedSub1.getDirectory());
+			status = new SubmoduleStatusCommand(clonedSub1);
+			statuses = status.call();
+		}
 		pathStatus = statuses.get(path);
 		assertNotNull(pathStatus);
 		assertEquals(SubmoduleStatusType.INITIALIZED, pathStatus.getType());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
index 1d5c674..ca0630e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
@@ -120,9 +120,9 @@
 			// create first file
 			File file = new File(db.getWorkTree(), "a.txt");
 			FileUtils.createNewFile(file);
-			PrintWriter writer = new PrintWriter(file);
-			writer.print("content1");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("content1");
+			}
 
 			// First commit - a.txt file
 			git.add().addFilepattern("a.txt").call();
@@ -131,9 +131,9 @@
 			// create second file
 			file = new File(db.getWorkTree(), "b.txt");
 			FileUtils.createNewFile(file);
-			writer = new PrintWriter(file);
-			writer.print("content2");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("content2");
+			}
 
 			// Second commit - b.txt file
 			git.add().addFilepattern("b.txt").call();
@@ -231,9 +231,9 @@
 			JGitInternalException, GitAPIException {
 		File file = new File(db.getWorkTree(), "a.txt");
 		FileUtils.createNewFile(file);
-		PrintWriter writer = new PrintWriter(file);
-		writer.print("content");
-		writer.close();
+		try (PrintWriter writer = new PrintWriter(file)) {
+			writer.print("content");
+		}
 
 		try (Git git = new Git(db)) {
 			git.add().addFilepattern("a.txt").call();
@@ -242,9 +242,9 @@
 			assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
 					tw.getObjectId(0).getName());
 
-			writer = new PrintWriter(file);
-			writer.print("content2");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("content2");
+			}
 			commit = git.commit().setMessage("second commit").call();
 			tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
 			assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
@@ -265,9 +265,9 @@
 			// create file
 			File file = new File(db.getWorkTree(), "a.txt");
 			FileUtils.createNewFile(file);
-			PrintWriter writer = new PrintWriter(file);
-			writer.print("content1");
-			writer.close();
+			try (PrintWriter writer = new PrintWriter(file)) {
+				writer.print("content1");
+			}
 
 			// First commit - a.txt file
 			git.add().addFilepattern("a.txt").call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index a0834e7..0dd3749 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -223,9 +223,9 @@
 			assertEquals(uri, generator.getModulesUrl());
 			assertEquals(path, generator.getModulesPath());
 			assertEquals(uri, generator.getConfigUrl());
-			Repository subModRepo = generator.getRepository();
-			assertNotNull(subModRepo);
-			subModRepo.close();
+			try (Repository subModRepo = generator.getRepository()) {
+				assertNotNull(subModRepo);
+			}
 			assertEquals(commit, repo.resolve(Constants.HEAD));
 
 			RevCommit submoduleCommit = git.commit().setMessage("submodule add")
@@ -273,9 +273,9 @@
 			assertEquals(uri, generator.getModulesUrl());
 			assertEquals(path, generator.getModulesPath());
 			assertEquals(uri, generator.getConfigUrl());
-			Repository subModRepo = generator.getRepository();
-			assertNotNull(subModRepo);
-			subModRepo.close();
+			try (Repository subModRepo = generator.getRepository()) {
+				assertNotNull(subModRepo);
+			}
 			assertEquals(commit2, repo.resolve(Constants.HEAD));
 
 			RevCommit submoduleAddCommit = git.commit().setMessage("submodule add")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index d78a328..79da2da 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -297,9 +297,9 @@
 	}
 
 	private static void touch(File f, String contents) throws Exception {
-		FileWriter w = new FileWriter(f);
-		w.write(contents);
-		w.close();
+		try (FileWriter w = new FileWriter(f)) {
+			w.write(contents);
+		}
 	}
 
 	private String describe(ObjectId c1, boolean longDesc)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java
index 4208f4d..6c3504a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/HugeFileTest.java
@@ -87,9 +87,9 @@
 	public void testAddHugeFile() throws Exception {
 		measure("Commencing test");
 		File file = new File(db.getWorkTree(), "a.txt");
-		RandomAccessFile rf = new RandomAccessFile(file, "rw");
-		rf.setLength(4429185024L);
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.setLength(4429185024L);
+		}
 		measure("Created file");
 
 		git.add().addFilepattern("a.txt").call();
@@ -109,9 +109,9 @@
 		assertEquals(0, status.getUntracked().size());
 
 		// Does not change anything, but modified timestamp
-		rf = new RandomAccessFile(file, "rw");
-		rf.write(0);
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.write(0);
+		}
 
 		status = git.status().call();
 		measure("Status after non-modifying update");
@@ -125,9 +125,9 @@
 		assertEquals(0, status.getUntracked().size());
 
 		// Change something
-		rf = new RandomAccessFile(file, "rw");
-		rf.write('a');
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.write('a');
+		}
 
 		status = git.status().call();
 		measure("Status after modifying update");
@@ -141,10 +141,10 @@
 		assertEquals(0, status.getUntracked().size());
 
 		// Truncate mod 4G and re-establish equality
-		rf = new RandomAccessFile(file, "rw");
-		rf.setLength(134217728L);
-		rf.write(0);
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.setLength(134217728L);
+			rf.write(0);
+		}
 
 		status = git.status().call();
 		measure("Status after truncating update");
@@ -158,9 +158,9 @@
 		assertEquals(0, status.getUntracked().size());
 
 		// Change something
-		rf = new RandomAccessFile(file, "rw");
-		rf.write('a');
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.write('a');
+		}
 
 		status = git.status().call();
 		measure("Status after modifying and truncating update");
@@ -174,10 +174,10 @@
 		assertEquals(0, status.getUntracked().size());
 
 		// Truncate to entry length becomes negative int
-		rf = new RandomAccessFile(file, "rw");
-		rf.setLength(3429185024L);
-		rf.write(0);
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.setLength(3429185024L);
+			rf.write(0);
+		}
 
 		git.add().addFilepattern("a.txt").call();
 		measure("Added truncated file");
@@ -197,9 +197,9 @@
 		assertEquals(0, status.getUntracked().size());
 
 		// Change something
-		rf = new RandomAccessFile(file, "rw");
-		rf.write('a');
-		rf.close();
+		try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
+			rf.write('a');
+		}
 
 		status = git.status().call();
 		measure("Status after modifying and truncating update");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java
index e850223..9e3ee2c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/InitCommandTest.java
@@ -69,14 +69,14 @@
 	}
 
 	@Test
-	public void testInitRepository() throws IOException, JGitInternalException,
-			GitAPIException {
+	public void testInitRepository()
+			throws IOException, JGitInternalException, GitAPIException {
 		File directory = createTempDirectory("testInitRepository");
 		InitCommand command = new InitCommand();
 		command.setDirectory(directory);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
+		try (Git git = command.call()) {
+			assertNotNull(git.getRepository());
+		}
 	}
 
 	@Test
@@ -89,9 +89,9 @@
 		assertTrue(directory.listFiles().length > 0);
 		InitCommand command = new InitCommand();
 		command.setDirectory(directory);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
+		try (Git git = command.call()) {
+			assertNotNull(git.getRepository());
+		}
 	}
 
 	@Test
@@ -101,10 +101,11 @@
 		InitCommand command = new InitCommand();
 		command.setDirectory(directory);
 		command.setBare(true);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
-		assertTrue(repository.isBare());
+		try (Git git = command.call()) {
+			Repository repository = git.getRepository();
+			assertNotNull(repository);
+			assertTrue(repository.isBare());
+		}
 	}
 
 	// non-bare repos where gitDir and directory is set. Same as
@@ -117,11 +118,12 @@
 		InitCommand command = new InitCommand();
 		command.setDirectory(wt);
 		command.setGitDir(gitDir);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
-		assertEqualsFile(wt, repository.getWorkTree());
-		assertEqualsFile(gitDir, repository.getDirectory());
+		try (Git git = command.call()) {
+			Repository repository = git.getRepository();
+			assertNotNull(repository);
+			assertEqualsFile(wt, repository.getWorkTree());
+			assertEqualsFile(gitDir, repository.getDirectory());
+		}
 	}
 
 	// non-bare repos where only gitDir is set. Same as
@@ -135,12 +137,13 @@
 		File gitDir = createTempDirectory("testInitRepository/.git");
 		InitCommand command = new InitCommand();
 		command.setGitDir(gitDir);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
-		assertEqualsFile(gitDir, repository.getDirectory());
-		assertEqualsFile(new File(reader.getProperty("user.dir")),
-				repository.getWorkTree());
+		try (Git git = command.call()) {
+			Repository repository = git.getRepository();
+			assertNotNull(repository);
+			assertEqualsFile(gitDir, repository.getDirectory());
+			assertEqualsFile(new File(reader.getProperty("user.dir")),
+					repository.getWorkTree());
+		}
 	}
 
 	// Bare repos where gitDir and directory is set will only work if gitDir and
@@ -169,13 +172,14 @@
 				.getAbsolutePath());
 		InitCommand command = new InitCommand();
 		command.setBare(false);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
-		assertEqualsFile(new File(reader.getProperty("user.dir"), ".git"),
-				repository.getDirectory());
-		assertEqualsFile(new File(reader.getProperty("user.dir")),
-				repository.getWorkTree());
+		try (Git git = command.call()) {
+			Repository repository = git.getRepository();
+			assertNotNull(repository);
+			assertEqualsFile(new File(reader.getProperty("user.dir"), ".git"),
+					repository.getDirectory());
+			assertEqualsFile(new File(reader.getProperty("user.dir")),
+					repository.getWorkTree());
+		}
 	}
 
 	// If neither directory nor gitDir is set in a bare repo make sure
@@ -189,12 +193,13 @@
 				.getAbsolutePath());
 		InitCommand command = new InitCommand();
 		command.setBare(true);
-		Repository repository = command.call().getRepository();
-		addRepoToClose(repository);
-		assertNotNull(repository);
-		assertEqualsFile(new File(reader.getProperty("user.dir")),
-				repository.getDirectory());
-		assertNull(repository.getWorkTree());
+		try (Git git = command.call()) {
+			Repository repository = git.getRepository();
+			assertNotNull(repository);
+			assertEqualsFile(new File(reader.getProperty("user.dir")),
+					repository.getDirectory());
+			assertNull(repository.getWorkTree());
+		}
 	}
 
 	// In a non-bare repo when directory and gitDir is set then they shouldn't
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java
index 9d15699..6e06e95 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NotesCommandTest.java
@@ -42,6 +42,7 @@
  */
 package org.eclipse.jgit.api;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 
 import java.util.List;
@@ -87,7 +88,7 @@
 		git.notesAdd().setObjectId(commit2).setMessage("data").call();
 		Note note = git.notesShow().setObjectId(commit2).call();
 		String content = new String(db.open(note.getData()).getCachedBytes(),
-				"UTF-8");
+				UTF_8);
 		assertEquals(content, "data");
 
 		git.notesRemove().setObjectId(commit2).call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index cc5a024..9461c42 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -591,34 +591,23 @@
 
 	private static void writeToFile(File actFile, String string)
 			throws IOException {
-		FileOutputStream fos = null;
-		try {
-			fos = new FileOutputStream(actFile);
+		try (FileOutputStream fos = new FileOutputStream(actFile)) {
 			fos.write(string.getBytes(UTF_8));
-			fos.close();
-		} finally {
-			if (fos != null)
-				fos.close();
 		}
 	}
 
 	private static void assertFileContentsEqual(File actFile, String string)
 			throws IOException {
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		FileInputStream fis = null;
 		byte[] buffer = new byte[100];
-		try {
-			fis = new FileInputStream(actFile);
+		try (FileInputStream fis = new FileInputStream(actFile)) {
 			int read = fis.read(buffer);
 			while (read > 0) {
 				bos.write(buffer, 0, read);
 				read = fis.read(buffer);
 			}
-			String content = new String(bos.toByteArray(), "UTF-8");
+			String content = new String(bos.toByteArray(), UTF_8);
 			assertEquals(string, content);
-		} finally {
-			if (fis != null)
-				fis.close();
 		}
 	}
 }
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 dfe05b8..913b4ac 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
@@ -395,34 +395,23 @@
 
 	private static void writeToFile(File actFile, String string)
 			throws IOException {
-		FileOutputStream fos = null;
-		try {
-			fos = new FileOutputStream(actFile);
+		try (FileOutputStream fos = new FileOutputStream(actFile)) {
 			fos.write(string.getBytes(UTF_8));
-			fos.close();
-		} finally {
-			if (fos != null)
-				fos.close();
 		}
 	}
 
 	private static void assertFileContentsEqual(File actFile, String string)
 			throws IOException {
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		FileInputStream fis = null;
 		byte[] buffer = new byte[100];
-		try {
-			fis = new FileInputStream(actFile);
+		try (FileInputStream fis = new FileInputStream(actFile)) {
 			int read = fis.read(buffer);
 			while (read > 0) {
 				bos.write(buffer, 0, read);
 				read = fis.read(buffer);
 			}
-			String content = new String(bos.toByteArray(), "UTF-8");
+			String content = new String(bos.toByteArray(), UTF_8);
 			assertEquals(string, content);
-		} finally {
-			if (fis != null)
-				fis.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 3f43c6b..2bf91ae 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -2103,9 +2103,8 @@
 	private int countPicks() throws IOException {
 		int count = 0;
 		File todoFile = getTodoFile();
-		BufferedReader br = new BufferedReader(new InputStreamReader(
-				new FileInputStream(todoFile), "UTF-8"));
-		try {
+		try (BufferedReader br = new BufferedReader(new InputStreamReader(
+				new FileInputStream(todoFile), UTF_8))) {
 			String line = br.readLine();
 			while (line != null) {
 				int firstBlank = line.indexOf(' ');
@@ -2123,8 +2122,6 @@
 				line = br.readLine();
 			}
 			return count;
-		} finally {
-			br.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java
index 3483813..344d1af 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java
@@ -366,6 +366,34 @@
 	}
 
 	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception {
+		createFiles("a", "dir/b", "dir/sub/c");
+		writeTrashFile(".gitattributes", "**/ bar\n");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception {
+		createFiles("a", "dir/b", "dir/sub/c");
+		writeTrashFile(".gitattributes", "**/**/ bar\n");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception {
+		createFiles("a", "x/b", "sub/x/c", "sub/x/d/e");
+		writeTrashFile(".gitattributes", "x/**/ bar\n");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception {
+		createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y");
+		writeTrashFile(".gitattributes", "x/**/ bar\n");
+		assertSameAsCGit();
+	}
+
+	@Test
 	public void testDirectoryMatchSubComplex() throws Exception {
 		createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
 		writeTrashFile(".gitattributes", "s[rs]c/n*/ bar\n");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
index a4f3d18..01ca888 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
@@ -313,7 +313,6 @@
 			WrongRepositoryStateException, NoMessageException, GitAPIException {
 
 		RevCommit disableCheckedCommit;
-		FileInputStream mergeResultFile = null;
 		// Set up a git with conflict commits on images
 		try (Git git = new Git(db)) {
 			// First commit
@@ -352,15 +351,12 @@
 			assertEquals(MergeStatus.CONFLICTING, mergeResult.getMergeStatus());
 
 			// Check that the image was not modified (no conflict marker added)
-			mergeResultFile = new FileInputStream(
+			try (FileInputStream mergeResultFile = new FileInputStream(
 					db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF)
-							.toFile());
-			assertTrue(contentEquals(
-					getClass().getResourceAsStream(ENABLED_CHECKED_GIF),
-					mergeResultFile));
-		} finally {
-			if (mergeResultFile != null) {
-				mergeResultFile.close();
+							.toFile())) {
+				assertTrue(contentEquals(
+						getClass().getResourceAsStream(ENABLED_CHECKED_GIF),
+						mergeResultFile));
 			}
 		}
 	}
@@ -373,7 +369,6 @@
 			WrongRepositoryStateException, NoMessageException, GitAPIException {
 
 		RevCommit disableCheckedCommit;
-		FileInputStream mergeResultFile = null;
 		// Set up a git whith conflict commits on images
 		try (Git git = new Git(db)) {
 			// First commit
@@ -412,14 +407,12 @@
 			assertEquals(MergeStatus.CONFLICTING, mergeResult.getMergeStatus());
 
 			// Check that the image was not modified (not conflict marker added)
-			mergeResultFile = new FileInputStream(db.getWorkTree().toPath()
-					.resolve(ENABLED_CHECKED_GIF).toFile());
-			assertTrue(contentEquals(
-					getClass().getResourceAsStream(ENABLED_CHECKED_GIF),
-					mergeResultFile));
-		} finally {
-			if (mergeResultFile != null) {
-				mergeResultFile.close();
+			try (FileInputStream mergeResultFile = new FileInputStream(
+					db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF)
+							.toFile())) {
+				assertTrue(contentEquals(
+						getClass().getResourceAsStream(ENABLED_CHECKED_GIF),
+						mergeResultFile));
 			}
 		}
 	}
@@ -432,7 +425,6 @@
 			NoMessageException, GitAPIException {
 
 		RevCommit disableCheckedCommit;
-		FileInputStream mergeResultFile = null;
 		// Set up a git whith conflict commits on images
 		try (Git git = new Git(db)) {
 			// First commit
@@ -471,14 +463,12 @@
 			assertEquals(MergeStatus.CONFLICTING, mergeResult.getMergeStatus());
 
 			// Check that the image was not modified (not conflict marker added)
-			mergeResultFile = new FileInputStream(db.getWorkTree().toPath()
-					.resolve(ENABLED_CHECKED_GIF).toFile());
-			assertFalse(contentEquals(
-					getClass().getResourceAsStream(ENABLED_CHECKED_GIF),
-					mergeResultFile));
-		} finally {
-			if (mergeResultFile != null) {
-				mergeResultFile.close();
+			try (FileInputStream mergeResultFile = new FileInputStream(
+					db.getWorkTree().toPath().resolve(ENABLED_CHECKED_GIF)
+							.toFile())) {
+				assertFalse(contentEquals(
+						getClass().getResourceAsStream(ENABLED_CHECKED_GIF),
+						mergeResultFile));
 			}
 		}
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java
index 31f70cf..49e5d1b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java
@@ -181,17 +181,14 @@
 	}
 
 	private Patch parseTestPatchFile(final String patchFile) throws IOException {
-		final InputStream in = getClass().getResourceAsStream(patchFile);
-		if (in == null) {
-			fail("No " + patchFile + " test vector");
-			return null; // Never happens
-		}
-		try {
+		try (InputStream in = getClass().getResourceAsStream(patchFile)) {
+			if (in == null) {
+				fail("No " + patchFile + " test vector");
+				return null; // Never happens
+			}
 			final Patch p = new Patch();
 			p.parse(in);
 			return p;
-		} finally {
-			in.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
index fabf034..45832f4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
@@ -55,12 +55,14 @@
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.patch.FileHeader;
 import org.eclipse.jgit.patch.HunkHeader;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 import org.eclipse.jgit.util.FileUtils;
@@ -485,6 +487,102 @@
 		}
 	}
 
+	@Test
+	public void testDiffAutoCrlfSmallFile() throws Exception {
+		String content = "01234\r\n01234\r\n01234\r\n";
+		String expectedDiff = "diff --git a/test.txt b/test.txt\n"
+				+ "index fe25983..a44a032 100644\n" //
+				+ "--- a/test.txt\n" //
+				+ "+++ b/test.txt\n" //
+				+ "@@ -1,3 +1,4 @@\n" //
+				+ " 01234\n" //
+				+ "+ABCD\n" //
+				+ " 01234\n" //
+				+ " 01234\n";
+		doAutoCrLfTest(content, expectedDiff);
+	}
+
+	@Test
+	public void testDiffAutoCrlfMediumFile() throws Exception {
+		String content = mediumCrLfString();
+		String expectedDiff = "diff --git a/test.txt b/test.txt\n"
+				+ "index 215c502..c10f08c 100644\n" //
+				+ "--- a/test.txt\n" //
+				+ "+++ b/test.txt\n" //
+				+ "@@ -1,4 +1,5 @@\n" //
+				+ " 01234567\n" //
+				+ "+ABCD\n" //
+				+ " 01234567\n" //
+				+ " 01234567\n" //
+				+ " 01234567\n";
+		doAutoCrLfTest(content, expectedDiff);
+	}
+
+	@Test
+	public void testDiffAutoCrlfLargeFile() throws Exception {
+		String content = largeCrLfString();
+		String expectedDiff = "diff --git a/test.txt b/test.txt\n"
+				+ "index 7014942..c0487a7 100644\n" //
+				+ "--- a/test.txt\n" //
+				+ "+++ b/test.txt\n" //
+				+ "@@ -1,4 +1,5 @@\n"
+				+ " 012345678901234567890123456789012345678901234567\n"
+				+ "+ABCD\n"
+				+ " 012345678901234567890123456789012345678901234567\n"
+				+ " 012345678901234567890123456789012345678901234567\n"
+				+ " 012345678901234567890123456789012345678901234567\n";
+		doAutoCrLfTest(content, expectedDiff);
+	}
+
+	private void doAutoCrLfTest(String content, String expectedDiff)
+			throws Exception {
+		FileBasedConfig config = db.getConfig();
+		config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_AUTOCRLF, "true");
+		config.save();
+		commitFile("test.txt", content, "master");
+		// Insert a line into content
+		int i = content.indexOf('\n');
+		content = content.substring(0, i + 1) + "ABCD\r\n"
+				+ content.substring(i + 1);
+		writeTrashFile("test.txt", content);
+		// Create the patch
+		try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+				DiffFormatter dfmt = new DiffFormatter(
+						new BufferedOutputStream(os))) {
+			dfmt.setRepository(db);
+			dfmt.format(new DirCacheIterator(db.readDirCache()),
+					new FileTreeIterator(db));
+			dfmt.flush();
+
+			String actual = os.toString("UTF-8");
+
+			assertEquals(expectedDiff, actual);
+		}
+	}
+
+	private static String largeCrLfString() {
+		String line = "012345678901234567890123456789012345678901234567\r\n";
+		StringBuilder builder = new StringBuilder(
+				2 * RawText.FIRST_FEW_BYTES);
+		while (builder.length() < 2 * RawText.FIRST_FEW_BYTES) {
+			builder.append(line);
+		}
+		return builder.toString();
+	}
+
+	private static String mediumCrLfString() {
+		// Create a CR-LF string longer than RawText.FIRST_FEW_BYTES whose
+		// canonical representation is shorter than RawText.FIRST_FEW_BYTES.
+		String line = "01234567\r\n"; // 10 characters
+		StringBuilder builder = new StringBuilder(
+				RawText.FIRST_FEW_BYTES + line.length());
+		while (builder.length() <= RawText.FIRST_FEW_BYTES) {
+			builder.append(line);
+		}
+		return builder.toString();
+	}
+
 	private static String makeDiffHeader(String pathA, String pathB,
 			ObjectId aId,
 			ObjectId bId) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
index 92ce4e1..dec1762 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
@@ -43,6 +43,7 @@
 
 package org.eclipse.jgit.dircache;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.eclipse.jgit.junit.Assert.assertEquals;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -234,32 +235,26 @@
 
 	private static Map<String, CGitIndexRecord> readLsFiles() throws Exception {
 		final LinkedHashMap<String, CGitIndexRecord> r = new LinkedHashMap<>();
-		final BufferedReader br = new BufferedReader(new InputStreamReader(
-				new FileInputStream(pathOf("gitgit.lsfiles")), "UTF-8"));
-		try {
+		try (BufferedReader br = new BufferedReader(new InputStreamReader(
+				new FileInputStream(pathOf("gitgit.lsfiles")), UTF_8))) {
 			String line;
 			while ((line = br.readLine()) != null) {
 				final CGitIndexRecord cr = new CGitIndexRecord(line);
 				r.put(cr.path, cr);
 			}
-		} finally {
-			br.close();
 		}
 		return r;
 	}
 
 	private static Map<String, CGitLsTreeRecord> readLsTree() throws Exception {
 		final LinkedHashMap<String, CGitLsTreeRecord> r = new LinkedHashMap<>();
-		final BufferedReader br = new BufferedReader(new InputStreamReader(
-				new FileInputStream(pathOf("gitgit.lstree")), "UTF-8"));
-		try {
+		try (BufferedReader br = new BufferedReader(new InputStreamReader(
+				new FileInputStream(pathOf("gitgit.lstree")), UTF_8))) {
 			String line;
 			while ((line = br.readLine()) != null) {
 				final CGitLsTreeRecord cr = new CGitLsTreeRecord(line);
 				r.put(cr.path, cr);
 			}
-		} finally {
-			br.close();
 		}
 		return r;
 	}
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 9afb58e..2253a04 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
@@ -186,6 +186,59 @@
 	}
 
 	@Test
+	public void runTwiceIsNOP() 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(UTF_8)))
+			.setRemoteReader(repos)
+			.setURI("platform/")
+			.setTargetURI("platform/superproject")
+			.setRecordRemoteBranch(true)
+			.setRecordSubmoduleLabels(true)
+			.call();
+
+		String firstIdStr = commit.getId().name() + ":" + ".gitmodules";
+		commit = new RepoCommand(dest)
+			.setInputStream(new ByteArrayInputStream(
+				xmlContent.toString().getBytes(UTF_8)))
+			.setRemoteReader(repos)
+			.setURI("platform/")
+			.setTargetURI("platform/superproject")
+			.setRecordRemoteBranch(true)
+			.setRecordSubmoduleLabels(true)
+			.call();
+		String idStr = commit.getId().name() + ":" + ".gitmodules";
+		assertEquals(firstIdStr, idStr);
+		child.close();
+		dest.close();
+	}
+
+	@Test
 	public void androidSetup() throws Exception {
 		Repository child = Git.cloneRepository()
 				.setURI(groupADb.getDirectory().toURI().toString())
@@ -213,8 +266,7 @@
 		repos.put("platform/base", child);
 
 		RevCommit commit = cmd
-				.setInputStream(new ByteArrayInputStream(
-						xmlContent.toString().getBytes(UTF_8)))
+			.setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(UTF_8)))
 			.setRemoteReader(repos)
 			.setURI("platform/")
 			.setTargetURI("platform/superproject")
@@ -238,6 +290,48 @@
 	}
 
 	@Test
+	public void recordUnreachableRemotes() throws Exception {
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+			.append("<manifest>")
+			.append("<remote name=\"remote1\" fetch=\"https://host.com/\" />")
+			.append("<default revision=\"master\" remote=\"remote1\" />")
+			.append("<project path=\"base\" name=\"platform/base\" />")
+			.append("</manifest>");
+
+		Repository dest = Git.cloneRepository()
+				.setURI(db.getDirectory().toURI().toString())
+				.setDirectory(createUniqueTestGitDir(true)).setBare(true).call()
+				.getRepository();
+
+		assertTrue(dest.isBare());
+
+		RevCommit commit = new RepoCommand(dest)
+			.setInputStream(new ByteArrayInputStream(
+				xmlContent.toString().getBytes(UTF_8)))
+			.setRemoteReader(new IndexedRepos())
+			.setURI("platform/")
+			.setTargetURI("platform/superproject")
+			.setRecordRemoteBranch(true)
+			.setIgnoreRemoteFailures(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, "https://host.com/platform/base");
+		}
+
+		dest.close();
+	}
+
+	@Test
 	public void gerritSetup() throws Exception {
 		Repository child =
 			Git.cloneRepository().setURI(groupADb.getDirectory().toURI().toString())
@@ -345,6 +439,63 @@
 	}
 
 	@Test
+	public void absoluteRemoteURLAbsoluteTargetURL() 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();
+		String abs = "https://chromium.googlesource.com";
+		String repoUrl = "https://chromium.googlesource.com/chromium/src";
+		boolean fetchSlash = false;
+		boolean baseSlash = false;
+		do {
+			do {
+				String fetchUrl = fetchSlash ? abs + "/" : abs;
+				String baseUrl = baseSlash ? abs + "/" : abs;
+
+				StringBuilder xmlContent = new StringBuilder();
+				xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+					.append("<manifest>")
+					.append("<remote name=\"origin\" fetch=\"" + fetchUrl + "\" />")
+					.append("<default revision=\"master\" remote=\"origin\" />")
+					.append("<project path=\"src\" name=\"chromium/src\" />")
+					.append("</manifest>");
+				RepoCommand cmd = new RepoCommand(dest);
+
+				IndexedRepos repos = new IndexedRepos();
+				repos.put(repoUrl, child);
+
+				RevCommit commit = cmd
+					.setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(UTF_8)))
+					.setRemoteReader(repos)
+					.setURI(baseUrl)
+					.setTargetURI(abs + "/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", "src", "url");
+					assertEquals("../chromium/src", subUrl);
+				}
+				fetchSlash = !fetchSlash;
+			} while (fetchSlash);
+			baseSlash = !baseSlash;
+		} while (baseSlash);
+		child.close();
+		dest.close();
+	}
+
+	@Test
 	public void testAddRepoManifest() throws Exception {
 		StringBuilder xmlContent = new StringBuilder();
 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
@@ -814,37 +965,6 @@
 		assertEquals("submodule content should be as expected",
 				"master world", content);
 	}
-
-	@Test
-	public void testNonDefaultRemotes() throws Exception {
-		StringBuilder xmlContent = new StringBuilder();
-		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-			.append("<manifest>")
-			.append("<remote name=\"remote1\" fetch=\".\" />")
-			.append("<remote name=\"remote2\" fetch=\"")
-			.append(notDefaultUri)
-			.append("\" />")
-			.append("<default revision=\"master\" remote=\"remote1\" />")
-			.append("<project path=\"foo\" name=\"")
-			.append(defaultUri)
-			.append("\" />")
-			.append("<project path=\"bar\" name=\".\" remote=\"remote2\" />")
-			.append("</manifest>");
-
-		Repository localDb = createWorkRepository();
-		JGitTestUtil.writeTrashFile(
-				localDb, "manifest.xml", xmlContent.toString());
-		RepoCommand command = new RepoCommand(localDb);
-		command
-			.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-			.setURI(rootUri)
-			.call();
-		File file = new File(localDb.getWorkTree(), "foo/hello.txt");
-		assertTrue("We should have foo", file.exists());
-		file = new File(localDb.getWorkTree(), "bar/world.txt");
-		assertTrue("We should have bar", file.exists());
-	}
-
 	@Test
 	public void testRemoteAlias() throws Exception {
 		StringBuilder xmlContent = new StringBuilder();
@@ -1125,5 +1245,6 @@
 		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");
+		testRelative("http://base.com/a/", "http://base.com/a/b", "b");
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
index ee8191f..3b11616 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
@@ -118,20 +118,41 @@
 		}
 	}
 
-	private LinkedHashSet<String> jgitIgnored() throws IOException {
+	private String[] cgitUntracked() throws Exception {
+		FS fs = db.getFS();
+		ProcessBuilder builder = fs.runInShell("git",
+				new String[] { "ls-files", "--exclude-standard", "-o" });
+		builder.directory(db.getWorkTree());
+		builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+		ExecutionResult result = fs.execute(builder,
+				new ByteArrayInputStream(new byte[0]));
+		String errorOut = toString(result.getStderr());
+		assertEquals("External git failed", "exit 0\n",
+				"exit " + result.getRc() + '\n' + errorOut);
+		try (BufferedReader r = new BufferedReader(new InputStreamReader(
+				new BufferedInputStream(result.getStdout().openInputStream()),
+				Constants.CHARSET))) {
+			return r.lines().toArray(String[]::new);
+		}
+	}
+
+	private void jgitIgnoredAndUntracked(LinkedHashSet<String> ignored,
+			LinkedHashSet<String> untracked) throws IOException {
 		// Do a tree walk that does descend into ignored directories and return
 		// a list of all ignored files
-		LinkedHashSet<String> result = new LinkedHashSet<>();
 		try (TreeWalk walk = new TreeWalk(db)) {
 			walk.addTree(new FileTreeIterator(db));
 			walk.setRecursive(true);
 			while (walk.next()) {
 				if (walk.getTree(WorkingTreeIterator.class).isEntryIgnored()) {
-					result.add(walk.getPathString());
+					ignored.add(walk.getPathString());
+				} else {
+					// tests of this class won't add any files to the index,
+					// hence everything what is not ignored is untracked
+					untracked.add(walk.getPathString());
 				}
 			}
 		}
-		return result;
 	}
 
 	private void assertNoIgnoredVisited(Set<String> ignored) throws Exception {
@@ -150,9 +171,13 @@
 	}
 
 	private void assertSameAsCGit(String... notIgnored) throws Exception {
-		LinkedHashSet<String> ignored = jgitIgnored();
+		LinkedHashSet<String> ignored = new LinkedHashSet<>();
+		LinkedHashSet<String> untracked = new LinkedHashSet<>();
+		jgitIgnoredAndUntracked(ignored, untracked);
 		String[] cgit = cgitIgnored();
+		String[] cgitUntracked = cgitUntracked();
 		assertArrayEquals(cgit, ignored.toArray());
+		assertArrayEquals(cgitUntracked, untracked.toArray());
 		for (String notExcluded : notIgnored) {
 			assertFalse("File " + notExcluded + " should not be ignored",
 					ignored.contains(notExcluded));
@@ -242,6 +267,34 @@
 	}
 
 	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles1() throws Exception {
+		createFiles("a", "dir/b", "dir/sub/c");
+		writeTrashFile(".gitignore", "**/\n");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles2() throws Exception {
+		createFiles("a", "dir/b", "dir/sub/c");
+		writeTrashFile(".gitignore", "**/**/\n");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles3() throws Exception {
+		createFiles("a", "x/b", "sub/x/c", "sub/x/d/e");
+		writeTrashFile(".gitignore", "x/**/\n");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testDirectoryWildmatchDoesNotMatchFiles4() throws Exception {
+		createFiles("a", "dir/x", "dir/sub1/x", "dir/sub2/x/y");
+		writeTrashFile(".gitignore", "**/x/\n");
+		assertSameAsCGit();
+	}
+
+	@Test
 	public void testUnescapedBracketsInGroup() throws Exception {
 		createFiles("[", "]", "[]", "][", "[[]", "[]]", "[[]]");
 		writeTrashFile(".gitignore", "[[]]\n");
@@ -268,4 +321,68 @@
 		writeTrashFile(".gitignore", "[\\[\\]]\n");
 		assertSameAsCGit();
 	}
+
+	@Test
+	public void testSimpleRootGitIgnoreGlobalNegation1() throws Exception {
+		// see IgnoreNodeTest.testSimpleRootGitIgnoreGlobalNegation1
+		createFiles("x1", "a/x2", "x3/y");
+		writeTrashFile(".gitignore", "*\n!x*");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testRepeatedNegationInDifferentFiles5() throws Exception {
+		// see IgnoreNodeTest.testRepeatedNegationInDifferentFiles5
+		createFiles("a/b/e/nothere.o");
+		writeTrashFile(".gitignore", "e");
+		writeTrashFile("a/.gitignore", "e");
+		writeTrashFile("a/b/.gitignore", "!e");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testRepeatedNegationInDifferentFilesWithWildmatcher1()
+			throws Exception {
+		createFiles("e", "x/e/f", "a/e/x1", "a/e/x2", "a/e/y", "a/e/sub/y");
+		writeTrashFile(".gitignore", "a/e/**");
+		writeTrashFile("a/.gitignore", "!e/x*");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testRepeatedNegationInDifferentFilesWithWildmatcher2()
+			throws Exception {
+		createFiles("e", "dir/f", "dir/g/h", "a/dir/i", "a/dir/j/k",
+				"a/b/dir/l", "a/b/dir/m/n", "a/b/dir/m/o/p", "a/q/dir/r",
+				"a/q/dir/dir/s", "c/d/dir/x", "c/d/dir/y");
+		writeTrashFile(".gitignore", "**/dir/*");
+		writeTrashFile("a/.gitignore", "!dir/*");
+		writeTrashFile("a/b/.gitignore", "!**/dir/*");
+		writeTrashFile("c/.gitignore", "!d/dir/x");
+		assertSameAsCGit();
+	}
+
+	@Test
+	public void testNegationForSubDirectoryWithinIgnoredDirectoryHasNoEffect1()
+			throws Exception {
+		createFiles("e", "a/f", "a/b/g", "a/b/h/i");
+		writeTrashFile(".gitignore", "a/b");
+		writeTrashFile("a/.gitignore", "!b/*");
+		assertSameAsCGit();
+	}
+
+	/*
+	 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=407475
+	 */
+	@Test
+	public void testNegationAllExceptJavaInSrcAndExceptChildDirInSrc()
+			throws Exception {
+		// see
+		// IgnoreNodeTest.testNegationAllExceptJavaInSrcAndExceptChildDirInSrc
+		createFiles("nothere.o", "src/keep.java", "src/nothere.o",
+				"src/a/keep.java", "src/a/keep.o");
+		writeTrashFile(".gitignore", "/*\n!/src/");
+		writeTrashFile("src/.gitignore", "*\n!*.java\n!*/");
+		assertSameAsCGit();
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
index 06164c8..2a1721e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
@@ -48,10 +48,18 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import org.junit.Before;
 import org.junit.Test;
 
 public class FastIgnoreRuleTest {
 
+	private boolean pathMatch;
+
+	@Before
+	public void setup() {
+		pathMatch = false;
+	}
+
 	@Test
 	public void testSimpleCharClass() {
 		assertMatched("][a]", "]a");
@@ -410,6 +418,19 @@
 
 		assertMatched("a/**/b/**/c", "a/c/b/d/c");
 		assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c");
+
+		assertMatched("**/", "a/");
+		assertMatched("**/", "a/b");
+		assertMatched("**/", "a/b/c");
+		assertMatched("**/**/", "a/");
+		assertMatched("**/**/", "a/b");
+		assertMatched("**/**/", "a/b/");
+		assertMatched("**/**/", "a/b/c");
+		assertMatched("x/**/", "x/a/");
+		assertMatched("x/**/", "x/a/b");
+		assertMatched("x/**/", "x/a/b/");
+		assertMatched("**/x/", "a/x/");
+		assertMatched("**/x/", "a/b/x/");
 	}
 
 	@Test
@@ -424,6 +445,10 @@
 		assertNotMatched("!/**/*.zip", "c/a/b.zip");
 		assertNotMatched("!**/*.zip", "c/a/b.zip");
 		assertNotMatched("a/**/b", "a/c/bb");
+
+		assertNotMatched("**/", "a");
+		assertNotMatched("**/**/", "a");
+		assertNotMatched("**/x/", "a/b/x");
 	}
 
 	@SuppressWarnings("unused")
@@ -465,6 +490,28 @@
 				split("/a/b/c/", '/').toArray());
 	}
 
+	@Test
+	public void testPathMatch() {
+		pathMatch = true;
+		assertMatched("a", "a");
+		assertMatched("a/", "a/");
+		assertNotMatched("a/", "a/b");
+
+		assertMatched("**", "a");
+		assertMatched("**", "a/");
+		assertMatched("**", "a/b");
+
+		assertNotMatched("**/", "a");
+		assertNotMatched("**/", "a/b");
+		assertMatched("**/", "a/");
+		assertMatched("**/", "a/b/");
+
+		assertNotMatched("x/**/", "x/a");
+		assertNotMatched("x/**/", "x/a/b");
+		assertMatched("x/**/", "x/a/");
+		assertMatched("x/**/", "x/y/a/");
+	}
+
 	private void assertMatched(String pattern, String path) {
 		boolean match = match(pattern, path);
 		String result = path + " is " + (match ? "ignored" : "not ignored")
@@ -520,7 +567,7 @@
 		FastIgnoreRule r = new FastIgnoreRule(pattern);
 		// If speed of this test is ever an issue, we can use a presetRule field
 		// to avoid recompiling a pattern each time.
-		boolean match = r.isMatch(target, isDirectory);
+		boolean match = r.isMatch(target, isDirectory, pathMatch);
 		if (r.getNegation())
 			match = !match;
 		return match;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
index 1178eb3..ccc64fb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
@@ -81,6 +81,141 @@
 	private TreeWalk walk;
 
 	@Test
+	public void testSimpleRootGitIgnoreGlobalIgnore() throws IOException {
+		writeIgnoreFile(".gitignore", "x");
+
+		writeTrashFile("a/x/file", "");
+		writeTrashFile("b/x", "");
+		writeTrashFile("x/file", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(D, ignored, "a/x");
+		assertEntry(F, ignored, "a/x/file");
+		assertEntry(D, tracked, "b");
+		assertEntry(F, ignored, "b/x");
+		assertEntry(D, ignored, "x");
+		assertEntry(F, ignored, "x/file");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreGlobalDirIgnore() throws IOException {
+		writeIgnoreFile(".gitignore", "x/");
+
+		writeTrashFile("a/x/file", "");
+		writeTrashFile("x/file", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(D, ignored, "a/x");
+		assertEntry(F, ignored, "a/x/file");
+		assertEntry(D, ignored, "x");
+		assertEntry(F, ignored, "x/file");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreWildMatcher() throws IOException {
+		writeIgnoreFile(".gitignore", "**");
+
+		writeTrashFile("a/x", "");
+		writeTrashFile("y", "");
+
+		beginWalk();
+		assertEntry(F, ignored, ".gitignore");
+		assertEntry(D, ignored, "a");
+		assertEntry(F, ignored, "a/x");
+		assertEntry(F, ignored, "y");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreWildMatcherDirOnly() throws IOException {
+		writeIgnoreFile(".gitignore", "**/");
+
+		writeTrashFile("a/x", "");
+		writeTrashFile("y", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, ignored, "a");
+		assertEntry(F, ignored, "a/x");
+		assertEntry(F, tracked, "y");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreGlobalNegation1() throws IOException {
+		writeIgnoreFile(".gitignore", "*", "!x*");
+		writeTrashFile("x1", "");
+		writeTrashFile("a/x2", "");
+		writeTrashFile("x3/y", "");
+
+		beginWalk();
+		assertEntry(F, ignored, ".gitignore");
+		assertEntry(D, ignored, "a");
+		assertEntry(F, ignored, "a/x2");
+		assertEntry(F, tracked, "x1");
+		assertEntry(D, tracked, "x3");
+		assertEntry(F, ignored, "x3/y");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreGlobalNegation2() throws IOException {
+		writeIgnoreFile(".gitignore", "*", "!x*", "!/a");
+		writeTrashFile("x1", "");
+		writeTrashFile("a/x2", "");
+		writeTrashFile("x3/y", "");
+
+		beginWalk();
+		assertEntry(F, ignored, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(F, tracked, "a/x2");
+		assertEntry(F, tracked, "x1");
+		assertEntry(D, tracked, "x3");
+		assertEntry(F, ignored, "x3/y");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreGlobalNegation3() throws IOException {
+		writeIgnoreFile(".gitignore", "*", "!x*", "!x*/**");
+		writeTrashFile("x1", "");
+		writeTrashFile("a/x2", "");
+		writeTrashFile("x3/y", "");
+
+		beginWalk();
+		assertEntry(F, ignored, ".gitignore");
+		assertEntry(D, ignored, "a");
+		assertEntry(F, ignored, "a/x2");
+		assertEntry(F, tracked, "x1");
+		assertEntry(D, tracked, "x3");
+		assertEntry(F, tracked, "x3/y");
+		endWalk();
+	}
+
+	@Test
+	public void testSimpleRootGitIgnoreGlobalNegation4() throws IOException {
+		writeIgnoreFile(".gitignore", "*", "!**/");
+		writeTrashFile("x1", "");
+		writeTrashFile("a/x2", "");
+		writeTrashFile("x3/y", "");
+
+		beginWalk();
+		assertEntry(F, ignored, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(F, ignored, "a/x2");
+		assertEntry(F, ignored, "x1");
+		assertEntry(D, tracked, "x3");
+		assertEntry(F, ignored, "x3/y");
+		endWalk();
+	}
+
+	@Test
 	public void testRules() throws IOException {
 		writeIgnoreFile(".git/info/exclude", "*~", "/out");
 
@@ -210,7 +345,7 @@
 		assertEntry(F, ignored, "src/.gitignore");
 		assertEntry(D, tracked, "src/a");
 		assertEntry(F, tracked, "src/a/keep.java");
-		assertEntry(F, tracked, "src/a/keep.o");
+		assertEntry(F, ignored, "src/a/keep.o");
 		assertEntry(F, tracked, "src/keep.java");
 		assertEntry(F, ignored, "src/nothere.o");
 		endWalk();
@@ -316,6 +451,103 @@
 	}
 
 	@Test
+	public void testRepeatedNegationInDifferentFiles5() throws IOException {
+		writeIgnoreFile(".gitignore", "e");
+		writeIgnoreFile("a/.gitignore", "e");
+		writeIgnoreFile("a/b/.gitignore", "!e");
+		writeTrashFile("a/b/e/nothere.o", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(F, tracked, "a/.gitignore");
+		assertEntry(D, tracked, "a/b");
+		assertEntry(F, tracked, "a/b/.gitignore");
+		assertEntry(D, tracked, "a/b/e");
+		assertEntry(F, tracked, "a/b/e/nothere.o");
+		endWalk();
+	}
+
+	@Test
+	public void testIneffectiveNegationDifferentLevels1() throws IOException {
+		writeIgnoreFile(".gitignore", "a/b/e/", "!a/b/e/*");
+		writeTrashFile("a/b/e/nothere.o", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(D, tracked, "a/b");
+		assertEntry(D, ignored, "a/b/e");
+		assertEntry(F, ignored, "a/b/e/nothere.o");
+		endWalk();
+	}
+
+	@Test
+	public void testIneffectiveNegationDifferentLevels2() throws IOException {
+		writeIgnoreFile(".gitignore", "a/b/e/");
+		writeIgnoreFile("a/.gitignore", "!b/e/*");
+		writeTrashFile("a/b/e/nothere.o", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(F, tracked, "a/.gitignore");
+		assertEntry(D, tracked, "a/b");
+		assertEntry(D, ignored, "a/b/e");
+		assertEntry(F, ignored, "a/b/e/nothere.o");
+		endWalk();
+	}
+
+	@Test
+	public void testIneffectiveNegationDifferentLevels3() throws IOException {
+		writeIgnoreFile(".gitignore", "a/b/e/");
+		writeIgnoreFile("a/b/.gitignore", "!e/*");
+		writeTrashFile("a/b/e/nothere.o", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(D, tracked, "a/b");
+		assertEntry(F, tracked, "a/b/.gitignore");
+		assertEntry(D, ignored, "a/b/e");
+		assertEntry(F, ignored, "a/b/e/nothere.o");
+		endWalk();
+	}
+
+	@Test
+	public void testIneffectiveNegationDifferentLevels4() throws IOException {
+		writeIgnoreFile(".gitignore", "a/b/e/");
+		writeIgnoreFile("a/b/e/.gitignore", "!*");
+		writeTrashFile("a/b/e/nothere.o", "");
+
+		beginWalk();
+		assertEntry(F, tracked, ".gitignore");
+		assertEntry(D, tracked, "a");
+		assertEntry(D, tracked, "a/b");
+		assertEntry(D, ignored, "a/b/e");
+		assertEntry(F, ignored, "a/b/e/.gitignore");
+		assertEntry(F, ignored, "a/b/e/nothere.o");
+		endWalk();
+	}
+
+	@Test
+	public void testIneffectiveNegationDifferentLevels5() throws IOException {
+		writeIgnoreFile("a/.gitignore", "b/e/");
+		writeIgnoreFile("a/b/.gitignore", "!e/*");
+		writeTrashFile("a/b/e/nothere.o", "");
+
+		beginWalk();
+		assertEntry(D, tracked, "a");
+		assertEntry(F, tracked, "a/.gitignore");
+		assertEntry(D, tracked, "a/b");
+		assertEntry(F, tracked, "a/b/.gitignore");
+		assertEntry(D, ignored, "a/b/e");
+		assertEntry(F, ignored, "a/b/e/nothere.o");
+		endWalk();
+	}
+
+	@SuppressWarnings("deprecation")
+	@Test
 	public void testEmptyIgnoreNode() {
 		// Rules are never empty: WorkingTreeIterator optimizes empty files away
 		// So we have to test it manually in case third party clients use
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java
index 3db2792..d5d3857 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/indexdiff/IndexDiffWithSymlinkTest.java
@@ -111,14 +111,12 @@
 				&& FS.DETECTED.supportsSymlinks());
 		super.setUp();
 		File testDir = createTempDirectory(this.getClass().getSimpleName());
-		InputStream in = this.getClass().getClassLoader().getResourceAsStream(
+		try (InputStream in = this.getClass().getClassLoader()
+				.getResourceAsStream(
 				this.getClass().getPackage().getName().replace('.', '/') + '/'
-						+ FILEREPO + ".txt");
-		assertNotNull("Test repo file not found", in);
-		try {
+								+ FILEREPO + ".txt")) {
+			assertNotNull("Test repo file not found", in);
 			testRepoDir = restoreGitRepo(in, testDir, FILEREPO);
-		} finally {
-			in.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
index a9edf73..1486348 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
@@ -187,7 +187,10 @@
 			PackIndexWriter writer = new PackIndexWriterV2(dst);
 			writer.write(objects, new byte[OBJECT_ID_LENGTH]);
 		}
-		new FileOutputStream(packFile).close();
+
+		try (FileOutputStream unused = new FileOutputStream(packFile)) {
+			// unused
+		}
 
 		assertEquals(id.abbreviate(20), reader.abbreviate(id, 2));
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
index 9998666..9ceaa34 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
@@ -135,11 +135,8 @@
 	}
 
 	private static void append(File f, byte b) throws IOException {
-		FileOutputStream os = new FileOutputStream(f, true);
-		try {
+		try (FileOutputStream os = new FileOutputStream(f, true)) {
 			os.write(b);
-		} finally {
-			os.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java
index b37cd7a..3caae72 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDeleteEmptyRefsFoldersTest.java
@@ -129,4 +129,4 @@
 		assertTrue(ref01.toFile().exists());
 		assertTrue(ref02.toFile().exists());
 	}
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java
index b782ce8..8e438bc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackInserterTest.java
@@ -95,6 +95,8 @@
 public class PackInserterTest extends RepositoryTestCase {
 	private WindowCacheConfig origWindowCacheConfig;
 
+	private static final Random random = new Random(0);
+
 	@Before
 	public void setWindowCacheConfig() {
 		origWindowCacheConfig = new WindowCacheConfig();
@@ -518,7 +520,7 @@
 
 	private static byte[] newLargeBlob() {
 		byte[] blob = new byte[10240];
-		new Random(0).nextBytes(blob);
+		random.nextBytes(blob);
 		return blob;
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
index 63bd7f4..dc05eea 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
@@ -270,19 +270,16 @@
 
 	private void setupReflog(String logName, byte[] data)
 			throws FileNotFoundException, IOException {
-				File logfile = new File(db.getDirectory(), logName);
-				if (!logfile.getParentFile().mkdirs()
-						&& !logfile.getParentFile().isDirectory()) {
-					throw new IOException(
-							"oops, cannot create the directory for the test reflog file"
-									+ logfile);
-				}
-				FileOutputStream fileOutputStream = new FileOutputStream(logfile);
-				try {
-					fileOutputStream.write(data);
-				} finally {
-					fileOutputStream.close();
-				}
-			}
+		File logfile = new File(db.getDirectory(), logName);
+		if (!logfile.getParentFile().mkdirs()
+				&& !logfile.getParentFile().isDirectory()) {
+			throw new IOException(
+					"oops, cannot create the directory for the test reflog file"
+							+ logfile);
+		}
+		try (FileOutputStream fileOutputStream = new FileOutputStream(logfile)) {
+			fileOutputStream.write(data);
+		}
+	}
 
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
index 7f40bae..1d188c3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
@@ -87,11 +87,8 @@
 					"oops, cannot create the directory for the test reflog file"
 							+ logfile);
 		}
-		FileInputStream fileInputStream = new FileInputStream(logfile);
-		try {
+		try (FileInputStream fileInputStream = new FileInputStream(logfile)) {
 			fileInputStream.read(buffer);
-		} finally {
-			fileInputStream.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index aa50697..d7505af 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -47,6 +47,7 @@
 package org.eclipse.jgit.internal.storage.file;
 
 import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -521,7 +522,7 @@
 				4294967295000L, 60));
 		commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
 				4294967295000L, 60));
-		commit.setEncoding("UTF-8");
+		commit.setEncoding(UTF_8);
 		commit.setMessage("\u00dcbergeeks");
 		ObjectId cid = insertCommit(commit);
 		assertEquals("4680908112778718f37e686cbebcc912730b3154", cid.name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
index adba048..ec60bd9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
@@ -218,6 +218,27 @@
 	}
 
 	@Test
+	public void scanDuplicates() throws IOException {
+		List<Ref> delta1 = Arrays.asList(
+				ref("refs/heads/apple", 1),
+				ref("refs/heads/banana", 2));
+		List<Ref> delta2 = Arrays.asList(
+				ref("refs/heads/apple", 3),
+				ref("refs/heads/apple", 4));
+
+		MergedReftable mr = merge(write(delta1, 1000), write(delta2, 2000));
+		try (RefCursor rc = mr.allRefs()) {
+			assertTrue(rc.next());
+			assertEquals("refs/heads/apple", rc.getRef().getName());
+			assertEquals(id(3), rc.getRef().getObjectId());
+			assertTrue(rc.next());
+			assertEquals("refs/heads/banana", rc.getRef().getName());
+			assertEquals(id(2), rc.getRef().getObjectId());
+			assertFalse(rc.next());
+		}
+	}
+
+	@Test
 	public void scanIncludeDeletes() throws IOException {
 		List<Ref> delta1 = Arrays.asList(ref("refs/heads/next", 4));
 		List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index fb1ee8c..7862005 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -808,8 +808,14 @@
 			fbConfig.load();
 			fail();
 		} catch (ConfigInvalidException cie) {
-			assertEquals(JGitText.get().tooManyIncludeRecursions,
-					cie.getCause().getMessage());
+			for (Throwable t = cie; t != null; t = t.getCause()) {
+				if (t.getMessage()
+						.equals(JGitText.get().tooManyIncludeRecursions)) {
+					return;
+				}
+			}
+			fail("Expected to find expected exception message: "
+					+ JGitText.get().tooManyIncludeRecursions);
 		}
 	}
 
@@ -824,6 +830,80 @@
 		assertFalse(parsed.getBoolean("foo", "bar", false));
 	}
 
+	@Test
+	public void testIncludeCaseInsensitiveSection()
+			throws IOException, ConfigInvalidException {
+		File included = tmp.newFile("included");
+		String content = "[foo]\nbar=true\n";
+		Files.write(included.toPath(), content.getBytes());
+
+		File config = tmp.newFile("config");
+		content = "[Include]\npath=" + pathToString(included) + "\n";
+		Files.write(config.toPath(), content.getBytes());
+
+		FileBasedConfig fbConfig = new FileBasedConfig(null, config,
+				FS.DETECTED);
+		fbConfig.load();
+		assertTrue(fbConfig.getBoolean("foo", "bar", false));
+	}
+
+	@Test
+	public void testIncludeCaseInsensitiveKey()
+			throws IOException, ConfigInvalidException {
+		File included = tmp.newFile("included");
+		String content = "[foo]\nbar=true\n";
+		Files.write(included.toPath(), content.getBytes());
+
+		File config = tmp.newFile("config");
+		content = "[include]\nPath=" + pathToString(included) + "\n";
+		Files.write(config.toPath(), content.getBytes());
+
+		FileBasedConfig fbConfig = new FileBasedConfig(null, config,
+				FS.DETECTED);
+		fbConfig.load();
+		assertTrue(fbConfig.getBoolean("foo", "bar", false));
+	}
+
+	@Test
+	public void testIncludeExceptionContainsLine() {
+		try {
+			parse("[include]\npath=\n");
+			fail("Expected ConfigInvalidException");
+		} catch (ConfigInvalidException e) {
+			assertTrue(
+					"Expected to find the problem line in the exception message",
+					e.getMessage().contains("include.path"));
+		}
+	}
+
+	@Test
+	public void testIncludeExceptionContainsFile() throws IOException {
+		File included = tmp.newFile("included");
+		String includedPath = pathToString(included);
+		String content = "[include]\npath=\n";
+		Files.write(included.toPath(), content.getBytes());
+
+		File config = tmp.newFile("config");
+		String include = "[include]\npath=" + includedPath + "\n";
+		Files.write(config.toPath(), include.getBytes());
+		FileBasedConfig fbConfig = new FileBasedConfig(null, config,
+				FS.DETECTED);
+		try {
+			fbConfig.load();
+			fail("Expected ConfigInvalidException");
+		} catch (ConfigInvalidException e) {
+			// Check that there is some exception in the chain that contains
+			// includedPath
+			for (Throwable t = e; t != null; t = t.getCause()) {
+				if (t.getMessage().contains(includedPath)) {
+					return;
+				}
+			}
+			fail("Expected to find the path in the exception message: "
+					+ includedPath);
+		}
+	}
+
 	private static void assertReadLong(long exp) throws ConfigInvalidException {
 		assertReadLong(exp, String.valueOf(exp));
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 4d42bd1..0412242 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
@@ -1923,18 +1923,20 @@
 				if (file.isFile()) {
 					assertNotNull("found unexpected file for path " + path
 							+ " in workdir", expectedValue);
-					FileInputStream is = new FileInputStream(file);
-					byte[] buffer = new byte[(int) file.length()];
-					int offset = 0;
-					int numRead = 0;
-					while (offset < buffer.length
-							&& (numRead = is.read(buffer, offset, buffer.length
-									- offset)) >= 0) {
-						offset += numRead;
+					try (FileInputStream is = new FileInputStream(file)) {
+						byte[] buffer = new byte[(int) file.length()];
+						int offset = 0;
+						int numRead = 0;
+						while (offset < buffer.length
+								&& (numRead = is.read(buffer, offset,
+										buffer.length - offset)) >= 0) {
+							offset += numRead;
+						}
+						assertArrayEquals(
+								"unexpected content for path " + path
+										+ " in workDir. ",
+								buffer, i.get(path).getBytes());
 					}
-					is.close();
-					assertArrayEquals("unexpected content for path " + path
-							+ " in workDir. ", buffer, i.get(path).getBytes());
 					nrFiles++;
 				} else if (file.isDirectory()) {
 					String[] files = file.list();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
index 4f26a27..347883f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java
@@ -68,13 +68,11 @@
 		assertEquals(db.readMergeHeads().size(), 2);
 		assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
 		assertEquals(db.readMergeHeads().get(1), ObjectId.fromString(sampleId));
+
 		// same test again, this time with lower-level io
-		FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(),
-		"MERGE_HEAD"));
-		try {
+		try (FileOutputStream fos = new FileOutputStream(
+				new File(db.getDirectory(), "MERGE_HEAD"));) {
 			fos.write("0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n".getBytes(Constants.CHARACTER_ENCODING));
-		} finally {
-			fos.close();
 		}
 		assertEquals(db.readMergeHeads().size(), 2);
 		assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId());
@@ -82,12 +80,9 @@
 		db.writeMergeHeads(Collections.<ObjectId> emptyList());
 		assertEquals(read(new File(db.getDirectory(), "MERGE_HEAD")), "");
 		assertEquals(db.readMergeHeads(), null);
-		fos = new FileOutputStream(new File(db.getDirectory(),
-				"MERGE_HEAD"));
-		try {
+		try (FileOutputStream fos = new FileOutputStream(
+				new File(db.getDirectory(), "MERGE_HEAD"))) {
 			fos.write(sampleId.getBytes(Constants.CHARACTER_ENCODING));
-		} finally {
-			fos.close();
 		}
 		assertEquals(db.readMergeHeads().size(), 1);
 		assertEquals(db.readMergeHeads().get(0), ObjectId.fromString(sampleId));
@@ -103,12 +98,9 @@
 		db.writeMergeCommitMsg(null);
 		assertEquals(db.readMergeCommitMsg(), null);
 		assertFalse(new File(db.getDirectory(), "MERGE_MSG").exists());
-		FileOutputStream fos = new FileOutputStream(new File(db.getDirectory(),
-				Constants.MERGE_MSG));
-		try {
+		try (FileOutputStream fos = new FileOutputStream(
+				new File(db.getDirectory(), Constants.MERGE_MSG))) {
 			fos.write(mergeMsg.getBytes(Constants.CHARACTER_ENCODING));
-		} finally {
-			fos.close();
 		}
 		assertEquals(db.readMergeCommitMsg(), mergeMsg);
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java
new file mode 100644
index 0000000..d98b792
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSerializerTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018, David Pursehouse <david.pursehouse@gmail.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.lib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.junit.Test;
+
+public class ObjectIdSerializerTest {
+	@Test
+	public void serialize() throws Exception {
+		ObjectId original = new ObjectId(1, 2, 3, 4, 5);
+		ObjectId deserialized = writeAndReadBackFromTempFile(original);
+		assertEquals(original, deserialized);
+	}
+
+	@Test
+	public void serializeZeroId() throws Exception {
+		ObjectId original = ObjectId.zeroId();
+		ObjectId deserialized = writeAndReadBackFromTempFile(original);
+		assertEquals(original, deserialized);
+	}
+
+	@Test
+	public void serializeNull() throws Exception {
+		ObjectId deserialized = writeAndReadBackFromTempFile(null);
+		assertNull(deserialized);
+	}
+
+	private ObjectId writeAndReadBackFromTempFile(ObjectId objectId)
+			throws Exception {
+		File file = File.createTempFile("ObjectIdSerializerTest_", "");
+		try (OutputStream out = new FileOutputStream(file)) {
+			if (objectId == null) {
+				ObjectIdSerializer.write(out, objectId);
+			} else {
+				ObjectIdSerializer.writeWithoutMarker(out, objectId);
+			}
+		}
+		try (InputStream in = new FileInputStream(file)) {
+			if (objectId == null) {
+				return ObjectIdSerializer.read(in);
+			} else {
+				return ObjectIdSerializer.readWithoutMarker(in);
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
index 039a6e8..190224a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
@@ -817,40 +817,35 @@
 
 	void modifyWorktree(WorktreeState worktreeState, String path, String other)
 			throws Exception {
-		FileOutputStream fos = null;
-		ObjectId bloblId;
-
-		try {
-			switch (worktreeState) {
-			case Missing:
-				new File(db.getWorkTree(), path).delete();
-				break;
-			case DifferentFromHeadAndOther:
-				write(new File(db.getWorkTree(), path),
-						Integer.toString(counter++));
-				break;
-			case SameAsHead:
-				bloblId = contentId(Constants.HEAD, path);
-				fos = new FileOutputStream(new File(db.getWorkTree(), path));
-				db.newObjectReader().open(bloblId).copyTo(fos);
-				break;
-			case SameAsOther:
-				bloblId = contentId(other, path);
-				fos = new FileOutputStream(new File(db.getWorkTree(), path));
-				db.newObjectReader().open(bloblId).copyTo(fos);
-				break;
-			case Bare:
-				if (db.isBare())
-					return;
-				File workTreeFile = db.getWorkTree();
-				db.getConfig().setBoolean("core", null, "bare", true);
-				db.getDirectory().renameTo(new File(workTreeFile, "test.git"));
-				db = new FileRepository(new File(workTreeFile, "test.git"));
-				db_t = new TestRepository<>(db);
+		switch (worktreeState) {
+		case Missing:
+			new File(db.getWorkTree(), path).delete();
+			break;
+		case DifferentFromHeadAndOther:
+			write(new File(db.getWorkTree(), path),
+					Integer.toString(counter++));
+			break;
+		case SameAsHead:
+			try (FileOutputStream fos = new FileOutputStream(
+					new File(db.getWorkTree(), path))) {
+				db.newObjectReader().open(contentId(Constants.HEAD, path))
+						.copyTo(fos);
 			}
-		} finally {
-			if (fos != null)
-				fos.close();
+			break;
+		case SameAsOther:
+			try (FileOutputStream fos = new FileOutputStream(
+					new File(db.getWorkTree(), path))) {
+				db.newObjectReader().open(contentId(other, path)).copyTo(fos);
+			}
+			break;
+		case Bare:
+			if (db.isBare())
+				return;
+			File workTreeFile = db.getWorkTree();
+			db.getConfig().setBoolean("core", null, "bare", true);
+			db.getDirectory().renameTo(new File(workTreeFile, "test.git"));
+			db = new FileRepository(new File(workTreeFile, "test.git"));
+			db_t = new TestRepository<>(db);
 		}
 	}
 
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 3272d59..9322a47 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
@@ -46,6 +46,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.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayOutputStream;
@@ -53,6 +55,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Map;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.MergeResult;
@@ -61,21 +64,29 @@
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 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.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.lib.StoredConfig;
 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.storage.file.FileBasedConfig;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
@@ -387,6 +398,38 @@
 				mergeResult.getMergeStatus());
 	}
 
+	@Theory
+	public void mergeWithCrlfAutoCrlfTrue(MergeStrategy strategy)
+			throws IOException, GitAPIException {
+		Git git = Git.wrap(db);
+		db.getConfig().setString("core", null, "autocrlf", "true");
+		db.getConfig().save();
+		writeTrashFile("crlf.txt", "a crlf file\r\n");
+		git.add().addFilepattern("crlf.txt").call();
+		git.commit().setMessage("base").call();
+
+		git.branchCreate().setName("brancha").call();
+
+		writeTrashFile("crlf.txt", "a crlf file\r\na second line\r\n");
+		git.add().addFilepattern("crlf.txt").call();
+		git.commit().setMessage("on master").call();
+
+		git.checkout().setName("brancha").call();
+		File testFile = writeTrashFile("crlf.txt",
+				"a first line\r\na crlf file\r\n");
+		git.add().addFilepattern("crlf.txt").call();
+		git.commit().setMessage("on brancha").call();
+
+		MergeResult mergeResult = git.merge().setStrategy(strategy)
+				.include(db.resolve("master")).call();
+		assertEquals(MergeResult.MergeStatus.MERGED,
+				mergeResult.getMergeStatus());
+		checkFile(testFile, "a first line\r\na crlf file\r\na second line\r\n");
+		assertEquals(
+				"[crlf.txt, mode:100644, content:a first line\na crlf file\na second line\n]",
+				indexState(CONTENT));
+	}
+
 	/**
 	 * Merging two equal subtrees when the index does not contain any file in
 	 * that subtree should lead to a merged state.
@@ -1098,6 +1141,146 @@
 				indexState(CONTENT));
 	}
 
+	/**
+	 * Merging two conflicting submodules when the index does not contain any
+	 * entry for that submodule.
+	 *
+	 * @param strategy
+	 * @throws Exception
+	 */
+	@Theory
+	public void checkMergeConflictingSubmodulesWithoutIndex(
+			MergeStrategy strategy) throws Exception {
+		Git git = Git.wrap(db);
+		writeTrashFile("initial", "initial");
+		git.add().addFilepattern("initial").call();
+		RevCommit initial = git.commit().setMessage("initial").call();
+
+		writeSubmodule("one", ObjectId
+				.fromString("1000000000000000000000000000000000000000"));
+		git.add().addFilepattern(Constants.DOT_GIT_MODULES).call();
+		RevCommit right = git.commit().setMessage("added one").call();
+
+		// a second commit in the submodule
+
+		git.checkout().setStartPoint(initial).setName("left")
+				.setCreateBranch(true).call();
+		writeSubmodule("one", ObjectId
+				.fromString("2000000000000000000000000000000000000000"));
+
+		git.add().addFilepattern(Constants.DOT_GIT_MODULES).call();
+		git.commit().setMessage("a different one").call();
+
+		MergeResult result = git.merge().setStrategy(strategy).include(right)
+				.call();
+
+		assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+		Map<String, int[][]> conflicts = result.getConflicts();
+		assertEquals(1, conflicts.size());
+		assertNotNull(conflicts.get("one"));
+	}
+
+	/**
+	 * Merging two non-conflicting submodules when the index does not contain
+	 * any entry for either submodule.
+	 *
+	 * @param strategy
+	 * @throws Exception
+	 */
+	@Theory
+	public void checkMergeNonConflictingSubmodulesWithoutIndex(
+			MergeStrategy strategy) throws Exception {
+		Git git = Git.wrap(db);
+		writeTrashFile("initial", "initial");
+		git.add().addFilepattern("initial").call();
+
+		writeSubmodule("one", ObjectId
+				.fromString("1000000000000000000000000000000000000000"));
+
+		// Our initial commit should include a .gitmodules with a bunch of
+		// comment lines, so that
+		// we don't have a content merge issue when we add a new submodule at
+		// the top and a different
+		// one at the bottom. This is sort of a hack, but it should allow
+		// add/add submodule merges
+		String existing = read(Constants.DOT_GIT_MODULES);
+		String context = "\n# context\n# more context\n# yet more context\n";
+		write(new File(db.getWorkTree(), Constants.DOT_GIT_MODULES),
+				existing + context + context + context);
+
+		git.add().addFilepattern(Constants.DOT_GIT_MODULES).call();
+		RevCommit initial = git.commit().setMessage("initial").call();
+
+		writeSubmodule("two", ObjectId
+				.fromString("1000000000000000000000000000000000000000"));
+		git.add().addFilepattern(Constants.DOT_GIT_MODULES).call();
+
+		RevCommit right = git.commit().setMessage("added two").call();
+
+		git.checkout().setStartPoint(initial).setName("left")
+				.setCreateBranch(true).call();
+
+		// we need to manually create the submodule for three for the
+		// .gitmodules hackery
+		addSubmoduleToIndex("three", ObjectId
+				.fromString("1000000000000000000000000000000000000000"));
+		new File(db.getWorkTree(), "three").mkdir();
+
+		existing = read(Constants.DOT_GIT_MODULES);
+		String three = "[submodule \"three\"]\n\tpath = three\n\turl = "
+				+ db.getDirectory().toURI() + "\n";
+		write(new File(db.getWorkTree(), Constants.DOT_GIT_MODULES),
+				three + existing);
+
+		git.add().addFilepattern(Constants.DOT_GIT_MODULES).call();
+		git.commit().setMessage("a different one").call();
+
+		MergeResult result = git.merge().setStrategy(strategy).include(right)
+				.call();
+
+		assertNull(result.getCheckoutConflicts());
+		assertNull(result.getFailingPaths());
+		for (String dir : Arrays.asList("one", "two", "three")) {
+			assertTrue(new File(db.getWorkTree(), dir).isDirectory());
+		}
+	}
+
+	private void writeSubmodule(String path, ObjectId commit)
+			throws IOException, ConfigInvalidException {
+		addSubmoduleToIndex(path, commit);
+		new File(db.getWorkTree(), path).mkdir();
+
+		StoredConfig config = db.getConfig();
+		config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+				ConfigConstants.CONFIG_KEY_URL,
+				db.getDirectory().toURI().toString());
+		config.save();
+
+		FileBasedConfig modulesConfig = new FileBasedConfig(
+				new File(db.getWorkTree(), Constants.DOT_GIT_MODULES),
+				db.getFS());
+		modulesConfig.load();
+		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+				ConfigConstants.CONFIG_KEY_PATH, path);
+		modulesConfig.save();
+
+	}
+
+	private void addSubmoduleToIndex(String path, ObjectId commit)
+			throws IOException {
+		DirCache cache = db.lockDirCache();
+		DirCacheEditor editor = cache.editor();
+		editor.add(new DirCacheEditor.PathEdit(path) {
+
+			@Override
+			public void apply(DirCacheEntry ent) {
+				ent.setFileMode(FileMode.GITLINK);
+				ent.setObjectId(commit);
+			}
+		});
+		editor.commit();
+	}
+
 	// Assert that every specified index entry has the same last modification
 	// timestamp as the associated file
 	private void checkConsistentLastModified(String... pathes)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java
index 61bd8cf..6027aff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java
@@ -90,17 +90,14 @@
 	}
 
 	private Patch parseTestPatchFile(final String patchFile) throws IOException {
-		final InputStream in = getClass().getResourceAsStream(patchFile);
-		if (in == null) {
-			fail("No " + patchFile + " test vector");
-			return null; // Never happens
-		}
-		try {
+		try (InputStream in = getClass().getResourceAsStream(patchFile)) {
+			if (in == null) {
+				fail("No " + patchFile + " test vector");
+				return null; // Never happens
+			}
 			final Patch p = new Patch();
 			p.parse(in);
 			return p;
-		} finally {
-			in.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java
index 2aaf6af..65375c7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java
@@ -43,6 +43,8 @@
 
 package org.eclipse.jgit.patch;
 
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -58,7 +60,7 @@
 public class GetTextTest {
 	@Test
 	public void testGetText_BothISO88591() throws IOException {
-		final Charset cs = Charset.forName("ISO-8859-1");
+		final Charset cs = ISO_8859_1;
 		final Patch p = parseTestPatchFile();
 		assertTrue(p.getErrors().isEmpty());
 		assertEquals(1, p.getFiles().size());
@@ -69,7 +71,7 @@
 
 	@Test
 	public void testGetText_NoBinary() throws IOException {
-		final Charset cs = Charset.forName("ISO-8859-1");
+		final Charset cs = ISO_8859_1;
 		final Patch p = parseTestPatchFile();
 		assertTrue(p.getErrors().isEmpty());
 		assertEquals(1, p.getFiles().size());
@@ -80,8 +82,8 @@
 
 	@Test
 	public void testGetText_Convert() throws IOException {
-		final Charset csOld = Charset.forName("ISO-8859-1");
-		final Charset csNew = Charset.forName("UTF-8");
+		final Charset csOld = ISO_8859_1;
+		final Charset csNew = UTF_8;
 		final Patch p = parseTestPatchFile();
 		assertTrue(p.getErrors().isEmpty());
 		assertEquals(1, p.getFiles().size());
@@ -100,8 +102,8 @@
 
 	@Test
 	public void testGetText_DiffCc() throws IOException {
-		final Charset csOld = Charset.forName("ISO-8859-1");
-		final Charset csNew = Charset.forName("UTF-8");
+		final Charset csOld = ISO_8859_1;
+		final Charset csNew = UTF_8;
 		final Patch p = parseTestPatchFile();
 		assertTrue(p.getErrors().isEmpty());
 		assertEquals(1, p.getFiles().size());
@@ -121,28 +123,24 @@
 
 	private Patch parseTestPatchFile() throws IOException {
 		final String patchFile = JGitTestUtil.getName() + ".patch";
-		final InputStream in = getClass().getResourceAsStream(patchFile);
-		if (in == null) {
-			fail("No " + patchFile + " test vector");
-			return null; // Never happens
-		}
-		try {
+		try (InputStream in = getClass().getResourceAsStream(patchFile)) {
+			if (in == null) {
+				fail("No " + patchFile + " test vector");
+				return null; // Never happens
+			}
 			final Patch p = new Patch();
 			p.parse(in);
 			return p;
-		} finally {
-			in.close();
 		}
 	}
 
 	private String readTestPatchFile(final Charset cs) throws IOException {
 		final String patchFile = JGitTestUtil.getName() + ".patch";
-		final InputStream in = getClass().getResourceAsStream(patchFile);
-		if (in == null) {
-			fail("No " + patchFile + " test vector");
-			return null; // Never happens
-		}
-		try {
+		try (InputStream in = getClass().getResourceAsStream(patchFile)) {
+			if (in == null) {
+				fail("No " + patchFile + " test vector");
+				return null; // Never happens
+			}
 			final InputStreamReader r = new InputStreamReader(in, cs);
 			char[] tmp = new char[2048];
 			final StringBuilder s = new StringBuilder();
@@ -150,8 +148,6 @@
 			while ((n = r.read(tmp)) > 0)
 				s.append(tmp, 0, n);
 			return s.toString();
-		} finally {
-			in.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
index 2a54dc6..7f0d602 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java
@@ -242,11 +242,8 @@
 		dir.mkdirs();
 
 		File f = File.createTempFile(getClass().getName(), null, dir);
-		FileOutputStream os = new FileOutputStream(f, true);
-		try {
+		try (FileOutputStream os = new FileOutputStream(f, true)) {
 			os.write(content);
-		} finally {
-			os.close();
 		}
 		return f;
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
index b926e48..86c92bb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
@@ -60,6 +60,9 @@
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackStatistics;
+import org.eclipse.jgit.transport.BasePackFetchConnection.FetchConfig;
 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
 import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
 import org.eclipse.jgit.transport.resolver.UploadPackFactory;
@@ -70,6 +73,11 @@
 public class TestProtocolTest {
 	private static final RefSpec HEADS = new RefSpec("+refs/heads/*:refs/heads/*");
 
+	private static final RefSpec MASTER = new RefSpec(
+			"+refs/heads/master:refs/heads/master");
+
+	private static final int HAVES_PER_ROUND = 32;
+
 	private static class User {
 		private final String name;
 
@@ -81,7 +89,14 @@
 	private static class DefaultUpload implements UploadPackFactory<User> {
 		@Override
 		public UploadPack create(User req, Repository db) {
-			return new UploadPack(db);
+			UploadPack up = new UploadPack(db);
+			up.setPostUploadHook(new PostUploadHook() {
+				@Override
+				public void onPostUpload(PackStatistics stats) {
+					havesCount = stats.getHaves();
+				}
+			});
+			return up;
 		}
 	}
 
@@ -92,6 +107,8 @@
 		}
 	}
 
+	private static long havesCount;
+
 	private List<TransportProtocol> protos;
 	private TestRepository<InMemoryRepository> local;
 	private TestRepository<InMemoryRepository> remote;
@@ -147,6 +164,68 @@
 	}
 
 	@Test
+	public void testFullNegotiation() throws Exception {
+		TestProtocol<User> proto = registerDefault();
+		URIish uri = proto.register(new User("user"), remote.getRepository());
+
+		// Enough local branches to cause 10 rounds of negotiation,
+		// and a unique remote master branch commit with a later timestamp.
+		for (int i = 0; i < 10 * HAVES_PER_ROUND; i++) {
+			local.branch("local-branch-" + i).commit().create();
+		}
+		remote.tick(11 * HAVES_PER_ROUND);
+		RevCommit master = remote.branch("master").commit()
+				.add("readme.txt", "unique commit").create();
+
+		try (Git git = new Git(local.getRepository())) {
+			assertNull(local.getRepository().exactRef("refs/heads/master"));
+			git.fetch().setRemote(uri.toString()).setRefSpecs(MASTER).call();
+			assertEquals(master, local.getRepository()
+					.exactRef("refs/heads/master").getObjectId());
+			assertEquals(10 * HAVES_PER_ROUND, havesCount);
+		}
+	}
+
+	@Test
+	public void testMinimalNegotiation() throws Exception {
+		TestProtocol<User> proto = registerDefault();
+		URIish uri = proto.register(new User("user"), remote.getRepository());
+
+		// Enough local branches to cause 10 rounds of negotiation,
+		// and a unique remote master branch commit with a later timestamp.
+		for (int i = 0; i < 10 * HAVES_PER_ROUND; i++) {
+			local.branch("local-branch-" + i).commit().create();
+		}
+		remote.tick(11 * HAVES_PER_ROUND);
+		RevCommit master = remote.branch("master").commit()
+				.add("readme.txt", "unique commit").create();
+
+		TestProtocol.setFetchConfig(new FetchConfig(true, true));
+		try (Git git = new Git(local.getRepository())) {
+			assertNull(local.getRepository().exactRef("refs/heads/master"));
+			git.fetch().setRemote(uri.toString()).setRefSpecs(MASTER).call();
+			assertEquals(master, local.getRepository()
+					.exactRef("refs/heads/master").getObjectId());
+			assertTrue(havesCount <= 2 * HAVES_PER_ROUND);
+
+			// Update the remote master and add local branches for 5 more rounds
+			// of negotiation, where the local branch commits have newer
+			// timestamps. Negotiation should send 5 rounds for those newer
+			// branches before getting to the round that sends its stale version
+			// of master.
+			master = remote.branch("master").commit().parent(master).create();
+			local.tick(2 * HAVES_PER_ROUND);
+			for (int i = 0; i < 5 * HAVES_PER_ROUND; i++) {
+				local.branch("local-" + i).commit().create();
+			}
+			git.fetch().setRemote(uri.toString()).setRefSpecs(MASTER).call();
+			assertEquals(master, local.getRepository()
+					.exactRef("refs/heads/master").getObjectId());
+			assertEquals(6 * HAVES_PER_ROUND, havesCount);
+		}
+	}
+
+	@Test
 	public void testUploadPackFactory() throws Exception {
 		ObjectId master = remote.branch("master").commit().create();
 
@@ -171,7 +250,7 @@
 			try {
 				git.fetch()
 						.setRemote(user1Uri.toString())
-						.setRefSpecs(HEADS)
+						.setRefSpecs(MASTER)
 						.call();
 			} catch (InvalidRemoteException expected) {
 				// Expected.
@@ -181,7 +260,7 @@
 
 			git.fetch()
 					.setRemote(user2Uri.toString())
-					.setRefSpecs(HEADS)
+					.setRefSpecs(MASTER)
 					.call();
 			assertEquals(1, rejected.get());
 			assertEquals(master,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index 1eb218c..39cd719 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -1052,4 +1052,32 @@
 		assertEquals("", u.getPath());
 		assertEquals(str, u.toString());
 	}
+
+	@Test
+	public void testEqualsHashcode() throws Exception
+	{
+		String[] urls = { "http://user:pass@example.com:8081/path", "../x",
+				"ssh://x.y:23/z", "ssh://example.com:/path", "D:\\m y",
+				"\\\\some\\place", "http://localhost:1234",
+				"user@example.com:some/p ath", "a",
+				"http://user:pwd@example.com:8081/path",
+				"http://user:pass@another.com:8081/path",
+				"http://user:pass@example.com:8083/path" };
+		URIish w = new URIish("http://user:pass@example.com:8081/path/x");
+		for (String s : urls) {
+			URIish u = new URIish(s);
+			URIish v = new URIish(s);
+			assertTrue(u.equals(v));
+			assertTrue(v.equals(u));
+
+			assertFalse(u.equals(null));
+			assertFalse(u.equals(new Object()));
+			assertFalse(new Object().equals(u));
+			assertFalse(u.equals(w));
+			assertFalse(w.equals(u));
+
+			assertTrue(u.hashCode() == v.hashCode());
+			assertFalse(u.hashCode() == new Object().hashCode());
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
index 1b434d3..cb04f83 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
@@ -43,7 +43,7 @@
 
 package org.eclipse.jgit.transport;
 
-import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.UTF_8;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListPBE;
 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListTrans;
 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.folderDelete;
@@ -77,7 +77,6 @@
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.UnknownHostException;
-import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.security.GeneralSecurityException;
 import java.security.Provider;
@@ -353,8 +352,6 @@
 	 */
 	static class Util {
 
-		static final Charset UTF_8 = Charset.forName("UTF-8");
-
 		/**
 		 * Read UTF-8 encoded text file into string.
 		 *
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
index 83a53b9..1272e16 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java
@@ -93,25 +93,24 @@
 		byte[] expectBytes = expect.getBytes();
 		for (int i = 0; i < 5; ++i) {
 			byte[] buf = new byte[i];
-			ByteArrayInputStream bis = new ByteArrayInputStream(inbytes);
-			InputStream in = new AutoCRLFInputStream(bis, true);
-			ByteArrayOutputStream out = new ByteArrayOutputStream();
-			if (i > 0) {
-				int n;
-				while ((n = in.read(buf)) >= 0) {
-					out.write(buf, 0, n);
+			try (ByteArrayInputStream bis = new ByteArrayInputStream(inbytes);
+					InputStream in = new AutoCRLFInputStream(bis, true);
+					ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+				if (i > 0) {
+					int n;
+					while ((n = in.read(buf)) >= 0) {
+						out.write(buf, 0, n);
+					}
+				} else {
+					int c;
+					while ((c = in.read()) != -1)
+						out.write(c);
 				}
-			} else {
-				int c;
-				while ((c = in.read()) != -1)
-					out.write(c);
+				out.flush();
+				byte[] actualBytes = out.toByteArray();
+				Assert.assertEquals("bufsize=" + i, encode(expectBytes),
+						encode(actualBytes));
 			}
-			out.flush();
-			in.close();
-			out.close();
-			byte[] actualBytes = out.toByteArray();
-			Assert.assertEquals("bufsize=" + i, encode(expectBytes),
-					encode(actualBytes));
 		}
 	}
 
diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
index ede0f7d..89394ec 100644
--- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index d1709f8..01be246 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,14 +4,14 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit.ui
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="4.10.1"
-Import-Package: org.eclipse.jgit.errors;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.lib;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.nls;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revplot;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.revwalk;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.transport;version="[4.10.1,4.11.0)",
- org.eclipse.jgit.util;version="[4.10.1,4.11.0)"
+Export-Package: org.eclipse.jgit.awtui;version="4.11.3"
+Import-Package: org.eclipse.jgit.errors;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.lib;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.nls;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revplot;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.revwalk;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.transport;version="[4.11.3,4.12.0)",
+ org.eclipse.jgit.util;version="[4.11.3,4.12.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 4979f08..18c1d90 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-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 fdc1efc..e6e3309 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.10.1"/>
-                <message_argument value="4.10.0"/>
+                <message_argument value="4.11.2"/>
+                <message_argument value="4.11.0"/>
             </message_arguments>
         </filter>
     </resource>
@@ -12,7 +12,7 @@
         <filter id="1141899266">
             <message_arguments>
                 <message_argument value="4.7"/>
-                <message_argument value="4.10"/>
+                <message_argument value="4.11"/>
                 <message_argument value="LOCK_SUFFIX"/>
             </message_arguments>
         </filter>
@@ -21,7 +21,7 @@
         <filter id="1141899266">
             <message_arguments>
                 <message_argument value="4.7"/>
-                <message_argument value="4.10"/>
+                <message_argument value="4.11"/>
                 <message_argument value="createNewFileAtomic(File)"/>
             </message_arguments>
         </filter>
@@ -30,7 +30,7 @@
         <filter id="1141899266">
             <message_arguments>
                 <message_argument value="4.7"/>
-                <message_argument value="4.10"/>
+                <message_argument value="4.11"/>
                 <message_argument value="LockToken"/>
             </message_arguments>
         </filter>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 06ddbab..13c32a6 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -25,7 +25,7 @@
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index a8a53f2..6ba7796 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -14,12 +14,17 @@
 java_library(
     name = "jgit",
     srcs = SRCS,
+    javacopts = select({
+        "//:jdk9": ["--add-modules=java.xml.bind"],
+        "//conditions:default": [],
+    }),
     resource_strip_prefix = "org.eclipse.jgit/resources",
     resources = RESOURCES,
     deps = [
         ":insecure_cipher_factory",
         "//lib:javaewah",
         "//lib:jsch",
+        "//lib:jzlib",
         "//lib:slf4j-api",
     ],
 )
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index b2d7c8e..8a26d41 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@
 Bundle-Name: %plugin_name
 Automatic-Module-Name: org.eclipse.jgit
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 4.10.1.qualifier
+Bundle-Version: 4.11.3.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="4.10.1",
- org.eclipse.jgit.api;version="4.10.1";
+Export-Package: org.eclipse.jgit.annotations;version="4.11.3",
+ org.eclipse.jgit.api;version="4.11.3";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
@@ -22,52 +22,52 @@
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="4.10.1",
- org.eclipse.jgit.blame;version="4.10.1";
+ org.eclipse.jgit.api.errors;version="4.11.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.11.3",
+ org.eclipse.jgit.blame;version="4.11.3";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="4.10.1";
+ org.eclipse.jgit.diff;version="4.11.3";
   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.10.1";
+ org.eclipse.jgit.dircache;version="4.11.3";
   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.10.1";
+ org.eclipse.jgit.errors;version="4.11.3";
   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.10.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="4.10.1",
- org.eclipse.jgit.gitrepo;version="4.10.1";
+ org.eclipse.jgit.events;version="4.11.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.11.3",
+ org.eclipse.jgit.gitrepo;version="4.11.3";
   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.10.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.10.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="4.10.1",
- org.eclipse.jgit.ignore.internal;version="4.10.1";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="4.10.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="4.10.1";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.dfs;version="4.10.1";
+ org.eclipse.jgit.gitrepo.internal;version="4.11.3";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.11.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.11.3",
+ org.eclipse.jgit.ignore.internal;version="4.11.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.11.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.fsck;version="4.11.3";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal.ketch;version="4.11.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.11.3";
   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.10.1";
+ org.eclipse.jgit.internal.storage.file;version="4.11.3";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -75,12 +75,12 @@
    org.eclipse.jgit.lfs,
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test",
- org.eclipse.jgit.internal.storage.io;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="4.10.1";
+ org.eclipse.jgit.internal.storage.io;version="4.11.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.pack;version="4.11.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftable;version="4.11.3";
   x-friends:="org.eclipse.jgit.http.test,org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="4.10.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="4.10.1";
+ org.eclipse.jgit.internal.storage.reftree;version="4.11.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.11.3";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
@@ -90,33 +90,33 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.submodule",
- org.eclipse.jgit.lib.internal;version="4.10.1";x-internal:=true,
- org.eclipse.jgit.merge;version="4.10.1";
+ org.eclipse.jgit.lib.internal;version="4.11.3";x-internal:=true,
+ org.eclipse.jgit.merge;version="4.11.3";
   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.10.1",
- org.eclipse.jgit.notes;version="4.10.1";
+ org.eclipse.jgit.nls;version="4.11.3",
+ org.eclipse.jgit.notes;version="4.11.3";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="4.10.1";
+ org.eclipse.jgit.patch;version="4.11.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.11.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.11.3";
   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.10.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.10.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="4.10.1";
+ org.eclipse.jgit.revwalk.filter;version="4.11.3";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.11.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.11.3";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.11.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.11.3";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -128,24 +128,24 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.10.1";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.10.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="4.10.1";
+ org.eclipse.jgit.transport.http;version="4.11.3";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.11.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.11.3";
   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.10.1";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="4.10.1";
+ org.eclipse.jgit.treewalk.filter;version="4.11.3";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.11.3";
   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.10.1",
- org.eclipse.jgit.util.sha1;version="4.10.1",
- org.eclipse.jgit.util.time;version="4.10.1"
+ org.eclipse.jgit.util.io;version="4.11.3",
+ org.eclipse.jgit.util.sha1;version="4.11.3",
+ org.eclipse.jgit.util.time;version="4.11.3"
 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 ec2a99a..c07c92b 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.10.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="4.10.1.qualifier";roots="."
+Bundle-Version: 4.11.3.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.11.3.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index a0d8a12..1f4ac41 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.10.1-SNAPSHOT</version>
+    <version>4.11.3-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
@@ -75,6 +75,11 @@
     </dependency>
 
     <dependency>
+      <groupId>com.jcraft</groupId>
+      <artifactId>jzlib</artifactId>
+    </dependency>
+
+    <dependency>
       <groupId>com.googlecode.javaewah</groupId>
       <artifactId>JavaEWAH</artifactId>
     </dependency>
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 268a0f9..c5d03ba 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -41,6 +41,8 @@
 blameNotCommittedYet=Not Committed Yet
 blobNotFound=Blob not found: {0}
 blobNotFoundForPath=Blob not found: {0} for path: {1}
+blockLimitNotMultipleOfBlockSize=blockLimit {0} must be a multiple of blockSize {1}
+blockLimitNotPositive=blockLimit must be positive: {0}
 blockSizeNotPowerOf2=blockSize must be a power of 2
 bothRefTargetsMustNotBeNull=both old and new ref targets must not be null.
 branchNameInvalid=Branch name {0} is not allowed
@@ -116,6 +118,7 @@
 cantPassMeATree=Can't pass me a tree!
 channelMustBeInRange1_255=channel {0} must be in range [1, 255]
 characterClassIsNotSupported=The character class {0} is not supported.
+checkingOutFiles=Checking out files
 checkoutConflictWithFile=Checkout conflict with file: {0}
 checkoutConflictWithFiles=Checkout conflict with files: {0}
 checkoutUnexpectedResult=Checkout returned unexpected result {0}
@@ -369,6 +372,7 @@
 invalidIntegerValue=Invalid integer value: {0}.{1}={2}
 invalidKey=Invalid key: {0}
 invalidLineInConfigFile=Invalid line in config file
+invalidLineInConfigFileWithParam=Invalid line in config file: {0}
 invalidModeFor=Invalid mode {0} for {1} {2} in {3}.
 invalidModeForPath=Invalid mode {0} for path {1}
 invalidObject=Invalid {0} {1}: {2}
@@ -407,6 +411,7 @@
 largeObjectException={0} exceeds size limit
 largeObjectOutOfMemory=Out of memory loading {0}
 lengthExceedsMaximumArraySize=Length exceeds maximum array size
+lfsHookConflict=LFS built-in hook conflicts with existing pre-push hook in repository {0}. Either remove the pre-push hook or disable built-in LFS support.
 listingAlternates=Listing alternates
 listingPacks=Listing packs
 localObjectsIncomplete=Local objects incomplete.
@@ -461,6 +466,7 @@
 noMergeBase=No merge base could be determined. Reason={0}. {1}
 noMergeHeadSpecified=No merge head specified
 nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos
+noPathAttributesFound=No Attributes found for {0}.
 noSuchRef=no such ref
 noSuchSubmodule=no such submodule {0}
 notABoolean=Not a boolean: {0}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
index 08f81cb..1cc65c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
@@ -56,7 +56,7 @@
 /**
  * JGit's replacement for the {@code javax.annotation.Nonnull}.
  * <p>
- * Denotes that a local variable, parameter, field, method return value expected
+ * Denotes that a local variable, parameter, field, method return value is expected
  * to be non {@code null}.
  *
  * @since 4.2
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
index f80c8c7..688f5f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
@@ -99,7 +99,7 @@
 				map = NoteMap.read(walk.getObjectReader(), notesCommit);
 			}
 			map.set(id, message, inserter);
-			commitNoteMap(walk, map, notesCommit, inserter,
+			commitNoteMap(repo, notesRef, walk, map, notesCommit, inserter,
 					"Notes added by 'git notes add'"); //$NON-NLS-1$
 			return map.getNote(id);
 		} catch (IOException e) {
@@ -134,7 +134,8 @@
 		return this;
 	}
 
-	private void commitNoteMap(RevWalk walk, NoteMap map,
+	static void commitNoteMap(Repository r, String ref, RevWalk walk,
+			NoteMap map,
 			RevCommit notesCommit,
 			ObjectInserter inserter,
 			String msg)
@@ -142,14 +143,14 @@
 		// commit the note
 		CommitBuilder builder = new CommitBuilder();
 		builder.setTreeId(map.writeTree(inserter));
-		builder.setAuthor(new PersonIdent(repo));
+		builder.setAuthor(new PersonIdent(r));
 		builder.setCommitter(builder.getAuthor());
 		builder.setMessage(msg);
 		if (notesCommit != null)
 			builder.setParentIds(notesCommit);
 		ObjectId commit = inserter.insert(builder);
 		inserter.flush();
-		RefUpdate refUpdate = repo.updateRef(notesRef);
+		RefUpdate refUpdate = r.updateRef(ref);
 		if (notesCommit != null)
 			refUpdate.setExpectedOldObjectId(notesCommit);
 		else
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index cfc55d8..5b84032 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -258,9 +258,9 @@
 		if (sb.length() > 0) {
 			sb.deleteCharAt(sb.length() - 1);
 		}
-		FileWriter fw = new FileWriter(f);
-		fw.write(sb.toString());
-		fw.close();
+		try (FileWriter fw = new FileWriter(f)) {
+			fw.write(sb.toString());
+		}
 
 		getRepository().getFS().setExecute(f, fh.getNewMode() == FileMode.EXECUTABLE_FILE);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
index c29ed0e..10397f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
@@ -392,9 +392,10 @@
 	private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) {
 		try {
 			try (TreeWalk walk = new TreeWalk(repo);
-					RevWalk rw = new RevWalk(walk.getObjectReader())) {
+					RevWalk rw = new RevWalk(walk.getObjectReader());
+					T outa = fmt.createArchiveOutputStream(out,
+							formatOptions)) {
 				String pfx = prefix == null ? "" : prefix; //$NON-NLS-1$
-				T outa = fmt.createArchiveOutputStream(out, formatOptions);
 				MutableObjectId idBuf = new MutableObjectId();
 				ObjectReader reader = walk.getObjectReader();
 
@@ -427,7 +428,6 @@
 					walk.getObjectId(idBuf, 0);
 					fmt.putEntry(outa, tree, name, mode, reader.open(idBuf));
 				}
-				outa.close();
 				return out;
 			} finally {
 				out.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index aa9939e..11edb10 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -76,8 +76,10 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 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.RefUpdate;
 import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -182,6 +184,8 @@
 
 	private Set<String> actuallyModifiedPaths;
 
+	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
 	/**
 	 * Constructor for CheckoutCommand
 	 *
@@ -266,6 +270,7 @@
 				dco = new DirCacheCheckout(repo, headTree, dc,
 						newCommit.getTree());
 				dco.setFailOnConflict(true);
+				dco.setProgressMonitor(monitor);
 				try {
 					dco.checkout();
 				} catch (org.eclipse.jgit.errors.CheckoutConflictException e) {
@@ -347,6 +352,20 @@
 	}
 
 	/**
+	 * @param monitor
+	 *            a progress monitor
+	 * @return this instance
+	 * @since 4.11
+	 */
+	public CheckoutCommand setProgressMonitor(ProgressMonitor monitor) {
+		if (monitor == null) {
+			monitor = NullProgressMonitor.INSTANCE;
+		}
+		this.monitor = monitor;
+		return this;
+	}
+
+	/**
 	 * Add a single slash-separated path to the list of paths to check out. To
 	 * check out all paths, use {@link #setAllPaths(boolean)}.
 	 * <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index 771798a..f45e39e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -60,8 +60,10 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
 import org.eclipse.jgit.lib.Repository;
@@ -95,6 +97,8 @@
 
 	private boolean noCommit = false;
 
+	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
 	/**
 	 * Constructor for CherryPickCommand
 	 *
@@ -160,6 +164,7 @@
 							newHead.getTree(), repo.lockDirCache(),
 							merger.getResultTreeId());
 					dco.setFailOnConflict(true);
+					dco.setProgressMonitor(monitor);
 					dco.checkout();
 					if (!noCommit)
 						newHead = new Git(getRepository()).commit()
@@ -332,6 +337,24 @@
 		return this;
 	}
 
+	/**
+	 * The progress monitor associated with the cherry-pick operation. By
+	 * default, this is set to <code>NullProgressMonitor</code>
+	 *
+	 * @see NullProgressMonitor
+	 * @param monitor
+	 *            a {@link org.eclipse.jgit.lib.ProgressMonitor}
+	 * @return {@code this}
+	 * @since 4.11
+	 */
+	public CherryPickCommand setProgressMonitor(ProgressMonitor monitor) {
+		if (monitor == null) {
+			monitor = NullProgressMonitor.INSTANCE;
+		}
+		this.monitor = monitor;
+		return this;
+	}
+
 	private String calculateOurName(Ref headRef) {
 		if (ourCommitName != null)
 			return ourCommitName;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
index c814250..0d9fe41 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
@@ -103,27 +103,25 @@
 			StatusCommand command = new StatusCommand(repo);
 			Status status = command.call();
 
-			Set<String> untrackedAndIgnoredFiles = new TreeSet<>(
-					status.getUntracked());
-			Set<String> untrackedAndIgnoredDirs = new TreeSet<>(
+			Set<String> untrackedFiles = new TreeSet<>(status.getUntracked());
+			Set<String> untrackedDirs = new TreeSet<>(
 					status.getUntrackedFolders());
 
 			FS fs = getRepository().getFS();
 			for (String p : status.getIgnoredNotInIndex()) {
 				File f = new File(repo.getWorkTree(), p);
-				if (fs.isFile(f) || fs.isSymLink(f))
-					untrackedAndIgnoredFiles.add(p);
-				else if (fs.isDirectory(f))
-					untrackedAndIgnoredDirs.add(p);
+				if (fs.isFile(f) || fs.isSymLink(f)) {
+					untrackedFiles.add(p);
+				} else if (fs.isDirectory(f)) {
+					untrackedDirs.add(p);
+				}
 			}
 
-			Set<String> filtered = filterFolders(untrackedAndIgnoredFiles,
-					untrackedAndIgnoredDirs);
+			Set<String> filtered = filterFolders(untrackedFiles, untrackedDirs);
 
 			Set<String> notIgnoredFiles = filterIgnorePaths(filtered,
 					status.getIgnoredNotInIndex(), true);
-			Set<String> notIgnoredDirs = filterIgnorePaths(
-					untrackedAndIgnoredDirs,
+			Set<String> notIgnoredDirs = filterIgnorePaths(untrackedDirs,
 					status.getIgnoredNotInIndex(), false);
 
 			for (String file : notIgnoredFiles)
@@ -174,20 +172,22 @@
 				if (new File(curFile, DOT_GIT).exists()) {
 					if (force) {
 						if (!dryRun) {
-							FileUtils.delete(curFile, FileUtils.RECURSIVE);
+							FileUtils.delete(curFile, FileUtils.RECURSIVE
+									| FileUtils.SKIP_MISSING);
 						}
 						inFiles.add(path + "/"); //$NON-NLS-1$
 					}
 				} else {
 					if (!dryRun) {
-						FileUtils.delete(curFile, FileUtils.RECURSIVE);
+						FileUtils.delete(curFile,
+								FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
 					}
 					inFiles.add(path + "/"); //$NON-NLS-1$
 				}
 			}
 		} else {
 			if (!dryRun) {
-				FileUtils.delete(curFile, FileUtils.NONE);
+				FileUtils.delete(curFile, FileUtils.SKIP_MISSING);
 			}
 			inFiles.add(path);
 		}
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 6d3afc6..79b0efb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -361,6 +361,7 @@
 			DirCache dc = clonedRepo.lockDirCache();
 			DirCacheCheckout co = new DirCacheCheckout(clonedRepo, dc,
 					commit.getTree());
+			co.setProgressMonitor(monitor);
 			co.checkout();
 			if (cloneSubmodules)
 				cloneSubmodules(clonedRepo);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 8a89ba1..f257283 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -409,14 +409,11 @@
 									inserter = repo.newObjectInserter();
 								long contentLength = fTree
 										.getEntryContentLength();
-								InputStream inputStream = fTree
-										.openEntryStream();
-								try {
+								try (InputStream inputStream = fTree
+										.openEntryStream()) {
 									dcEntry.setObjectId(inserter.insert(
 											Constants.OBJ_BLOB, contentLength,
 											inputStream));
-								} finally {
-									inputStream.close();
 								}
 							}
 						}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
index 4c6f351..f65b573 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
@@ -104,6 +104,12 @@
 		super(repo);
 	}
 
+	private DiffFormatter getDiffFormatter() {
+		return out != null && !showNameAndStatusOnly
+				? new DiffFormatter(new BufferedOutputStream(out))
+				: new DiffFormatter(NullOutputStream.INSTANCE);
+	}
+
 	/**
 	 * {@inheritDoc}
 	 * <p>
@@ -114,14 +120,9 @@
 	 */
 	@Override
 	public List<DiffEntry> call() throws GitAPIException {
-		final DiffFormatter diffFmt;
-		if (out != null && !showNameAndStatusOnly)
-			diffFmt = new DiffFormatter(new BufferedOutputStream(out));
-		else
-			diffFmt = new DiffFormatter(NullOutputStream.INSTANCE);
-		diffFmt.setRepository(repo);
-		diffFmt.setProgressMonitor(monitor);
-		try {
+		try (DiffFormatter diffFmt = getDiffFormatter()) {
+			diffFmt.setRepository(repo);
+			diffFmt.setProgressMonitor(monitor);
 			if (cached) {
 				if (oldTree == null) {
 					ObjectId head = repo.resolve(HEAD + "^{tree}"); //$NON-NLS-1$
@@ -159,8 +160,6 @@
 			}
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
-		} finally {
-			diffFmt.close();
 		}
 	}
 
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 b2c28da..5d178bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -171,38 +171,42 @@
 			}
 			walk.setTree(revWalk.parseTree(fetchHead));
 			while (walk.next()) {
-				Repository submoduleRepo = walk.getRepository();
+				try (Repository submoduleRepo = walk.getRepository()) {
 
-				// Skip submodules that don't exist locally (have not been
-				// cloned), are not registered in the .gitmodules file, or
-				// not registered in the parent repository's config.
-				if (submoduleRepo == null || walk.getModulesPath() == null
-						|| walk.getConfigUrl() == null) {
-					continue;
-				}
-
-				FetchRecurseSubmodulesMode recurseMode = getRecurseMode(
-						walk.getPath());
-
-				// When the fetch mode is "yes" we always fetch. When the mode
-				// is "on demand", we only fetch if the submodule's revision was
-				// updated to an object that is not currently present in the
-				// submodule.
-				if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
-						&& !submoduleRepo.hasObject(walk.getObjectId()))
-						|| recurseMode == FetchRecurseSubmodulesMode.YES) {
-					FetchCommand f = new FetchCommand(submoduleRepo)
-							.setProgressMonitor(monitor).setTagOpt(tagOption)
-							.setCheckFetchedObjects(checkFetchedObjects)
-							.setRemoveDeletedRefs(isRemoveDeletedRefs())
-							.setThin(thin).setRefSpecs(refSpecs)
-							.setDryRun(dryRun)
-							.setRecurseSubmodules(recurseMode);
-					configure(f);
-					if (callback != null) {
-						callback.fetchingSubmodule(walk.getPath());
+					// Skip submodules that don't exist locally (have not been
+					// cloned), are not registered in the .gitmodules file, or
+					// not registered in the parent repository's config.
+					if (submoduleRepo == null || walk.getModulesPath() == null
+							|| walk.getConfigUrl() == null) {
+						continue;
 					}
-					results.addSubmodule(walk.getPath(), f.call());
+
+					FetchRecurseSubmodulesMode recurseMode = getRecurseMode(
+							walk.getPath());
+
+					// When the fetch mode is "yes" we always fetch. When the
+					// mode
+					// is "on demand", we only fetch if the submodule's revision
+					// was
+					// updated to an object that is not currently present in the
+					// submodule.
+					if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
+							&& !submoduleRepo.hasObject(walk.getObjectId()))
+							|| recurseMode == FetchRecurseSubmodulesMode.YES) {
+						FetchCommand f = new FetchCommand(submoduleRepo)
+								.setProgressMonitor(monitor)
+								.setTagOpt(tagOption)
+								.setCheckFetchedObjects(checkFetchedObjects)
+								.setRemoveDeletedRefs(isRemoveDeletedRefs())
+								.setThin(thin).setRefSpecs(refSpecs)
+								.setDryRun(dryRun)
+								.setRecurseSubmodules(recurseMode);
+						configure(f);
+						if (callback != null) {
+							callback.fetchingSubmodule(walk.getPath());
+						}
+						results.addSubmodule(walk.getPath(), f.call());
+					}
 				}
 			}
 		} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
index f10bcdf..d48049f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
@@ -75,6 +75,9 @@
 	 * {@inheritDoc}
 	 * <p>
 	 * Executes the {@code Init} command.
+	 *
+	 * @return a {@code Git} instance that owns the {@code Repository} that it
+	 *         wraps.
 	 */
 	@Override
 	public Git call() throws GitAPIException {
@@ -120,7 +123,7 @@
 			Repository repository = builder.build();
 			if (!repository.getObjectDatabase().exists())
 				repository.create(bare);
-			return new Git(repository);
+			return new Git(repository, true);
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index 44ff18f..cd50cae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -237,9 +237,8 @@
 		fallBackToConfiguration();
 		checkParameters();
 
-		RevWalk revWalk = null;
 		DirCacheCheckout dco = null;
-		try {
+		try (RevWalk revWalk = new RevWalk(repo)) {
 			Ref head = repo.exactRef(Constants.HEAD);
 			if (head == null)
 				throw new NoHeadException(
@@ -247,7 +246,6 @@
 			StringBuilder refLogMessage = new StringBuilder("merge "); //$NON-NLS-1$
 
 			// Check for FAST_FORWARD, ALREADY_UP_TO_DATE
-			revWalk = new RevWalk(repo);
 
 			// we know for now there is only one commit
 			Ref ref = commits.get(0);
@@ -268,6 +266,7 @@
 				dco = new DirCacheCheckout(repo,
 						repo.lockDirCache(), srcCommit.getTree());
 				dco.setFailOnConflict(true);
+				dco.setProgressMonitor(monitor);
 				dco.checkout();
 				RefUpdate refUpdate = repo
 						.updateRef(head.getTarget().getName());
@@ -298,6 +297,7 @@
 				dco = new DirCacheCheckout(repo,
 						headCommit.getTree(), repo.lockDirCache(),
 						srcCommit.getTree());
+				dco.setProgressMonitor(monitor);
 				dco.setFailOnConflict(true);
 				dco.checkout();
 				String msg = null;
@@ -376,6 +376,7 @@
 							headCommit.getTree(), repo.lockDirCache(),
 							merger.getResultTreeId());
 					dco.setFailOnConflict(true);
+					dco.setProgressMonitor(monitor);
 					dco.checkout();
 
 					String msg = null;
@@ -436,9 +437,6 @@
 					MessageFormat.format(
 							JGitText.get().exceptionCaughtDuringExecutionOfMergeCommand,
 							e), e);
-		} finally {
-			if (revWalk != null)
-				revWalk.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index b86a2fd..da1ff06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -932,6 +932,7 @@
 		try {
 			DirCacheCheckout dco = new DirCacheCheckout(repo, dc, headTree);
 			dco.setFailOnConflict(false);
+			dco.setProgressMonitor(monitor);
 			boolean needsDeleteFiles = dco.checkout();
 			if (needsDeleteFiles) {
 				List<String> fileList = dco.getToBeDeleted();
@@ -1265,6 +1266,7 @@
 
 		CheckoutCommand co = new CheckoutCommand(repo);
 		try {
+			co.setProgressMonitor(monitor);
 			co.setName(newCommit.name()).call();
 			if (headName.startsWith(Constants.R_HEADS)) {
 				RefUpdate rup = repo.updateRef(headName);
@@ -1407,6 +1409,7 @@
 			DirCacheCheckout dco = new DirCacheCheckout(repo, head.getTree(),
 					repo.lockDirCache(), commit.getTree());
 			dco.setFailOnConflict(true);
+			dco.setProgressMonitor(monitor);
 			try {
 				dco.checkout();
 			} catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
index baae8248f..cfbf0dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
@@ -46,13 +46,9 @@
 
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.notes.Note;
 import org.eclipse.jgit.notes.NoteMap;
@@ -99,7 +95,8 @@
 				map = NoteMap.read(walk.getObjectReader(), notesCommit);
 			}
 			map.set(id, null, inserter);
-			commitNoteMap(walk, map, notesCommit, inserter,
+			AddNoteCommand.commitNoteMap(repo, notesRef, walk, map, notesCommit,
+					inserter,
 					"Notes removed by 'git notes remove'"); //$NON-NLS-1$
 			return map.getNote(id);
 		} catch (IOException e) {
@@ -121,30 +118,6 @@
 		return this;
 	}
 
-	private void commitNoteMap(RevWalk walk, NoteMap map,
-			RevCommit notesCommit,
-			ObjectInserter inserter,
-			String msg)
-			throws IOException {
-		// commit the note
-		CommitBuilder builder = new CommitBuilder();
-		builder.setTreeId(map.writeTree(inserter));
-		builder.setAuthor(new PersonIdent(repo));
-		builder.setCommitter(builder.getAuthor());
-		builder.setMessage(msg);
-		if (notesCommit != null)
-			builder.setParentIds(notesCommit);
-		ObjectId commit = inserter.insert(builder);
-		inserter.flush();
-		RefUpdate refUpdate = repo.updateRef(notesRef);
-		if (notesCommit != null)
-			refUpdate.setExpectedOldObjectId(notesCommit);
-		else
-			refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
-		refUpdate.setNewObjectId(commit);
-		refUpdate.update(walk);
-	}
-
 	/**
 	 * Set the name of the <code>Ref</code> to remove a note from.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
index 86a69b0..be446f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -58,7 +58,9 @@
 import org.eclipse.jgit.dircache.DirCacheIterator;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
@@ -125,6 +127,8 @@
 
 	private boolean isReflogDisabled;
 
+	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
 	/**
 	 * <p>
 	 * Constructor for ResetCommand.
@@ -336,6 +340,24 @@
 			return Constants.HEAD;
 	}
 
+	/**
+	 * The progress monitor associated with the reset operation. By default,
+	 * this is set to <code>NullProgressMonitor</code>
+	 *
+	 * @see NullProgressMonitor
+	 * @param monitor
+	 *            a {@link org.eclipse.jgit.lib.ProgressMonitor}
+	 * @return {@code this}
+	 * @since 4.11
+	 */
+	public ResetCommand setProgressMonitor(ProgressMonitor monitor) {
+		if (monitor == null) {
+			monitor = NullProgressMonitor.INSTANCE;
+		}
+		this.monitor = monitor;
+		return this;
+	}
+
 	private void resetIndexForPaths(ObjectId commitTree) {
 		DirCache dc = null;
 		try (final TreeWalk tw = new TreeWalk(repo)) {
@@ -420,6 +442,7 @@
 			DirCacheCheckout checkout = new DirCacheCheckout(repo, dc,
 					commitTree);
 			checkout.setFailOnConflict(false);
+			checkout.setProgressMonitor(monitor);
 			try {
 				checkout.checkout();
 			} catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index fa0d4c4..46e0df7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -61,8 +61,10 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
 import org.eclipse.jgit.lib.Repository;
@@ -97,6 +99,8 @@
 
 	private MergeStrategy strategy = MergeStrategy.RECURSIVE;
 
+	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
 	/**
 	 * <p>
 	 * Constructor for RevertCommand.
@@ -178,6 +182,7 @@
 							headCommit.getTree(), repo.lockDirCache(),
 							merger.getResultTreeId());
 					dco.setFailOnConflict(true);
+					dco.setProgressMonitor(monitor);
 					dco.checkout();
 					try (Git git = new Git(getRepository())) {
 						newHead = git.commit().setMessage(newMessage)
@@ -325,4 +330,22 @@
 		this.strategy = strategy;
 		return this;
 	}
+
+	/**
+	 * The progress monitor associated with the revert operation. By default,
+	 * this is set to <code>NullProgressMonitor</code>
+	 *
+	 * @see NullProgressMonitor
+	 * @param monitor
+	 *            a {@link org.eclipse.jgit.lib.ProgressMonitor}
+	 * @return {@code this}
+	 * @since 4.11
+	 */
+	public RevertCommand setProgressMonitor(ProgressMonitor monitor) {
+		if (monitor == null) {
+			monitor = NullProgressMonitor.INSTANCE;
+		}
+		this.monitor = monitor;
+		return this;
+	}
 }
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 4b4e18c..3362d46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -226,6 +226,7 @@
 								submoduleRepo, submoduleRepo.lockDirCache(),
 								commit.getTree());
 						co.setFailOnConflict(true);
+						co.setProgressMonitor(monitor);
 						co.checkout();
 						RefUpdate refUpdate = submoduleRepo.updateRef(
 								Constants.HEAD, true);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java
index aae0076..c4357d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java
@@ -67,6 +67,9 @@
 
 	/**
 	 * Constructor for FilterCommand
+	 * <p>
+	 * FilterCommand implementors are required to manage the in and out streams
+	 * (close on success and/or exception).
 	 *
 	 * @param in
 	 *            The {@link java.io.InputStream} this command should read from
@@ -84,6 +87,9 @@
 	 * number of bytes it read from {@link #in}. It should be called in a loop
 	 * until it returns -1 signaling that the {@link java.io.InputStream} is
 	 * completely processed.
+	 * <p>
+	 * On successful completion (return -1) or on Exception, the streams
+	 * {@link #in} and {@link #out} are closed by the implementation.
 	 *
 	 * @return the number of bytes read from the {@link java.io.InputStream} or
 	 *         -1. -1 means that the {@link java.io.InputStream} is completely
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index e2411d6..1ad7a30 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -303,7 +303,8 @@
 			throws IOException {
 		if (description == null)
 			description = JGitText.get().blameNotCommittedYet;
-		BlobCandidate c = new BlobCandidate(description, resultPath);
+		BlobCandidate c = new BlobCandidate(getRepository(), description,
+				resultPath);
 		c.sourceText = contents;
 		c.regionList = new Region(0, 0, contents.size());
 		remaining = contents.size();
@@ -333,7 +334,8 @@
 		if (ldr.getType() == OBJ_BLOB) {
 			if (description == null)
 				description = JGitText.get().blameNotCommittedYet;
-			BlobCandidate c = new BlobCandidate(description, resultPath);
+			BlobCandidate c = new BlobCandidate(getRepository(), description,
+					resultPath);
 			c.sourceBlob = id.toObjectId();
 			c.sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE));
 			c.regionList = new Region(0, 0, c.sourceText.size());
@@ -346,7 +348,7 @@
 		if (!find(commit, resultPath))
 			return this;
 
-		Candidate c = new Candidate(commit, resultPath);
+		Candidate c = new Candidate(getRepository(), commit, resultPath);
 		c.sourceBlob = idBuf.toObjectId();
 		c.loadText(reader);
 		c.regionList = new Region(0, 0, c.sourceText.size());
@@ -430,7 +432,8 @@
 			// just pump the queue
 		}
 
-		ReverseCandidate c = new ReverseCandidate(result, resultPath);
+		ReverseCandidate c = new ReverseCandidate(getRepository(), result,
+				resultPath);
 		c.sourceBlob = idBuf.toObjectId();
 		c.loadText(reader);
 		c.regionList = new Region(0, 0, c.sourceText.size());
@@ -637,7 +640,8 @@
 			return false;
 		}
 
-		Candidate next = n.create(parent, PathFilter.create(r.getOldPath()));
+		Candidate next = n.create(getRepository(), parent,
+				PathFilter.create(r.getOldPath()));
 		next.sourceBlob = r.getOldId().toObjectId();
 		next.renameScore = r.getScore();
 		next.loadText(reader);
@@ -653,7 +657,7 @@
 
 	private boolean splitBlameWithParent(Candidate n, RevCommit parent)
 			throws IOException {
-		Candidate next = n.create(parent, n.sourcePath);
+		Candidate next = n.create(getRepository(), parent, n.sourcePath);
 		next.sourceBlob = idBuf.toObjectId();
 		next.loadText(reader);
 		return split(next, n);
@@ -740,12 +744,12 @@
 
 			Candidate p;
 			if (renames != null && renames[pIdx] != null) {
-				p = n.create(parent,
+				p = n.create(getRepository(), parent,
 						PathFilter.create(renames[pIdx].getOldPath()));
 				p.renameScore = renames[pIdx].getScore();
 				p.sourceBlob = renames[pIdx].getOldId().toObjectId();
 			} else if (ids != null && ids[pIdx] != null) {
-				p = n.create(parent, n.sourcePath);
+				p = n.create(getRepository(), parent, n.sourcePath);
 				p.sourceBlob = ids[pIdx];
 			} else {
 				continue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java
index 855ef78..457d1d2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/Candidate.java
@@ -55,10 +55,12 @@
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.util.LfsFactory;
 
 /**
  * A source that may have supplied some (or all) of the result file.
@@ -109,7 +111,11 @@
 	 */
 	int renameScore;
 
-	Candidate(RevCommit commit, PathFilter path) {
+	/** repository used for LFS blob handling */
+	private Repository sourceRepository;
+
+	Candidate(Repository repo, RevCommit commit, PathFilter path) {
+		sourceRepository = repo;
 		sourceCommit = commit;
 		sourcePath = path;
 	}
@@ -150,12 +156,12 @@
 		return sourceCommit.getAuthorIdent();
 	}
 
-	Candidate create(RevCommit commit, PathFilter path) {
-		return new Candidate(commit, path);
+	Candidate create(Repository repo, RevCommit commit, PathFilter path) {
+		return new Candidate(repo, commit, path);
 	}
 
 	Candidate copy(RevCommit commit) {
-		Candidate r = create(commit, sourcePath);
+		Candidate r = create(sourceRepository, commit, sourcePath);
 		r.sourceBlob = sourceBlob;
 		r.sourceText = sourceText;
 		r.regionList = regionList;
@@ -164,7 +170,11 @@
 	}
 
 	void loadText(ObjectReader reader) throws IOException {
-		ObjectLoader ldr = reader.open(sourceBlob, Constants.OBJ_BLOB);
+		ObjectLoader ldr = LfsFactory.getInstance().applySmudgeFilter(sourceRepository,
+				reader.open(sourceBlob, Constants.OBJ_BLOB),
+				LfsFactory.getAttributesForPath(sourceRepository,
+						sourcePath.getPath(), sourceCommit)
+						.get(Constants.ATTR_DIFF));
 		sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE));
 	}
 
@@ -349,8 +359,9 @@
 	 * children pointers, allowing reverse navigation of history.
 	 */
 	static final class ReverseCandidate extends Candidate {
-		ReverseCandidate(ReverseCommit commit, PathFilter path) {
-			super(commit, path);
+		ReverseCandidate(Repository repo, ReverseCommit commit,
+				PathFilter path) {
+			super(repo, commit, path);
 		}
 
 		@Override
@@ -370,8 +381,8 @@
 		}
 
 		@Override
-		Candidate create(RevCommit commit, PathFilter path) {
-			return new ReverseCandidate((ReverseCommit) commit, path);
+		Candidate create(Repository repo, RevCommit commit, PathFilter path) {
+			return new ReverseCandidate(repo, (ReverseCommit) commit, path);
 		}
 
 		@Override
@@ -400,8 +411,8 @@
 		/** Author name to refer to this blob with. */
 		String description;
 
-		BlobCandidate(String name, PathFilter path) {
-			super(null, path);
+		BlobCandidate(Repository repo, String name, PathFilter path) {
+			super(repo, null, path);
 			description = name;
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
index 5ede3ea..f3e986a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
@@ -170,10 +170,11 @@
 		@Override
 		public ObjectLoader open(String path, ObjectId id) throws IOException {
 			seek(path);
+			long entrySize = ptr.getEntryContentLength();
 			return new ObjectLoader() {
 				@Override
 				public long getSize() {
-					return ptr.getEntryLength();
+					return entrySize;
 				}
 
 				@Override
@@ -184,7 +185,7 @@
 				@Override
 				public ObjectStream openStream() throws MissingObjectException,
 						IOException {
-					long contentLength = ptr.getEntryContentLength();
+					long contentLength = entrySize;
 					InputStream in = ptr.openEntryStream();
 					in = new BufferedInputStream(in);
 					return new ObjectStream.Filter(getType(), contentLength, in);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
index 0f5ea76..5c8343f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
@@ -48,9 +48,11 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.eclipse.jgit.attributes.Attribute;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.ObjectId;
@@ -196,6 +198,11 @@
 			entry.newMode = walk.getFileMode(1);
 			entry.newPath = entry.oldPath = walk.getPathString();
 
+			if (walk.getAttributesNodeProvider() != null) {
+				entry.diffAttribute = walk.getAttributes()
+						.get(Constants.ATTR_DIFF);
+			}
+
 			if (treeFilterMarker != null)
 				entry.treeFilterMarks = treeFilterMarker.getMarks(walk);
 
@@ -282,6 +289,7 @@
 		del.newMode = FileMode.MISSING;
 		del.newPath = DiffEntry.DEV_NULL;
 		del.changeType = ChangeType.DELETE;
+		del.diffAttribute = entry.diffAttribute;
 
 		DiffEntry add = new DiffEntry();
 		add.oldId = A_ZERO;
@@ -292,6 +300,7 @@
 		add.newMode = entry.getNewMode();
 		add.newPath = entry.getNewPath();
 		add.changeType = ChangeType.ADD;
+		add.diffAttribute = entry.diffAttribute;
 		return Arrays.asList(del, add);
 	}
 
@@ -306,6 +315,7 @@
 		r.newId = dst.newId;
 		r.newMode = dst.newMode;
 		r.newPath = dst.newPath;
+		r.diffAttribute = dst.diffAttribute;
 
 		r.changeType = changeType;
 		r.score = score;
@@ -321,6 +331,13 @@
 	/** File name of the new (post-image). */
 	protected String newPath;
 
+	/**
+	 * diff filter attribute
+	 *
+	 * @since 4.11
+	 */
+	protected Attribute diffAttribute;
+
 	/** Old mode of the file, if described by the patch, else null. */
 	protected FileMode oldMode;
 
@@ -395,6 +412,14 @@
 	}
 
 	/**
+	 * @return the {@link Attribute} determining filters to be applied.
+	 * @since 4.11
+	 */
+	public Attribute getDiffAttribute() {
+		return diffAttribute;
+	}
+
+	/**
 	 * Get the old file mode
 	 *
 	 * @return the old file mode, if described in the patch
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index bf9a27b..2e29b81 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -98,6 +98,7 @@
 import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.LfsFactory;
 import org.eclipse.jgit.util.QuotedString;
 
 /**
@@ -141,6 +142,8 @@
 
 	private ContentSource.Pair source;
 
+	private Repository repository;
+
 	/**
 	 * Create a new formatter with a default level of context.
 	 *
@@ -172,6 +175,7 @@
 	 *            source repository holding referenced objects.
 	 */
 	public void setRepository(Repository repository) {
+		this.repository = repository;
 		setReader(repository.newObjectReader(), repository.getConfig(), true);
 	}
 
@@ -1057,7 +1061,8 @@
 				throw new AmbiguousObjectException(id, ids);
 		}
 
-		ObjectLoader ldr = source.open(side, entry);
+		ObjectLoader ldr = LfsFactory.getInstance().applySmudgeFilter(repository,
+				source.open(side, entry), entry.getDiffAttribute());
 		return RawText.load(ldr, binaryFileThreshold);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index 7be1659..ec88ce4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -74,7 +74,7 @@
 	public static final RawText EMPTY_TEXT = new RawText(new byte[0]);
 
 	/** Number of bytes to check for heuristics in {@link #isBinary(byte[])} */
-	private static final int FIRST_FEW_BYTES = 8000;
+	static final int FIRST_FEW_BYTES = 8000;
 
 	/** The file content for this sequence. */
 	protected final byte[] content;
@@ -110,6 +110,14 @@
 		this(IO.readFully(file));
 	}
 
+	/**
+	 * @return the raw, unprocessed content read.
+	 * @since 4.11
+	 */
+	public byte[] getRawContent() {
+		return content;
+	}
+
 	/** @return total number of items in the sequence. */
 	/** {@inheritDoc} */
 	@Override
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 d41a1f5..0b03eb1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -56,6 +56,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.eclipse.jgit.api.errors.CanceledException;
 import org.eclipse.jgit.api.errors.FilterFailedException;
 import org.eclipse.jgit.attributes.FilterCommand;
 import org.eclipse.jgit.attributes.FilterCommandRegistry;
@@ -66,6 +67,7 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
@@ -76,6 +78,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@@ -158,6 +161,8 @@
 
 	private boolean performingCheckout;
 
+	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
 	/**
 	 * Get list of updated paths and smudgeFilterCommands
 	 *
@@ -288,6 +293,18 @@
 	}
 
 	/**
+	 * Set a progress monitor which can be passed to built-in filter commands,
+	 * providing progress information for long running tasks.
+	 *
+	 * @param monitor
+	 *            the {@link ProgressMonitor}
+	 * @since 4.11
+	 */
+	public void setProgressMonitor(ProgressMonitor monitor) {
+		this.monitor = monitor != null ? monitor : NullProgressMonitor.INSTANCE;
+	}
+
+	/**
 	 * Scan head, index and merge tree. Used during normal checkout or merge
 	 * operations.
 	 *
@@ -465,6 +482,10 @@
 	public boolean checkout() throws IOException {
 		try {
 			return doCheckout();
+		} catch (CanceledException ce) {
+			// should actually be propagated, but this would change a LOT of
+			// APIs
+			throw new IOException(ce);
 		} finally {
 			try {
 				dc.unlock();
@@ -482,7 +503,7 @@
 
 	private boolean doCheckout() throws CorruptObjectException, IOException,
 			MissingObjectException, IncorrectObjectTypeException,
-			CheckoutConflictException, IndexWriteException {
+			CheckoutConflictException, IndexWriteException, CanceledException {
 		toBeDeleted.clear();
 		try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
 			if (headCommitTree != null)
@@ -500,6 +521,10 @@
 			// update our index
 			builder.finish();
 
+			// init progress reporting
+			int numTotal = removed.size() + updated.size();
+			monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);
+
 			performingCheckout = true;
 			File file = null;
 			String last = null;
@@ -525,6 +550,12 @@
 						removeEmptyParents(new File(repo.getWorkTree(), last));
 					last = r;
 				}
+				monitor.update(1);
+				if (monitor.isCancelled()) {
+					throw new CanceledException(MessageFormat.format(
+							JGitText.get().operationCanceled,
+							JGitText.get().checkingOutFiles));
+				}
 			}
 			if (file != null) {
 				removeEmptyParents(file);
@@ -540,10 +571,19 @@
 					String path = e.getKey();
 					CheckoutMetadata meta = e.getValue();
 					DirCacheEntry entry = dc.getEntry(path);
-					if (!FileMode.GITLINK.equals(entry.getRawMode())) {
+					if (FileMode.GITLINK.equals(entry.getRawMode())) {
+						checkoutGitlink(path, entry);
+					} else {
 						checkoutEntry(repo, entry, objectReader, false, meta);
 					}
 					e = null;
+
+					monitor.update(1);
+					if (monitor.isCancelled()) {
+						throw new CanceledException(MessageFormat.format(
+								JGitText.get().operationCanceled,
+								JGitText.get().checkingOutFiles));
+					}
 				}
 			} catch (Exception ex) {
 				// We didn't actually modify the current entry nor any that
@@ -557,6 +597,8 @@
 				}
 				throw ex;
 			}
+			monitor.endTask();
+
 			// commit the index builder - a new index is persisted
 			if (!builder.commit())
 				throw new IndexWriteException();
@@ -564,6 +606,14 @@
 		return toBeDeleted.size() == 0;
 	}
 
+	private void checkoutGitlink(String path, DirCacheEntry entry)
+			throws IOException {
+		File gitlinkDir = new File(repo.getWorkTree(), path);
+		FileUtils.mkdirs(gitlinkDir, true);
+		FS fs = repo.getFS();
+		entry.setLastModified(fs.lastModified(gitlinkDir));
+	}
+
 	private static ArrayList<String> filterOut(ArrayList<String> strings,
 			IntList indicesToRemove) {
 		int n = indicesToRemove.size();
@@ -1490,6 +1540,10 @@
 	private static void runBuiltinFilterCommand(Repository repo,
 			CheckoutMetadata checkoutMetadata, ObjectLoader ol,
 			OutputStream channel) throws MissingObjectException, IOException {
+		boolean isMandatory = repo.getConfig().getBoolean(
+				ConfigConstants.CONFIG_FILTER_SECTION,
+				ConfigConstants.CONFIG_SECTION_LFS,
+				ConfigConstants.CONFIG_KEY_REQUIRED, false);
 		FilterCommand command = null;
 		try {
 			command = FilterCommandRegistry.createFilterCommand(
@@ -1497,9 +1551,14 @@
 					channel);
 		} catch (IOException e) {
 			LOG.error(JGitText.get().failedToDetermineFilterDefinition, e);
-			// In case an IOException occurred during creating of the command
-			// then proceed as if there would not have been a builtin filter.
-			ol.copyTo(channel);
+			if (!isMandatory) {
+				// In case an IOException occurred during creating of the
+				// command then proceed as if there would not have been a
+				// builtin filter (only if the filter is not mandatory).
+				ol.copyTo(channel);
+			} else {
+				throw e;
+			}
 		}
 		if (command != null) {
 			while (command.run() != -1) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 68521d3..d768216 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -329,11 +329,8 @@
 			AttributesNode r = new AttributesNode();
 			ObjectLoader loader = reader.open(objectId);
 			if (loader != null) {
-				InputStream in = loader.openStream();
-				try {
+				try (InputStream in = loader.openStream()) {
 					r.parse(in);
-				} finally {
-					in.close();
 				}
 			}
 			return r.getRules().isEmpty() ? null : r;
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 c7bc1b6..8b8df87 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -54,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
 
@@ -124,7 +125,6 @@
 	private boolean ignoreRemoteFailures = false;
 
 	private List<RepoProject> bareProjects;
-	private Git git;
 	private ProgressMonitor monitor;
 
 	/**
@@ -486,58 +486,50 @@
 	/** {@inheritDoc} */
 	@Override
 	public RevCommit call() throws GitAPIException {
-		try {
-			checkCallable();
-			if (baseUri == null) {
-				baseUri = ""; //$NON-NLS-1$
-			}
-			if (inputStream == null) {
-				if (manifestPath == null || manifestPath.length() == 0)
-					throw new IllegalArgumentException(
-							JGitText.get().pathNotConfigured);
-				try {
-					inputStream = new FileInputStream(manifestPath);
-				} catch (IOException e) {
-					throw new IllegalArgumentException(
-							JGitText.get().pathNotConfigured);
-				}
-			}
-
-			if (repo.isBare()) {
-				bareProjects = new ArrayList<>();
-				if (author == null)
-					author = new PersonIdent(repo);
-				if (callback == null)
-					callback = new DefaultRemoteReader();
-			} else
-				git = new Git(repo);
-
-			ManifestParser parser = new ManifestParser(
-					includedReader, manifestPath, branch, baseUri, groupsParam, repo);
+		checkCallable();
+		if (baseUri == null) {
+			baseUri = ""; //$NON-NLS-1$
+		}
+		if (inputStream == null) {
+			if (manifestPath == null || manifestPath.length() == 0)
+				throw new IllegalArgumentException(
+						JGitText.get().pathNotConfigured);
 			try {
-				parser.read(inputStream);
-				for (RepoProject proj : parser.getFilteredProjects()) {
-					addSubmodule(proj.getUrl(),
-							proj.getPath(),
-							proj.getRevision(),
-							proj.getCopyFiles(),
-							proj.getLinkFiles(),
-							proj.getGroups(),
-							proj.getRecommendShallow());
-				}
-			} catch (GitAPIException | IOException e) {
-				throw new ManifestErrorException(e);
+				inputStream = new FileInputStream(manifestPath);
+			} catch (IOException e) {
+				throw new IllegalArgumentException(
+						JGitText.get().pathNotConfigured);
 			}
+		}
+
+		List<RepoProject> filteredProjects;
+		try {
+			ManifestParser parser = new ManifestParser(includedReader,
+					manifestPath, branch, baseUri, groupsParam, repo);
+			parser.read(inputStream);
+			filteredProjects = parser.getFilteredProjects();
+		} catch (IOException e) {
+			throw new ManifestErrorException(e);
 		} finally {
 			try {
-				if (inputStream != null)
-					inputStream.close();
+				inputStream.close();
 			} catch (IOException e) {
 				// Just ignore it, it's not important.
 			}
 		}
 
 		if (repo.isBare()) {
+			bareProjects = new ArrayList<>();
+			if (author == null)
+				author = new PersonIdent(repo);
+			if (callback == null)
+				callback = new DefaultRemoteReader();
+			for (RepoProject proj : filteredProjects) {
+				addSubmoduleBare(proj.getUrl(), proj.getPath(),
+						proj.getRevision(), proj.getCopyFiles(),
+						proj.getLinkFiles(), proj.getGroups(),
+						proj.getRecommendShallow());
+			}
 			DirCache index = DirCache.newInCore();
 			DirCacheBuilder builder = index.builder();
 			ObjectInserter inserter = repo.newObjectInserter();
@@ -552,10 +544,7 @@
 						objectId = ObjectId.fromString(proj.getRevision());
 					} else {
 						objectId = callback.sha1(nameUri, proj.getRevision());
-						if (objectId == null) {
-							if (ignoreRemoteFailures) {
-								continue;
-							}
+						if (objectId == null && !ignoreRemoteFailures) {
 							throw new RemoteUnavailableException(nameUri);
 						}
 						if (recordRemoteBranch) {
@@ -594,38 +583,40 @@
 					cfg.setString("submodule", path, "url", submodUrl.toString()); //$NON-NLS-1$ //$NON-NLS-2$
 
 					// create gitlink
-					DirCacheEntry dcEntry = new DirCacheEntry(path);
-					dcEntry.setObjectId(objectId);
-					dcEntry.setFileMode(FileMode.GITLINK);
-					builder.add(dcEntry);
+					if (objectId != null) {
+						DirCacheEntry dcEntry = new DirCacheEntry(path);
+						dcEntry.setObjectId(objectId);
+						dcEntry.setFileMode(FileMode.GITLINK);
+						builder.add(dcEntry);
 
-					for (CopyFile copyfile : proj.getCopyFiles()) {
-						byte[] src = callback.readFile(
+						for (CopyFile copyfile : proj.getCopyFiles()) {
+							byte[] src = callback.readFile(
 								nameUri, proj.getRevision(), copyfile.src);
-						objectId = inserter.insert(Constants.OBJ_BLOB, src);
-						dcEntry = new DirCacheEntry(copyfile.dest);
-						dcEntry.setObjectId(objectId);
-						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, src);
+							dcEntry = new DirCacheEntry(copyfile.dest);
+							dcEntry.setObjectId(objectId);
+							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,
+							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);
+									Constants.CHARACTER_ENCODING));
+							dcEntry = new DirCacheEntry(linkfile.dest);
+							dcEntry.setObjectId(objectId);
+							dcEntry.setFileMode(FileMode.SYMLINK);
+							builder.add(dcEntry);
+						}
 					}
 				}
 				String content = cfg.toText();
@@ -653,6 +644,11 @@
 
 				// Create a Commit object, populate it and write it
 				ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$
+				if (headId != null && rw.parseCommit(headId).getTree().getId().equals(treeId)) {
+					// No change. Do nothing.
+					return rw.parseCommit(headId);
+				}
+
 				CommitBuilder commit = new CommitBuilder();
 				commit.setTreeId(treeId);
 				if (headId != null)
@@ -689,53 +685,62 @@
 				}
 
 				return rw.parseCommit(commitId);
-			} catch (IOException e) {
+			} catch (GitAPIException | IOException e) {
 				throw new ManifestErrorException(e);
 			}
 		} else {
-			return git
-				.commit()
-				.setMessage(RepoText.get().repoCommitMessage)
-				.call();
+			try (Git git = new Git(repo)) {
+				for (RepoProject proj : filteredProjects) {
+					addSubmodule(proj.getUrl(), proj.getPath(),
+							proj.getRevision(), proj.getCopyFiles(),
+							proj.getLinkFiles(), git);
+				}
+				return git.commit().setMessage(RepoText.get().repoCommitMessage)
+						.call();
+			} catch (GitAPIException | IOException e) {
+				throw new ManifestErrorException(e);
+			}
 		}
 	}
 
 	private void addSubmodule(String url, String path, String revision,
-			List<CopyFile> copyfiles, List<LinkFile> linkfiles,
-			Set<String> groups, String recommendShallow)
+			List<CopyFile> copyfiles, List<LinkFile> linkfiles, Git git)
 			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)
-				.setURI(url);
-			if (monitor != null)
-				add.setProgressMonitor(monitor);
-
-			Repository subRepo = add.call();
-			if (revision != null) {
-				try (Git sub = new Git(subRepo)) {
-					sub.checkout().setName(findRef(revision, subRepo))
-							.call();
-				}
-				subRepo.close();
-				git.add().addFilepattern(path).call();
-			}
-			for (CopyFile copyfile : copyfiles) {
-				copyfile.copy();
-				git.add().addFilepattern(copyfile.dest).call();
-			}
+		assert (!repo.isBare());
+		assert (git != null);
+		if (!linkfiles.isEmpty()) {
+			throw new UnsupportedOperationException(
+					JGitText.get().nonBareLinkFilesNotSupported);
 		}
+
+		SubmoduleAddCommand add = git.submoduleAdd().setPath(path).setURI(url);
+		if (monitor != null)
+			add.setProgressMonitor(monitor);
+
+		Repository subRepo = add.call();
+		if (revision != null) {
+			try (Git sub = new Git(subRepo)) {
+				sub.checkout().setName(findRef(revision, subRepo)).call();
+			}
+			subRepo.close();
+			git.add().addFilepattern(path).call();
+		}
+		for (CopyFile copyfile : copyfiles) {
+			copyfile.copy();
+			git.add().addFilepattern(copyfile.dest).call();
+		}
+	}
+
+	private void addSubmoduleBare(String url, String path, String revision,
+			List<CopyFile> copyfiles, List<LinkFile> linkfiles,
+			Set<String> groups, String recommendShallow) {
+		assert (repo.isBare());
+		assert (bareProjects != null);
+		RepoProject proj = new RepoProject(url, path, revision, null, groups,
+				recommendShallow);
+		proj.addCopyFiles(copyfiles);
+		proj.addLinkFiles(linkfiles);
+		bareProjects.add(proj);
 	}
 
 	/*
@@ -745,12 +750,7 @@
 	 */
 	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())) {
+		if (!Objects.equals(current.getHost(), target.getHost())) {
 			return target;
 		}
 
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 fb015c9..ad43e2c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -172,4 +172,15 @@
 		}
 	}
 
+	/**
+	 * Check whether a 'native' (i.e. script) hook is installed in the
+	 * repository.
+	 *
+	 * @return whether a native hook script is installed in the repository.
+	 * @since 4.11
+	 */
+	public boolean isNativeHookPresent() {
+		return FS.DETECTED.findHook(getRepository(), getHookName()) != null;
+	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
index 87db36b..79395ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
@@ -43,8 +43,11 @@
 package org.eclipse.jgit.hooks;
 
 import java.io.PrintStream;
+import java.text.MessageFormat;
 
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.LfsFactory;
 
 /**
  * Factory class for instantiating supported hooks.
@@ -107,6 +110,17 @@
 	 * @since 4.2
 	 */
 	public static PrePushHook prePush(Repository repo, PrintStream outputStream) {
+		if (LfsFactory.getInstance().isAvailable()) {
+			PrePushHook hook = LfsFactory.getInstance().getPrePushHook(repo,
+					outputStream);
+			if (hook != null) {
+				if (hook.isNativeHookPresent()) {
+					throw new IllegalStateException(MessageFormat
+							.format(JGitText.get().lfsHookConflict, repo));
+				}
+				return hook;
+			}
+		}
 		return new PrePushHook(repo, outputStream);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
index f974eb7..051a1d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
@@ -137,6 +137,16 @@
 	}
 
 	/**
+	 * Get remote name
+	 *
+	 * @return remote name or null
+	 * @since 4.11
+	 */
+	protected String getRemoteName() {
+		return remoteName;
+	}
+
+	/**
 	 * Set remote location
 	 *
 	 * @param location
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
index 460f0ed..31e173b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
@@ -152,11 +152,35 @@
 	 *         result.
 	 */
 	public boolean isMatch(String path, boolean directory) {
+		return isMatch(path, directory, false);
+	}
+
+	/**
+	 * Returns true if a match was made. <br>
+	 * This function does NOT return the actual ignore status of the target!
+	 * Please consult {@link #getResult()} for the negation status. The actual
+	 * ignore status may be true or false depending on whether this rule is an
+	 * ignore rule or a negation rule.
+	 *
+	 * @param path
+	 *            Name pattern of the file, relative to the base directory of
+	 *            this rule
+	 * @param directory
+	 *            Whether the target file is a directory or not
+	 * @param pathMatch
+	 *            {@code true} if the match is for the full path: see
+	 *            {@link IMatcher#matches(String, int, int)}
+	 * @return True if a match was made. This does not necessarily mean that the
+	 *         target is ignored. Call {@link #getResult() getResult()} for the
+	 *         result.
+	 * @since 4.11
+	 */
+	public boolean isMatch(String path, boolean directory, boolean pathMatch) {
 		if (path == null)
 			return false;
 		if (path.length() == 0)
 			return false;
-		boolean match = matcher.matches(path, directory, false);
+		boolean match = matcher.matches(path, directory, pathMatch);
 		return match;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index 1ad6010..1224df4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -145,7 +145,13 @@
 	 * @return status of the path.
 	 */
 	public MatchResult isIgnored(String entryPath, boolean isDirectory) {
-		return isIgnored(entryPath, isDirectory, false);
+		final Boolean result = checkIgnored(entryPath, isDirectory);
+		if (result == null) {
+			return MatchResult.CHECK_PARENT;
+		}
+
+		return result.booleanValue() ? MatchResult.IGNORED
+				: MatchResult.NOT_IGNORED;
 	}
 
 	/**
@@ -159,45 +165,48 @@
 	 *            true if the target item is a directory.
 	 * @param negateFirstMatch
 	 *            true if the first match should be negated
+	 * @deprecated negateFirstMatch is not honored anymore
 	 * @return status of the path.
 	 * @since 3.6
 	 */
+	@Deprecated
 	public MatchResult isIgnored(String entryPath, boolean isDirectory,
 			boolean negateFirstMatch) {
-		if (rules.isEmpty())
-			if (negateFirstMatch)
-				return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH;
-			else
-				return MatchResult.CHECK_PARENT;
+		final Boolean result = checkIgnored(entryPath, isDirectory);
+		if (result == null) {
+			return negateFirstMatch
+					? MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH
+					: MatchResult.CHECK_PARENT;
+		}
 
-		// Parse rules in the reverse order that they were read
+		return result.booleanValue() ? MatchResult.IGNORED
+				: MatchResult.NOT_IGNORED;
+	}
+
+	/**
+	 * Determine if an entry path matches an ignore rule.
+	 *
+	 * @param entryPath
+	 *            the path to test. The path must be relative to this ignore
+	 *            node's own repository path, and in repository path format
+	 *            (uses '/' and not '\').
+	 * @param isDirectory
+	 *            true if the target item is a directory.
+	 * @return Boolean.TRUE, if the entry is ignored; Boolean.FALSE, if the
+	 *         entry is forced to be not ignored (negated match); or null, if
+	 *         undetermined
+	 * @since 4.11
+	 */
+	public Boolean checkIgnored(String entryPath, boolean isDirectory) {
+		// Parse rules in the reverse order that they were read because later
+		// rules have higher priority
 		for (int i = rules.size() - 1; i > -1; i--) {
 			FastIgnoreRule rule = rules.get(i);
-			if (rule.isMatch(entryPath, isDirectory)) {
-				if (rule.getResult()) {
-					// rule matches: path could be ignored
-					if (negateFirstMatch)
-						// ignore current match, reset "negate" flag, continue
-						negateFirstMatch = false;
-					else
-						// valid match, just return
-						return MatchResult.IGNORED;
-				} else {
-					// found negated rule
-					if (negateFirstMatch)
-						// not possible to re-include excluded ignore rule
-						return MatchResult.NOT_IGNORED;
-					else
-						// set the flag and continue
-						negateFirstMatch = true;
-				}
+			if (rule.isMatch(entryPath, isDirectory, true)) {
+				return Boolean.valueOf(rule.getResult());
 			}
 		}
-		if (negateFirstMatch)
-			// negated rule found but there is no previous rule in *this* file
-			return MatchResult.CHECK_PARENT_NEGATE_FIRST_MATCH;
-		// *this* file has no matching rules
-		return MatchResult.CHECK_PARENT;
+		return null;
 	}
 
 	/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
index dbf0638..14440d2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
@@ -58,8 +58,7 @@
 		}
 
 		@Override
-		public boolean matches(String segment, int startIncl, int endExcl,
-				boolean assumeDirectory) {
+		public boolean matches(String segment, int startIncl, int endExcl) {
 			return false;
 		}
 	};
@@ -91,11 +90,7 @@
 	 *            start index, inclusive
 	 * @param endExcl
 	 *            end index, exclusive
-	 * @param assumeDirectory
-	 *            true to assume this path as directory (even if it doesn't end
-	 *            with a slash)
 	 * @return true if this matcher pattern matches given string
 	 */
-	boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory);
+	boolean matches(String segment, int startIncl, int endExcl);
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
index 2bae8f2..fdd39bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
@@ -57,8 +57,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory) {
+	public boolean matches(String segment, int startIncl, int endExcl) {
 		// faster local access, same as in string.indexOf()
 		String s = subPattern;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
index 6a4b2b8..33f0fe7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
@@ -91,12 +91,12 @@
 			}
 			boolean match;
 			if (lastSlash < start) {
-				match = matches(path, start, stop, assumeDirectory);
+				match = matches(path, start, stop);
 			} else {
 				// Can't match if the path contains a slash if the pattern is
 				// anchored at the beginning
 				match = !beginning
-						&& matches(path, lastSlash + 1, stop, assumeDirectory);
+						&& matches(path, lastSlash + 1, stop);
 			}
 			if (match && dirOnly) {
 				match = assumeDirectory;
@@ -108,7 +108,7 @@
 			if (end < 0) {
 				end = stop;
 			}
-			if (end > start && matches(path, start, end, assumeDirectory)) {
+			if (end > start && matches(path, start, end)) {
 				// make sure the directory matches: either if we are done with
 				// segment and there is next one, or if the directory is assumed
 				return !dirOnly || assumeDirectory || end < stop;
@@ -123,8 +123,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory) {
+	public boolean matches(String segment, int startIncl, int endExcl) {
 		// faster local access, same as in string.indexOf()
 		String s = subPattern;
 		int length = s.length();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
index 50e78ae..3c0f17a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
@@ -61,7 +61,10 @@
  */
 public class PathMatcher extends AbstractMatcher {
 
-	private static final WildMatcher WILD = WildMatcher.INSTANCE;
+	private static final WildMatcher WILD_NO_DIRECTORY = new WildMatcher(false);
+
+	private static final WildMatcher WILD_ONLY_DIRECTORY = new WildMatcher(
+			true);
 
 	private final List<IMatcher> matchers;
 
@@ -94,11 +97,15 @@
 		for (int i = 0; i < segments.size(); i++) {
 			String segment = segments.get(i);
 			IMatcher matcher = createNameMatcher0(segment, pathSeparator,
-					dirOnly);
-			if (matcher == WILD && i > 0
-					&& matchers.get(matchers.size() - 1) == WILD)
-				// collapse wildmatchers **/** is same as **
-				continue;
+					dirOnly, i == segments.size() - 1);
+			if (i > 0) {
+				final IMatcher last = matchers.get(matchers.size() - 1);
+				if (isWild(matcher) && isWild(last))
+					// collapse wildmatchers **/** is same as **, but preserve
+					// dirOnly flag (i.e. always use the last wildmatcher)
+					matchers.remove(matchers.size() - 1);
+			}
+
 			matchers.add(matcher);
 		}
 		return matchers;
@@ -126,7 +133,7 @@
 		int slashIdx = pattern.indexOf(slash, 1);
 		if (slashIdx > 0 && slashIdx < pattern.length() - 1)
 			return new PathMatcher(pattern, pathSeparator, dirOnly);
-		return createNameMatcher0(pattern, pathSeparator, dirOnly);
+		return createNameMatcher0(pattern, pathSeparator, dirOnly, true);
 	}
 
 	/**
@@ -153,12 +160,13 @@
 	}
 
 	private static IMatcher createNameMatcher0(String segment,
-			Character pathSeparator, boolean dirOnly)
+			Character pathSeparator, boolean dirOnly, boolean lastSegment)
 			throws InvalidPatternException {
 		// check if we see /** or ** segments => double star pattern
 		if (WildMatcher.WILDMATCH.equals(segment)
 				|| WildMatcher.WILDMATCH2.equals(segment))
-			return WILD;
+			return dirOnly && lastSegment ? WILD_ONLY_DIRECTORY
+					: WILD_NO_DIRECTORY;
 
 		PatternState state = checkWildCards(segment);
 		switch (state) {
@@ -218,8 +226,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory) {
+	public boolean matches(String segment, int startIncl, int endExcl) {
 		throw new UnsupportedOperationException(
 				"Path matcher works only on entire paths"); //$NON-NLS-1$
 	}
@@ -241,18 +248,18 @@
 			if (right == -1) {
 				if (left < endExcl) {
 					match = matches(matcher, path, left, endExcl,
-							assumeDirectory);
+							assumeDirectory, pathMatch);
 				} else {
 					// a/** should not match a/ or a
-					match = match && matchers.get(matcher) != WILD;
+					match = match && !isWild(matchers.get(matcher));
 				}
 				if (match) {
 					if (matcher < matchers.size() - 1
-							&& matchers.get(matcher) == WILD) {
+							&& isWild(matchers.get(matcher))) {
 						// ** can match *nothing*: a/**/b match also a/b
 						matcher++;
 						match = matches(matcher, path, left, endExcl,
-								assumeDirectory);
+								assumeDirectory, pathMatch);
 					} else if (dirOnly && !assumeDirectory) {
 						// Directory expectations not met
 						return false;
@@ -264,14 +271,15 @@
 				wildmatchBacktrackPos = right;
 			}
 			if (right - left > 0) {
-				match = matches(matcher, path, left, right, assumeDirectory);
+				match = matches(matcher, path, left, right, assumeDirectory,
+						pathMatch);
 			} else {
 				// path starts with slash???
 				right++;
 				continue;
 			}
 			if (match) {
-				boolean wasWild = matchers.get(matcher) == WILD;
+				boolean wasWild = isWild(matchers.get(matcher));
 				if (wasWild) {
 					lastWildmatch = matcher;
 					wildmatchBacktrackPos = -1;
@@ -317,9 +325,19 @@
 	}
 
 	private boolean matches(int matcherIdx, String path, int startIncl,
-			int endExcl,
-			boolean assumeDirectory) {
+			int endExcl, boolean assumeDirectory, boolean pathMatch) {
 		IMatcher matcher = matchers.get(matcherIdx);
-		return matcher.matches(path, startIncl, endExcl, assumeDirectory);
+
+		final boolean matches = matcher.matches(path, startIncl, endExcl);
+		if (!matches || !pathMatch || matcherIdx < matchers.size() - 1
+				|| !(matcher instanceof AbstractMatcher)) {
+			return matches;
+		}
+
+		return assumeDirectory || !((AbstractMatcher) matcher).dirOnly;
+	}
+
+	private static boolean isWild(IMatcher matcher) {
+		return matcher == WILD_NO_DIRECTORY || matcher == WILD_ONLY_DIRECTORY;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
index 7df42b7..1e0335c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
@@ -57,8 +57,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory) {
+	public boolean matches(String segment, int startIncl, int endExcl) {
 		// faster local access, same as in string.indexOf()
 		String s = subPattern;
 		// we don't need to count '*' character itself
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
index ca8c581..3bbac81 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
@@ -66,8 +66,7 @@
 
 	/** {@inheritDoc} */
 	@Override
-	public boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory) {
+	public boolean matches(String segment, int startIncl, int endExcl) {
 		return p.matcher(segment.substring(startIncl, endExcl)).matches();
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
index ba8015c..2ad87da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
@@ -55,24 +55,26 @@
 	// double star for the beginning of pattern
 	static final String WILDMATCH2 = "/**"; //$NON-NLS-1$
 
-	static final WildMatcher INSTANCE = new WildMatcher();
-
-	private WildMatcher() {
-		super(WILDMATCH, false);
+	WildMatcher(boolean dirOnly) {
+		super(WILDMATCH, dirOnly);
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public final boolean matches(String path, boolean assumeDirectory,
 			boolean pathMatch) {
-		return true;
+		return !dirOnly || assumeDirectory
+				|| !pathMatch && isSubdirectory(path);
 	}
 
 	/** {@inheritDoc} */
 	@Override
-	public final boolean matches(String segment, int startIncl, int endExcl,
-			boolean assumeDirectory) {
+	public final boolean matches(String segment, int startIncl, int endExcl) {
 		return true;
 	}
 
+	private static boolean isSubdirectory(String path) {
+		final int slashIndex = path.indexOf('/');
+		return slashIndex >= 0 && slashIndex < path.length() - 1;
+	}
 }
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 72755ce..6f306b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -102,6 +102,8 @@
 	/***/ public String blameNotCommittedYet;
 	/***/ public String blobNotFound;
 	/***/ public String blobNotFoundForPath;
+	/***/ public String blockLimitNotMultipleOfBlockSize;
+	/***/ public String blockLimitNotPositive;
 	/***/ public String blockSizeNotPowerOf2;
 	/***/ public String bothRefTargetsMustNotBeNull;
 	/***/ public String branchNameInvalid;
@@ -177,6 +179,7 @@
 	/***/ public String cantPassMeATree;
 	/***/ public String channelMustBeInRange1_255;
 	/***/ public String characterClassIsNotSupported;
+	/***/ public String checkingOutFiles;
 	/***/ public String checkoutConflictWithFile;
 	/***/ public String checkoutConflictWithFiles;
 	/***/ public String checkoutUnexpectedResult;
@@ -430,6 +433,7 @@
 	/***/ public String invalidIntegerValue;
 	/***/ public String invalidKey;
 	/***/ public String invalidLineInConfigFile;
+	/***/ public String invalidLineInConfigFileWithParam;
 	/***/ public String invalidModeFor;
 	/***/ public String invalidModeForPath;
 	/***/ public String invalidObject;
@@ -468,6 +472,7 @@
 	/***/ public String largeObjectException;
 	/***/ public String largeObjectOutOfMemory;
 	/***/ public String lengthExceedsMaximumArraySize;
+	/***/ public String lfsHookConflict;
 	/***/ public String listingAlternates;
 	/***/ public String listingPacks;
 	/***/ public String localObjectsIncomplete;
@@ -522,6 +527,7 @@
 	/***/ public String noMergeBase;
 	/***/ public String noMergeHeadSpecified;
 	/***/ public String nonBareLinkFilesNotSupported;
+	/***/ public String noPathAttributesFound;
 	/***/ public String noSuchRef;
 	/***/ public String noSuchSubmodule;
 	/***/ public String notABoolean;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index e558a81..cd7901b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -95,13 +95,23 @@
 	/**
 	 * Set maximum number bytes of heap memory to dedicate to caching pack file
 	 * data.
+	 * <p>
+	 * It is strongly recommended to set the block limit to be an integer multiple
+	 * of the block size. This constraint is not enforced by this method (since
+	 * it may be called before {@link #setBlockSize(int)}), but it is enforced by
+	 * {@link #fromConfig(Config)}.
 	 *
 	 * @param newLimit
 	 *            maximum number bytes of heap memory to dedicate to caching
-	 *            pack file data.
+	 *            pack file data; must be positive.
 	 * @return {@code this}
 	 */
 	public DfsBlockCacheConfig setBlockLimit(final long newLimit) {
+		if (newLimit <= 0) {
+			throw new IllegalArgumentException(MessageFormat.format(
+					JGitText.get().blockLimitNotPositive,
+					Long.valueOf(newLimit)));
+		}
 		blockLimit = newLimit;
 		return this;
 	}
@@ -188,23 +198,34 @@
 	 * <p>
 	 * If a property is not defined in the configuration, then it is left
 	 * unmodified.
+	 * <p>
+	 * Enforces certain constraints on the combination of settings in the config,
+	 * for example that the block limit is a multiple of the block size.
 	 *
 	 * @param rc
 	 *            configuration to read properties from.
 	 * @return {@code this}
 	 */
 	public DfsBlockCacheConfig fromConfig(final Config rc) {
-		setBlockLimit(rc.getLong(
+		long cfgBlockLimit = rc.getLong(
 				CONFIG_CORE_SECTION,
 				CONFIG_DFS_SECTION,
 				CONFIG_KEY_BLOCK_LIMIT,
-				getBlockLimit()));
-
-		setBlockSize(rc.getInt(
+				getBlockLimit());
+		int cfgBlockSize = rc.getInt(
 				CONFIG_CORE_SECTION,
 				CONFIG_DFS_SECTION,
 				CONFIG_KEY_BLOCK_SIZE,
-				getBlockSize()));
+				getBlockSize());
+		if (cfgBlockLimit % cfgBlockSize != 0) {
+			throw new IllegalArgumentException(MessageFormat.format(
+					JGitText.get().blockLimitNotMultipleOfBlockSize,
+					Long.valueOf(cfgBlockLimit),
+					Long.valueOf(cfgBlockSize)));
+		}
+
+		setBlockLimit(cfgBlockLimit);
+		setBlockSize(cfgBlockSize);
 
 		setConcurrencyLevel(rc.getInt(
 				CONFIG_CORE_SECTION,
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 27a7992..197114b 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
@@ -44,12 +44,12 @@
 
 package org.eclipse.jgit.internal.storage.dfs;
 
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -578,10 +578,22 @@
 	public void selectObjectRepresentation(PackWriter packer,
 			ProgressMonitor monitor, Iterable<ObjectToPack> objects)
 			throws IOException, MissingObjectException {
-		// Don't check dirty bit on PackList; assume ObjectToPacks all came from the
-		// current list.
-		for (DfsPackFile pack : sortPacksForSelectRepresentation()) {
-			List<DfsObjectToPack> tmp = findAllFromPack(pack, objects);
+		// Don't check dirty bit on PackList; assume ObjectToPacks all came
+		// from the current list.
+		List<DfsPackFile> packs = sortPacksForSelectRepresentation();
+		trySelectRepresentation(packer, monitor, objects, packs, false);
+
+		List<DfsPackFile> garbage = garbagePacksForSelectRepresentation();
+		if (!garbage.isEmpty() && checkGarbagePacks(objects)) {
+			trySelectRepresentation(packer, monitor, objects, garbage, true);
+		}
+	}
+
+	private void trySelectRepresentation(PackWriter packer,
+			ProgressMonitor monitor, Iterable<ObjectToPack> objects,
+			List<DfsPackFile> packs, boolean skipFound) throws IOException {
+		for (DfsPackFile pack : packs) {
+			List<DfsObjectToPack> tmp = findAllFromPack(pack, objects, skipFound);
 			if (tmp.isEmpty())
 				continue;
 			Collections.sort(tmp, OFFSET_SORT);
@@ -620,24 +632,54 @@
 		}
 	};
 
-	private DfsPackFile[] sortPacksForSelectRepresentation()
+	private List<DfsPackFile> sortPacksForSelectRepresentation()
 			throws IOException {
 		DfsPackFile[] packs = db.getPacks();
-		DfsPackFile[] sorted = new DfsPackFile[packs.length];
-		System.arraycopy(packs, 0, sorted, 0, packs.length);
-		Arrays.sort(sorted, PACK_SORT_FOR_REUSE);
+		List<DfsPackFile> sorted = new ArrayList<>(packs.length);
+		for (DfsPackFile p : packs) {
+			if (p.getPackDescription().getPackSource() != UNREACHABLE_GARBAGE) {
+				sorted.add(p);
+			}
+		}
+		Collections.sort(sorted, PACK_SORT_FOR_REUSE);
 		return sorted;
 	}
 
+	private List<DfsPackFile> garbagePacksForSelectRepresentation()
+			throws IOException {
+		DfsPackFile[] packs = db.getPacks();
+		List<DfsPackFile> garbage = new ArrayList<>(packs.length);
+		for (DfsPackFile p : packs) {
+			if (p.getPackDescription().getPackSource() == UNREACHABLE_GARBAGE) {
+				garbage.add(p);
+			}
+		}
+		return garbage;
+	}
+
+	private static boolean checkGarbagePacks(Iterable<ObjectToPack> objects) {
+		for (ObjectToPack otp : objects) {
+			if (!((DfsObjectToPack) otp).isFound()) {
+				return true;
+			}
+		}
+		return false;
+	}
+
 	private List<DfsObjectToPack> findAllFromPack(DfsPackFile pack,
-			Iterable<ObjectToPack> objects) throws IOException {
+			Iterable<ObjectToPack> objects, boolean skipFound)
+					throws IOException {
 		List<DfsObjectToPack> tmp = new BlockList<>();
 		PackIndex idx = pack.getPackIndex(this);
-		for (ObjectToPack otp : objects) {
+		for (ObjectToPack obj : objects) {
+			DfsObjectToPack otp = (DfsObjectToPack) obj;
+			if (skipFound && otp.isFound()) {
+				continue;
+			}
 			long p = idx.findOffset(otp);
 			if (0 < p && !pack.isCorrupt(p)) {
 				otp.setOffset(p);
-				tmp.add((DfsObjectToPack) otp);
+				tmp.add(otp);
 			}
 		}
 		return tmp;
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
index cf925c9..c35801f 100644
--- 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
@@ -67,10 +67,16 @@
 		/** Total number of block cache hits. */
 		long blockCacheHit;
 
-		/** Total number of discrete blocks read from pack file(s). */
+		/**
+		 * Total number of discrete blocks actually read from pack file(s), that is,
+		 * block cache misses.
+		 */
 		long readBlock;
 
-		/** Total number of compressed bytes read as block sized units. */
+		/**
+		 * Total number of compressed bytes read during cache misses, as block sized
+		 * units.
+		 */
 		long readBlockBytes;
 
 		/** Total microseconds spent reading {@link #readBlock} blocks. */
@@ -144,7 +150,8 @@
 	}
 
 	/**
-	 * Get total number of discrete blocks read from pack file(s).
+	 * Get total number of discrete blocks actually read from pack file(s), that
+	 * is, block cache misses.
 	 *
 	 * @return total number of discrete blocks read from pack file(s).
 	 */
@@ -153,7 +160,8 @@
 	}
 
 	/**
-	 * Get total number of compressed bytes read as block sized units.
+	 * Get total number of compressed bytes read during cache misses, as block
+	 * sized units.
 	 *
 	 * @return total number of compressed bytes read as block sized units.
 	 */
@@ -162,7 +170,7 @@
 	}
 
 	/**
-	 * Get total microseconds spent reading blocks.
+	 * Get total microseconds spent reading blocks during cache misses.
 	 *
 	 * @return total microseconds spent reading blocks.
 	 */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
index 9c844eb..40cfb71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
@@ -241,7 +241,7 @@
 					: table.seekRef(prefix)) {
 				while (rc.next()) {
 					Ref ref = table.resolve(rc.getRef());
-					if (ref != null) {
+					if (ref != null && ref.getObjectId() != null) {
 						all.add(ref);
 					}
 				}
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 9e2fb55..9b82210 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
@@ -620,11 +620,8 @@
 		static void loadRulesFromFile(AttributesNode r, File attrs)
 				throws FileNotFoundException, IOException {
 			if (attrs.exists()) {
-				FileInputStream in = new FileInputStream(attrs);
-				try {
+				try (FileInputStream in = new FileInputStream(attrs)) {
 					r.parse(in);
-				} finally {
-					in.close();
 				}
 			}
 		}
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 a69485a..ad41d98 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
@@ -297,7 +297,7 @@
 		packRefs();
 		// TODO: implement reflog_expire(pm, repo);
 		Collection<PackFile> newPacks = repack();
-		prune(Collections.<ObjectId> emptySet());
+		prune(Collections.emptySet());
 		// TODO: implement rerere_gc(pm);
 		return newPacks;
 	}
@@ -570,7 +570,6 @@
 					} catch (IllegalArgumentException notAnObject) {
 						// ignoring the file that does not represent loose
 						// object
-						continue;
 					}
 				}
 			}
@@ -737,34 +736,31 @@
 		RevObject ro = w.next();
 		while (ro != null) {
 			checkCancelled();
-			if (id2File.remove(ro.getId()) != null)
-				if (id2File.isEmpty())
-					return;
+			if (id2File.remove(ro.getId()) != null && id2File.isEmpty()) {
+				return;
+			}
 			ro = w.next();
 		}
 		ro = w.nextObject();
 		while (ro != null) {
 			checkCancelled();
-			if (id2File.remove(ro.getId()) != null)
-				if (id2File.isEmpty())
-					return;
+			if (id2File.remove(ro.getId()) != null && id2File.isEmpty()) {
+				return;
+			}
 			ro = w.nextObject();
 		}
 	}
 
 	private static boolean equals(Ref r1, Ref r2) {
-		if (r1 == null || r2 == null)
+		if (r1 == null || r2 == null) {
 			return false;
-		if (r1.isSymbolic()) {
-			if (!r2.isSymbolic())
-				return false;
-			return r1.getTarget().getName().equals(r2.getTarget().getName());
-		} else {
-			if (r2.isSymbolic()) {
-				return false;
-			}
-			return Objects.equals(r1.getObjectId(), r2.getObjectId());
 		}
+		if (r1.isSymbolic()) {
+			return r2.isSymbolic() && r1.getTarget().getName()
+					.equals(r2.getTarget().getName());
+		}
+		return !r2.isSymbolic()
+				&& Objects.equals(r1.getObjectId(), r2.getObjectId());
 	}
 
 	/**
@@ -971,11 +967,10 @@
 		List<String> fileNames = null;
 		try (Stream<Path> files = Files.list(packDir)) {
 			fileNames = files.map(path -> path.getFileName().toString())
-					.filter(name -> {
-						return (name.endsWith(PACK_EXT)
-								|| name.endsWith(BITMAP_EXT)
-								|| name.endsWith(INDEX_EXT));
-					}).sorted(Collections.reverseOrder())
+					.filter(name -> (name.endsWith(PACK_EXT)
+							|| name.endsWith(BITMAP_EXT)
+							|| name.endsWith(INDEX_EXT)))
+					.sorted(Collections.reverseOrder())
 					.collect(Collectors.toList());
 		} catch (IOException e1) {
 			// ignore
@@ -991,7 +986,7 @@
 			} else {
 				if (base == null || !n.startsWith(base)) {
 					try {
-						Files.delete(FileUtils.toPath(new File(packDir.toFile(), n)));
+						Files.delete(packDir.resolve(n));
 					} catch (IOException e) {
 						LOG.error(e.getMessage(), e);
 					}
@@ -1036,7 +1031,7 @@
 		List<ReflogEntry> rlEntries = reflogReader
 				.getReverseEntries();
 		if (rlEntries == null || rlEntries.isEmpty())
-			return Collections.<ObjectId> emptySet();
+			return Collections.emptySet();
 		Set<ObjectId> ret = new HashSet<>();
 		for (ReflogEntry e : rlEntries) {
 			if (e.getWho().getWhen().getTime() < minTime)
@@ -1140,23 +1135,21 @@
 			throws IOException {
 		checkCancelled();
 		File tmpPack = null;
-		Map<PackExt, File> tmpExts = new TreeMap<>(
-				new Comparator<PackExt>() {
-					@Override
-					public int compare(PackExt o1, PackExt o2) {
-						// INDEX entries must be returned last, so the pack
-						// scanner does pick up the new pack until all the
-						// PackExt entries have been written.
-						if (o1 == o2)
-							return 0;
-						if (o1 == PackExt.INDEX)
-							return 1;
-						if (o2 == PackExt.INDEX)
-							return -1;
-						return Integer.signum(o1.hashCode() - o2.hashCode());
-					}
-
-				});
+		Map<PackExt, File> tmpExts = new TreeMap<>((o1, o2) -> {
+			// INDEX entries must be returned last, so the pack
+			// scanner does pick up the new pack until all the
+			// PackExt entries have been written.
+			if (o1 == o2) {
+				return 0;
+			}
+			if (o1 == PackExt.INDEX) {
+				return 1;
+			}
+			if (o2 == PackExt.INDEX) {
+				return -1;
+			}
+			return Integer.signum(o1.hashCode() - o2.hashCode());
+		});
 		try (PackWriter pw = new PackWriter(
 				(pconfig == null) ? new PackConfig(repo) : pconfig,
 				repo.newObjectReader())) {
@@ -1188,27 +1181,21 @@
 						JGitText.get().cannotCreateIndexfile, tmpIdx.getPath()));
 
 			// write the packfile
-			FileOutputStream fos = new FileOutputStream(tmpPack);
-			FileChannel channel = fos.getChannel();
-			OutputStream channelStream = Channels.newOutputStream(channel);
-			try {
+			try (FileOutputStream fos = new FileOutputStream(tmpPack);
+					FileChannel channel = fos.getChannel();
+					OutputStream channelStream = Channels
+							.newOutputStream(channel)) {
 				pw.writePack(pm, pm, channelStream);
-			} finally {
 				channel.force(true);
-				channelStream.close();
-				fos.close();
 			}
 
 			// write the packindex
-			fos = new FileOutputStream(tmpIdx);
-			FileChannel idxChannel = fos.getChannel();
-			OutputStream idxStream = Channels.newOutputStream(idxChannel);
-			try {
+			try (FileOutputStream fos = new FileOutputStream(tmpIdx);
+					FileChannel idxChannel = fos.getChannel();
+					OutputStream idxStream = Channels
+							.newOutputStream(idxChannel)) {
 				pw.writeIndex(idxStream);
-			} finally {
 				idxChannel.force(true);
-				idxStream.close();
-				fos.close();
 			}
 
 			if (pw.prepareBitmapIndex(pm)) {
@@ -1220,15 +1207,12 @@
 							JGitText.get().cannotCreateIndexfile,
 							tmpBitmapIdx.getPath()));
 
-				fos = new FileOutputStream(tmpBitmapIdx);
-				idxChannel = fos.getChannel();
-				idxStream = Channels.newOutputStream(idxChannel);
-				try {
+				try (FileOutputStream fos = new FileOutputStream(tmpBitmapIdx);
+						FileChannel idxChannel = fos.getChannel();
+						OutputStream idxStream = Channels
+								.newOutputStream(idxChannel)) {
 					pw.writeBitmapIndex(idxStream);
-				} finally {
 					idxChannel.force(true);
-					idxStream.close();
-					fos.close();
 				}
 			}
 
@@ -1527,8 +1511,8 @@
 	private boolean needGc() {
 		if (tooManyPacks()) {
 			addRepackAllOption();
-		} else if (!tooManyLooseObjects()) {
-			return false;
+		} else {
+			return tooManyLooseObjects();
 		}
 		// TODO run pre-auto-gc hook, if it fails return false
 		return true;
@@ -1569,22 +1553,17 @@
 		int n = 0;
 		int threshold = (auto + 255) / 256;
 		Path dir = repo.getObjectsDirectory().toPath().resolve("17"); //$NON-NLS-1$
-		if (!Files.exists(dir)) {
+		if (!dir.toFile().exists()) {
 			return false;
 		}
-		try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir,
-				new DirectoryStream.Filter<Path>() {
-
-					@Override
-					public boolean accept(Path file) throws IOException {
-						Path fileName = file.getFileName();
-						return Files.isRegularFile(file) && fileName != null
-								&& PATTERN_LOOSE_OBJECT
-										.matcher(fileName.toString()).matches();
-					}
+		try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, file -> {
+					Path fileName = file.getFileName();
+					return file.toFile().isFile() && fileName != null
+							&& PATTERN_LOOSE_OBJECT.matcher(fileName.toString())
+									.matches();
 				})) {
-			for (Iterator<Path> iter = stream.iterator(); iter.hasNext();
-					iter.next()) {
+			for (Iterator<Path> iter = stream.iterator(); iter.hasNext(); iter
+					.next()) {
 				if (++n > threshold) {
 					return true;
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index b382785..9befe2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -232,8 +232,7 @@
 	public void copyCurrentContent() throws IOException {
 		requireLock();
 		try {
-			final FileInputStream fis = new FileInputStream(ref);
-			try {
+			try (FileInputStream fis = new FileInputStream(ref)) {
 				if (fsync) {
 					FileChannel in = fis.getChannel();
 					long pos = 0;
@@ -249,8 +248,6 @@
 					while ((r = fis.read(buf)) >= 0)
 						os.write(buf, 0, r);
 				}
-			} finally {
-				fis.close();
 			}
 		} catch (FileNotFoundException fnfe) {
 			if (ref.exists()) {
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 3e2fd82..92b53ad 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
@@ -796,8 +796,7 @@
 				|| shallowFileSnapshot.isModified(shallowFile)) {
 			shallowCommitsIds = new HashSet<>();
 
-			final BufferedReader reader = open(shallowFile);
-			try {
+			try (BufferedReader reader = open(shallowFile)) {
 				String line;
 				while ((line = reader.readLine()) != null) {
 					try {
@@ -807,8 +806,6 @@
 								.format(JGitText.get().badShallowLine, line));
 					}
 				}
-			} finally {
-				reader.close();
 			}
 
 			shallowFileSnapshot = FileSnapshot.save(shallowFile);
@@ -1027,14 +1024,11 @@
 
 	private AlternateHandle[] loadAlternates() throws IOException {
 		final List<AlternateHandle> l = new ArrayList<>(4);
-		final BufferedReader br = open(alternatesFile);
-		try {
+		try (BufferedReader br = open(alternatesFile)) {
 			String line;
 			while ((line = br.readLine()) != null) {
 				l.add(openAlternate(line));
 			}
-		} finally {
-			br.close();
 		}
 		return l.toArray(new AlternateHandle[l.size()]);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 68c6dbe..106c84b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -67,6 +67,7 @@
 import java.io.InterruptedIOException;
 import java.nio.file.DirectoryNotEmptyException;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.security.DigestInputStream;
 import java.security.MessageDigest;
 import java.text.MessageFormat;
@@ -79,6 +80,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Stream;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
@@ -939,12 +941,27 @@
 		int retries = 0;
 		while (true) {
 			final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile);
-			final BufferedReader br;
 			final MessageDigest digest = Constants.newMessageDigest();
-			try {
-				br = new BufferedReader(new InputStreamReader(
-						new DigestInputStream(new FileInputStream(packedRefsFile),
-								digest), CHARSET));
+			try (BufferedReader br = new BufferedReader(new InputStreamReader(
+					new DigestInputStream(new FileInputStream(packedRefsFile),
+							digest),
+					CHARSET))) {
+				try {
+					return new PackedRefList(parsePackedRefs(br), snapshot,
+							ObjectId.fromRaw(digest.digest()));
+				} catch (IOException e) {
+					if (FileUtils.isStaleFileHandleInCausalChain(e)
+							&& retries < maxStaleRetries) {
+						if (LOG.isDebugEnabled()) {
+							LOG.debug(MessageFormat.format(
+									JGitText.get().packedRefsHandleIsStale,
+									Integer.valueOf(retries)), e);
+						}
+						retries++;
+						continue;
+					}
+					throw e;
+				}
 			} catch (FileNotFoundException noPackedRefs) {
 				if (packedRefsFile.exists()) {
 					throw noPackedRefs;
@@ -952,24 +969,6 @@
 				// Ignore it and leave the new list empty.
 				return NO_PACKED_REFS;
 			}
-			try {
-				return new PackedRefList(parsePackedRefs(br), snapshot,
-						ObjectId.fromRaw(digest.digest()));
-			} catch (IOException e) {
-				if (FileUtils.isStaleFileHandleInCausalChain(e)
-						&& retries < maxStaleRetries) {
-					if (LOG.isDebugEnabled()) {
-						LOG.debug(MessageFormat.format(
-								JGitText.get().packedRefsHandleIsStale,
-								Integer.valueOf(retries)), e);
-					}
-					retries++;
-					continue;
-				}
-				throw e;
-			} finally {
-				br.close();
-			}
 		}
 	}
 
@@ -1221,7 +1220,9 @@
 	}
 
 	private boolean hasLooseRef() throws IOException {
-		return Files.walk(refsDir.toPath()).anyMatch(Files::isRegularFile);
+		try (Stream<Path> stream = Files.walk(refsDir.toPath())) {
+			return stream.anyMatch(Files::isRegularFile);
+		}
 	}
 
 	/** If the parent should fire listeners, fires them. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index 213caca..e71284f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -171,7 +171,10 @@
 		return streamFileThreshold;
 	}
 
-	static WindowCache getInstance() {
+	/**
+	 * @return the cached instance.
+	 */
+	public static WindowCache getInstance() {
 		return cache;
 	}
 
@@ -269,11 +272,17 @@
 			throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit);
 	}
 
-	int getOpenFiles() {
+	/**
+	 * @return the number of open files.
+	 */
+	public int getOpenFiles() {
 		return openFiles.get();
 	}
 
-	long getOpenBytes() {
+	/**
+	 * @return the number of open bytes.
+	 */
+	public long getOpenBytes() {
 		return openBytes.get();
 	}
 
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 71d10ac..42df1a6 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
@@ -81,6 +81,7 @@
 import java.util.zip.DeflaterOutputStream;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.LargeObjectException;
@@ -355,6 +356,24 @@
 	 *            reader to read from the repository with.
 	 */
 	public PackWriter(final PackConfig config, final ObjectReader reader) {
+		this(config, reader, null);
+	}
+
+	/**
+	 * Create writer with a specified configuration.
+	 * <p>
+	 * Objects for packing are specified in {@link #preparePack(Iterator)} or
+	 * {@link #preparePack(ProgressMonitor, Set, Set)}.
+	 *
+	 * @param config
+	 *            configuration for the pack writer.
+	 * @param reader
+	 *            reader to read from the repository with.
+	 * @param statsAccumulator
+	 *            accumulator for statics
+	 */
+	public PackWriter(PackConfig config, final ObjectReader reader,
+			@Nullable PackStatistics.Accumulator statsAccumulator) {
 		this.config = config;
 		this.reader = reader;
 		if (reader instanceof ObjectReuseAsIs)
@@ -365,7 +384,8 @@
 		deltaBaseAsOffset = config.isDeltaBaseAsOffset();
 		reuseDeltas = config.isReuseDeltas();
 		reuseValidate = true; // be paranoid by default
-		stats = new PackStatistics.Accumulator();
+		stats = statsAccumulator != null ? statsAccumulator
+				: new PackStatistics.Accumulator();
 		state = new MutableState();
 		selfRef = new WeakReference<>(this);
 		instances.put(selfRef, Boolean.TRUE);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
index baebde2..ef686a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
@@ -198,8 +198,8 @@
 				ref = t.rc.getRef();
 				updateIndex = t.rc.getUpdateIndex();
 				boolean include = includeDeletes || !t.rc.wasDeleted();
-				skipShadowedRefs(ref.getName());
 				add(t);
+				skipShadowedRefs(ref.getName());
 				if (include) {
 					return true;
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java
index ac1529f..51b5b80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchTrackingStatus.java
@@ -87,22 +87,25 @@
 		if (local == null)
 			return null;
 
-		RevWalk walk = new RevWalk(repository);
+		try (RevWalk walk = new RevWalk(repository)) {
 
-		RevCommit localCommit = walk.parseCommit(local.getObjectId());
-		RevCommit trackingCommit = walk.parseCommit(tracking.getObjectId());
+			RevCommit localCommit = walk.parseCommit(local.getObjectId());
+			RevCommit trackingCommit = walk.parseCommit(tracking.getObjectId());
 
-		walk.setRevFilter(RevFilter.MERGE_BASE);
-		walk.markStart(localCommit);
-		walk.markStart(trackingCommit);
-		RevCommit mergeBase = walk.next();
+			walk.setRevFilter(RevFilter.MERGE_BASE);
+			walk.markStart(localCommit);
+			walk.markStart(trackingCommit);
+			RevCommit mergeBase = walk.next();
 
-		walk.reset();
-		walk.setRevFilter(RevFilter.ALL);
-		int aheadCount = RevWalkUtils.count(walk, localCommit, mergeBase);
-		int behindCount = RevWalkUtils.count(walk, trackingCommit, mergeBase);
+			walk.reset();
+			walk.setRevFilter(RevFilter.ALL);
+			int aheadCount = RevWalkUtils.count(walk, localCommit, mergeBase);
+			int behindCount = RevWalkUtils.count(walk, trackingCommit,
+					mergeBase);
 
-		return new BranchTrackingStatus(trackingBranch, aheadCount, behindCount);
+			return new BranchTrackingStatus(trackingBranch, aheadCount,
+					behindCount);
+		}
 	}
 
 	private final String remoteTrackingBranch;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index a6313f0..4d558c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -1125,7 +1125,7 @@
 				} else
 					e.value = readValue(in);
 
-				if (e.section.equals("include")) { //$NON-NLS-1$
+				if (e.section.equalsIgnoreCase("include")) { //$NON-NLS-1$
 					addIncludedConfig(newEntries, e, depth);
 				}
 			} else
@@ -1154,10 +1154,10 @@
 
 	private void addIncludedConfig(final List<ConfigLine> newEntries,
 			ConfigLine line, int depth) throws ConfigInvalidException {
-		if (!line.name.equals("path") || //$NON-NLS-1$
+		if (!line.name.equalsIgnoreCase("path") || //$NON-NLS-1$
 				line.value == null || line.value.equals(MAGIC_EMPTY_VALUE)) {
-			throw new ConfigInvalidException(
-					JGitText.get().invalidLineInConfigFile);
+			throw new ConfigInvalidException(MessageFormat.format(
+					JGitText.get().invalidLineInConfigFileWithParam, line));
 		}
 		byte[] bytes = readIncludedConfig(line.value);
 		if (bytes == null) {
@@ -1171,7 +1171,12 @@
 		} else {
 			decoded = RawParseUtils.decode(bytes);
 		}
-		newEntries.addAll(fromTextRecurse(decoded, depth + 1));
+		try {
+			newEntries.addAll(fromTextRecurse(decoded, depth + 1));
+		} catch (ConfigInvalidException e) {
+			throw new ConfigInvalidException(MessageFormat
+					.format(JGitText.get().cannotReadFile, line.value), e);
+		}
 	}
 
 	private ConfigSnapshot newState() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 08c883a..5a79035 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -420,4 +420,16 @@
 	 * @since 4.7
 	 */
 	public static final String CONFIG_KEY_RECURSE_SUBMODULES = "recurseSubmodules";
+
+	/**
+	 * The "required" key
+	 * @since 4.11
+	 */
+	public static final String CONFIG_KEY_REQUIRED = "required";
+
+	/**
+	 * The "lfs" section
+	 * @since 4.11
+	 */
+	public static final String CONFIG_SECTION_LFS = "lfs";
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index dad5871..10abd9e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -438,6 +438,13 @@
 	public static final String ATTR_MERGE = "merge"; //$NON-NLS-1$
 
 	/**
+	 * Diff attribute.
+	 *
+	 * @since 4.11
+	 */
+	public static final String ATTR_DIFF = "diff"; //$NON-NLS-1$
+
+	/**
 	 * Binary value for custom merger.
 	 *
 	 * @since 4.9
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSerializer.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSerializer.java
new file mode 100644
index 0000000..4f8bd32
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSerializer.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2009, The Android Open Source Project
+ * Copyright (C) 2009, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2018, David Pursehouse <david.pursehouse@gmail.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.lib;
+
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * Helper to serialize {@link ObjectId} instances. {@link ObjectId} is already
+ * serializable, but this class provides methods to handle null and non-null
+ * instances.
+ *
+ * @since 4.11
+ */
+public class ObjectIdSerializer {
+	/*
+	 * Marker to indicate a null ObjectId instance.
+	 */
+	private static final byte NULL_MARKER = 0;
+
+	/*
+	 * Marker to indicate a non-null ObjectId instance.
+	 */
+	private static final byte NON_NULL_MARKER = 1;
+
+	/**
+	 * Write a possibly null {@link ObjectId} to the stream, using markers to
+	 * differentiate null and non-null instances.
+	 *
+	 * <p>
+	 * If the id is non-null, writes a {@link #NON_NULL_MARKER} followed by the
+	 * id's words. If it is null, writes a {@link #NULL_MARKER} and nothing
+	 * else.
+	 *
+	 * @param out
+	 *            the output stream
+	 * @param id
+	 *            the object id to serialize; may be null
+	 * @throws IOException
+	 *             the stream writing failed
+	 */
+	public static void write(OutputStream out, @Nullable AnyObjectId id)
+			throws IOException {
+		if (id != null) {
+			out.write(NON_NULL_MARKER);
+			writeWithoutMarker(out, id);
+		} else {
+			out.write(NULL_MARKER);
+		}
+	}
+
+	/**
+	 * Write a non-null {@link ObjectId} to the stream.
+	 *
+	 * @param out
+	 *            the output stream
+	 * @param id
+	 *            the object id to serialize; never null
+	 * @throws IOException
+	 *             the stream writing failed
+	 * @since 4.11
+	 */
+	public static void writeWithoutMarker(OutputStream out, @NonNull AnyObjectId id)
+			throws IOException {
+		id.copyRawTo(out);
+	}
+
+	/**
+	 * Read a possibly null {@link ObjectId} from the stream.
+	 *
+	 * Reads the first byte of the stream, which is expected to be either
+	 * {@link #NON_NULL_MARKER} or {@link #NULL_MARKER}.
+	 *
+	 * @param in
+	 *            the input stream
+	 * @return the object id, or null
+	 * @throws IOException
+	 *             there was an error reading the stream
+	 */
+	@Nullable
+	public static ObjectId read(InputStream in) throws IOException {
+		byte marker = (byte) in.read();
+		switch (marker) {
+		case NULL_MARKER:
+			return null;
+		case NON_NULL_MARKER:
+			return readWithoutMarker(in);
+		default:
+			throw new IOException("Invalid flag before ObjectId: " + marker); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Read a non-null {@link ObjectId} from the stream.
+	 *
+	 * @param in
+	 *            the input stream
+	 * @return the object id; never null
+	 * @throws IOException
+	 *             there was an error reading the stream
+	 * @since 4.11
+	 */
+	@NonNull
+	public static ObjectId readWithoutMarker(InputStream in) throws IOException {
+		final byte[] b = new byte[OBJECT_ID_LENGTH];
+		IO.readFully(in, b, 0, OBJECT_ID_LENGTH);
+		return ObjectId.fromRaw(b);
+	}
+
+	private ObjectIdSerializer() {
+	}
+}
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 fae7f1c..db5cad0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -1106,7 +1106,8 @@
 	}
 
 	/**
-	 * Get mutable map of all known refs
+	 * Get mutable map of all known refs, including symrefs like HEAD that may
+	 * not point to any object yet.
 	 *
 	 * @return mutable map of all known refs (heads, tags, remotes).
 	 */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
index 90ef5c7..32ef504 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
@@ -180,8 +180,8 @@
 	 */
 	public byte[] build() {
 		ByteArrayOutputStream os = new ByteArrayOutputStream();
-		OutputStreamWriter w = new OutputStreamWriter(os, Constants.CHARSET);
-		try {
+		try (OutputStreamWriter w = new OutputStreamWriter(os,
+				Constants.CHARSET)) {
 			w.write("object "); //$NON-NLS-1$
 			getObjectId().copyTo(w);
 			w.write('\n');
@@ -203,7 +203,6 @@
 			w.write('\n');
 			if (getMessage() != null)
 				w.write(getMessage());
-			w.close();
 		} catch (IOException err) {
 			// This should never occur, the only way to get it above is
 			// for the ByteArrayOutputStream to throw, but it doesn't.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
old mode 100644
new mode 100755
index 6d43f09..6f7a702
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -3,6 +3,7 @@
  * Copyright (C) 2010-2012, Matthias Sohn <matthias.sohn@sap.com>
  * Copyright (C) 2012, Research In Motion Limited
  * Copyright (C) 2017, Obeo (mathieu.cartaud@obeo.fr)
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -53,7 +54,6 @@
 
 import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -87,22 +87,29 @@
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.submodule.SubmoduleConflict;
 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
 import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.WorkingTreeOptions;
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.LfsFactory;
+import org.eclipse.jgit.util.LfsFactory.LfsInputStream;
 import org.eclipse.jgit.util.TemporaryBuffer;
+import org.eclipse.jgit.util.io.EolStreamTypeUtil;
 
 /**
  * A three-way merger performing a content-merge if necessary
@@ -277,8 +284,16 @@
 	protected MergeAlgorithm mergeAlgorithm;
 
 	/**
-	 * The size limit (bytes) which controls a file to be stored in {@code Heap} or
-	 * {@code LocalFile} during the merge.
+	 * The {@link WorkingTreeOptions} are needed to determine line endings for
+	 * merged files.
+	 *
+	 * @since 4.11
+	 */
+	protected WorkingTreeOptions workingTreeOptions;
+
+	/**
+	 * The size limit (bytes) which controls a file to be stored in {@code Heap}
+	 * or {@code LocalFile} during the merge.
 	 */
 	private int inCoreLimit;
 
@@ -319,6 +334,7 @@
 			dircache = DirCache.newInCore();
 		} else {
 			implicitDirCache = true;
+			workingTreeOptions = local.getConfig().get(WorkingTreeOptions.KEY);
 		}
 	}
 
@@ -380,8 +396,13 @@
 		}
 		for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
 				.entrySet()) {
-			DirCacheCheckout.checkoutEntry(db, entry.getValue(), reader);
-			modifiedFiles.add(entry.getKey());
+			DirCacheEntry cacheEntry = entry.getValue();
+			if (cacheEntry.getFileMode() == FileMode.GITLINK) {
+				new File(nonNullRepo().getWorkTree(), entry.getKey()).mkdirs();
+			} else {
+				DirCacheCheckout.checkoutEntry(db, cacheEntry, reader);
+				modifiedFiles.add(entry.getKey());
+			}
 		}
 	}
 
@@ -712,24 +733,50 @@
 
 		if (nonTree(modeO) && nonTree(modeT)) {
 			// Check worktree before modifying files
-			if (isWorktreeDirty(work, ourDce))
+			boolean worktreeDirty = isWorktreeDirty(work, ourDce);
+			if (!attributes.canBeContentMerged() && worktreeDirty) {
 				return false;
+			}
 
+			boolean gitlinkConflict = isGitLink(modeO) || isGitLink(modeT);
 			// Don't attempt to resolve submodule link conflicts
-			if (isGitLink(modeO) || isGitLink(modeT)
-					|| !attributes.canBeContentMerged()) {
+			if (gitlinkConflict || !attributes.canBeContentMerged()) {
 				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
 				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
 				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
-				unmergedPaths.add(tw.getPathString());
+
+				if (gitlinkConflict) {
+					MergeResult<SubmoduleConflict> result = new MergeResult<>(
+							Arrays.asList(
+									new SubmoduleConflict(base == null ? null
+											: base.getEntryObjectId()),
+									new SubmoduleConflict(ours == null ? null
+											: ours.getEntryObjectId()),
+									new SubmoduleConflict(theirs == null ? null
+											: theirs.getEntryObjectId())));
+					result.setContainsConflicts(true);
+					mergeResults.put(tw.getPathString(), result);
+					if (!ignoreConflicts) {
+						unmergedPaths.add(tw.getPathString());
+					}
+				} else {
+					// attribute merge issues are conflicts but not failures
+					unmergedPaths.add(tw.getPathString());
+				}
 				return true;
 			}
 
-			MergeResult<RawText> result = contentMerge(base, ours, theirs);
+			// Check worktree before modifying files
+			if (worktreeDirty) {
+				return false;
+			}
+
+			MergeResult<RawText> result = contentMerge(base, ours, theirs,
+					attributes);
 			if (ignoreConflicts) {
 				result.setContainsConflicts(false);
 			}
-			updateIndex(base, ours, theirs, result);
+			updateIndex(base, ours, theirs, result, attributes);
 			if (result.containsConflicts() && !ignoreConflicts)
 				unmergedPaths.add(tw.getPathString());
 			modifiedFiles.add(tw.getPathString());
@@ -737,7 +784,8 @@
 			// OURS or THEIRS has been deleted
 			if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
 					.idEqual(T_BASE, T_THEIRS)))) {
-				MergeResult<RawText> result = contentMerge(base, ours, theirs);
+				MergeResult<RawText> result = contentMerge(base, ours, theirs,
+						attributes);
 
 				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
 				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
@@ -772,12 +820,14 @@
 	 * @param base
 	 * @param ours
 	 * @param theirs
+	 * @param attributes
 	 *
 	 * @return the result of the content merge
 	 * @throws IOException
 	 */
 	private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
-			CanonicalTreeParser ours, CanonicalTreeParser theirs)
+			CanonicalTreeParser ours, CanonicalTreeParser theirs,
+			Attributes attributes)
 			throws IOException {
 		RawText baseText;
 		RawText ourText;
@@ -785,11 +835,11 @@
 
 		try {
 			baseText = base == null ? RawText.EMPTY_TEXT : getRawText(
-				base.getEntryObjectId(), reader);
+							base.getEntryObjectId(), attributes);
 			ourText = ours == null ? RawText.EMPTY_TEXT : getRawText(
-				ours.getEntryObjectId(), reader);
+							ours.getEntryObjectId(), attributes);
 			theirsText = theirs == null ? RawText.EMPTY_TEXT : getRawText(
-				theirs.getEntryObjectId(), reader);
+							theirs.getEntryObjectId(), attributes);
 		} catch (BinaryBlobException e) {
 			MergeResult<RawText> r = new MergeResult<>(Collections.<RawText>emptyList());
 			r.setContainsConflicts(true);
@@ -853,80 +903,88 @@
 	 * @param ours
 	 * @param theirs
 	 * @param result
+	 * @param attributes
 	 * @throws FileNotFoundException
 	 * @throws IOException
 	 */
 	private void updateIndex(CanonicalTreeParser base,
 			CanonicalTreeParser ours, CanonicalTreeParser theirs,
-			MergeResult<RawText> result) throws FileNotFoundException,
+			MergeResult<RawText> result, Attributes attributes)
+			throws FileNotFoundException,
 			IOException {
-		File mergedFile = !inCore ? writeMergedFile(result) : null;
-
-		if (result.containsConflicts()) {
-			// A conflict occurred, the file will contain conflict markers
-			// the index will be populated with the three stages and the
-			// workdir (if used) contains the halfway merged content.
-			add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
-			add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
-			add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
-			mergeResults.put(tw.getPathString(), result);
-			return;
-		}
-
-		// No conflict occurred, the file will contain fully merged content.
-		// The index will be populated with the new merged version.
-		DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
-
-		// Set the mode for the new content. Fall back to REGULAR_FILE if
-		// we can't merge modes of OURS and THEIRS.
-		int newMode = mergeFileModes(
-				tw.getRawMode(0),
-				tw.getRawMode(1),
-				tw.getRawMode(2));
-		dce.setFileMode(newMode == FileMode.MISSING.getBits()
-				? FileMode.REGULAR_FILE
-				: FileMode.fromBits(newMode));
-		if (mergedFile != null) {
-			long len = mergedFile.length();
-			dce.setLastModified(FS.DETECTED.lastModified(mergedFile));
-			dce.setLength((int) len);
-			InputStream is = new FileInputStream(mergedFile);
-			try {
-				dce.setObjectId(getObjectInserter().insert(OBJ_BLOB, len, is));
-			} finally {
-				is.close();
+		TemporaryBuffer rawMerged = null;
+		try {
+			rawMerged = doMerge(result);
+			File mergedFile = inCore ? null
+					: writeMergedFile(rawMerged, attributes);
+			if (result.containsConflicts()) {
+				// A conflict occurred, the file will contain conflict markers
+				// the index will be populated with the three stages and the
+				// workdir (if used) contains the halfway merged content.
+				add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
+				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
+				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
+				mergeResults.put(tw.getPathString(), result);
+				return;
 			}
-		} else
-			dce.setObjectId(insertMergeResult(result));
-		builder.add(dce);
+
+			// No conflict occurred, the file will contain fully merged content.
+			// The index will be populated with the new merged version.
+			DirCacheEntry dce = new DirCacheEntry(tw.getPathString());
+
+			// Set the mode for the new content. Fall back to REGULAR_FILE if
+			// we can't merge modes of OURS and THEIRS.
+			int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1),
+					tw.getRawMode(2));
+			dce.setFileMode(newMode == FileMode.MISSING.getBits()
+					? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
+			if (mergedFile != null) {
+				dce.setLastModified(
+						nonNullRepo().getFS().lastModified(mergedFile));
+				dce.setLength((int) mergedFile.length());
+			}
+			dce.setObjectId(insertMergeResult(rawMerged, attributes));
+			builder.add(dce);
+		} finally {
+			if (rawMerged != null) {
+				rawMerged.destroy();
+			}
+		}
 	}
 
 	/**
 	 * Writes merged file content to the working tree.
 	 *
-	 * @param result
-	 *            the result of the content merge
+	 * @param rawMerged
+	 *            the raw merged content
+	 * @param attributes
+	 *            the files .gitattributes entries
 	 * @return the working tree file to which the merged content was written.
 	 * @throws FileNotFoundException
 	 * @throws IOException
 	 */
-	private File writeMergedFile(MergeResult<RawText> result)
+	private File writeMergedFile(TemporaryBuffer rawMerged,
+			Attributes attributes)
 			throws FileNotFoundException, IOException {
 		File workTree = nonNullRepo().getWorkTree();
 		FS fs = nonNullRepo().getFS();
 		File of = new File(workTree, tw.getPathString());
 		File parentFolder = of.getParentFile();
-		if (!fs.exists(parentFolder))
+		if (!fs.exists(parentFolder)) {
 			parentFolder.mkdirs();
-		try (OutputStream os = new BufferedOutputStream(
-				new FileOutputStream(of))) {
-			new MergeFormatter().formatMerge(os, result,
-					Arrays.asList(commitNames), CHARACTER_ENCODING);
+		}
+		EolStreamType streamType = EolStreamTypeUtil.detectStreamType(
+				OperationType.CHECKOUT_OP, workingTreeOptions,
+				attributes);
+		try (OutputStream os = EolStreamTypeUtil.wrapOutputStream(
+				new BufferedOutputStream(new FileOutputStream(of)),
+				streamType)) {
+			rawMerged.writeTo(os, null);
 		}
 		return of;
 	}
 
-	private ObjectId insertMergeResult(MergeResult<RawText> result)
+	private TemporaryBuffer doMerge(MergeResult<RawText> result)
 			throws IOException {
 		TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
 				db != null ? nonNullRepo().getDirectory() : null, inCoreLimit);
@@ -934,11 +992,20 @@
 			new MergeFormatter().formatMerge(buf, result,
 					Arrays.asList(commitNames), CHARACTER_ENCODING);
 			buf.close();
-			try (InputStream in = buf.openInputStream()) {
-				return getObjectInserter().insert(OBJ_BLOB, buf.length(), in);
-			}
-		} finally {
+		} catch (IOException e) {
 			buf.destroy();
+			throw e;
+		}
+		return buf;
+	}
+
+	private ObjectId insertMergeResult(TemporaryBuffer buf,
+			Attributes attributes) throws IOException {
+		InputStream in = buf.openInputStream();
+		try (LfsInputStream is = LfsFactory.getInstance().applyCleanFilter(
+				getRepository(), in,
+				buf.length(), attributes.get(Constants.ATTR_MERGE))) {
+			return getObjectInserter().insert(OBJ_BLOB, is.getLength(), is);
 		}
 	}
 
@@ -970,12 +1037,15 @@
 		return FileMode.MISSING.getBits();
 	}
 
-	private static RawText getRawText(ObjectId id, ObjectReader reader)
+	private RawText getRawText(ObjectId id,
+			Attributes attributes)
 			throws IOException, BinaryBlobException {
 		if (id.equals(ObjectId.zeroId()))
 			return new RawText(new byte[] {});
 
-		ObjectLoader loader = reader.open(id, OBJ_BLOB);
+		ObjectLoader loader = LfsFactory.getInstance().applySmudgeFilter(
+				getRepository(), reader.open(id, OBJ_BLOB),
+				attributes.get(Constants.ATTR_MERGE));
 		int threshold = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
 		return RawText.load(loader, threshold);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java
index 87bd4b5..54a2d89 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java
@@ -82,14 +82,11 @@
 
 		ObjectLoader lo = reader.open(ours.getData());
 		ObjectLoader lt = reader.open(theirs.getData());
-		UnionInputStream union = new UnionInputStream(lo.openStream(),
-				lt.openStream());
-		try {
+		try (UnionInputStream union = new UnionInputStream(lo.openStream(),
+				lt.openStream())) {
 			ObjectId noteData = inserter.insert(Constants.OBJ_BLOB,
 					lo.getSize() + lt.getSize(), union);
 			return new Note(ours, noteData);
-		} finally {
-			union.close();
 		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java
index 05fab92..da123a7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java
@@ -152,10 +152,10 @@
 	}
 
 	private static byte[] readFully(final InputStream is) throws IOException {
-		TemporaryBuffer b = new TemporaryBuffer.Heap(Integer.MAX_VALUE);
-		b.copy(is);
-		b.close();
-		return b.toByteArray();
+		try (TemporaryBuffer b = new TemporaryBuffer.Heap(Integer.MAX_VALUE)) {
+			b.copy(is);
+			return b.toByteArray();
+		}
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 5568241..91c15a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -53,6 +53,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.LargeObjectException;
@@ -513,6 +514,7 @@
 	 *
 	 * @return the current filter. Never null as a filter is always needed.
 	 */
+	@NonNull
 	public RevFilter getRevFilter() {
 		return filter;
 	}
@@ -552,6 +554,7 @@
 	 *         {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} is
 	 *         returned.
 	 */
+	@NonNull
 	public TreeFilter getTreeFilter() {
 		return treeFilter;
 	}
@@ -649,6 +652,7 @@
 	 *            name of the blob object.
 	 * @return reference to the blob object. Never null.
 	 */
+	@NonNull
 	public RevBlob lookupBlob(final AnyObjectId id) {
 		RevBlob c = (RevBlob) objects.get(id);
 		if (c == null) {
@@ -668,6 +672,7 @@
 	 *            name of the tree object.
 	 * @return reference to the tree object. Never null.
 	 */
+	@NonNull
 	public RevTree lookupTree(final AnyObjectId id) {
 		RevTree c = (RevTree) objects.get(id);
 		if (c == null) {
@@ -690,6 +695,7 @@
 	 *            name of the commit object.
 	 * @return reference to the commit object. Never null.
 	 */
+	@NonNull
 	public RevCommit lookupCommit(final AnyObjectId id) {
 		RevCommit c = (RevCommit) objects.get(id);
 		if (c == null) {
@@ -709,6 +715,7 @@
 	 *            name of the tag object.
 	 * @return reference to the tag object. Never null.
 	 */
+	@NonNull
 	public RevTag lookupTag(final AnyObjectId id) {
 		RevTag c = (RevTag) objects.get(id);
 		if (c == null) {
@@ -730,6 +737,7 @@
 	 *            type of the object. Must be a valid Git object type.
 	 * @return reference to the object. Never null.
 	 */
+	@NonNull
 	public RevObject lookupAny(final AnyObjectId id, final int type) {
 		RevObject r = objects.get(id);
 		if (r == null) {
@@ -784,6 +792,7 @@
 	 * @throws java.io.IOException
 	 *             a pack file or loose object could not be read.
 	 */
+	@NonNull
 	public RevCommit parseCommit(final AnyObjectId id)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
@@ -811,6 +820,7 @@
 	 * @throws java.io.IOException
 	 *             a pack file or loose object could not be read.
 	 */
+	@NonNull
 	public RevTree parseTree(final AnyObjectId id)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
@@ -845,6 +855,7 @@
 	 * @throws java.io.IOException
 	 *             a pack file or loose object could not be read.
 	 */
+	@NonNull
 	public RevTag parseTag(final AnyObjectId id) throws MissingObjectException,
 			IncorrectObjectTypeException, IOException {
 		RevObject c = parseAny(id);
@@ -870,6 +881,7 @@
 	 * @throws java.io.IOException
 	 *             a pack file or loose object could not be read.
 	 */
+	@NonNull
 	public RevObject parseAny(final AnyObjectId id)
 			throws MissingObjectException, IOException {
 		RevObject r = objects.get(id);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
index 3ab96ca..f82301a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RewriteGenerator.java
@@ -93,27 +93,25 @@
 	@Override
 	RevCommit next() throws MissingObjectException,
 			IncorrectObjectTypeException, IOException {
-		for (;;) {
-			final RevCommit c = source.next();
-			if (c == null)
-				return null;
-
-			boolean rewrote = false;
-			final RevCommit[] pList = c.parents;
-			final int nParents = pList.length;
-			for (int i = 0; i < nParents; i++) {
-				final RevCommit oldp = pList[i];
-				final RevCommit newp = rewrite(oldp);
-				if (oldp != newp) {
-					pList[i] = newp;
-					rewrote = true;
-				}
-			}
-			if (rewrote)
-				c.parents = cleanup(pList);
-
-			return c;
+		final RevCommit c = source.next();
+		if (c == null) {
+			return null;
 		}
+		boolean rewrote = false;
+		final RevCommit[] pList = c.parents;
+		final int nParents = pList.length;
+		for (int i = 0; i < nParents; i++) {
+			final RevCommit oldp = pList[i];
+			final RevCommit newp = rewrite(oldp);
+			if (oldp != newp) {
+				pList[i] = newp;
+				rewrote = true;
+			}
+		}
+		if (rewrote) {
+			c.parents = cleanup(pList);
+		}
+		return c;
 	}
 
 	private RevCommit rewrite(RevCommit p) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
new file mode 100644
index 0000000..3570733
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018, David Pursehouse <david.pursehouse@gmail.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.storage.file;
+
+import org.eclipse.jgit.internal.storage.file.WindowCache;
+
+/**
+ * Accessor for stats about {@link WindowCache}.
+ *
+ * @since 4.11
+ *
+ */
+public class WindowCacheStats {
+	/**
+	 * @return the number of open files.
+	 */
+	public static int getOpenFiles() {
+		return WindowCache.getInstance().getOpenFiles();
+	}
+
+	/**
+	 * @return the number of open bytes.
+	 */
+	public static long getOpenBytes() {
+		return WindowCache.getInstance().getOpenBytes();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
index 68cffd5..68878e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
@@ -166,6 +166,36 @@
 	 * POJO for accumulating the statistics.
 	 */
 	public static class Accumulator {
+		/**
+		 * The count of references in the ref advertisement.
+		 *
+		 * @since 4.11
+		 */
+		public long advertised;
+
+		/**
+		 * The count of client wants.
+		 *
+		 * @since 4.11
+		 */
+		public long wants;
+
+		/**
+		 * The count of client haves.
+		 *
+		 * @since 4.11
+		 */
+		public long haves;
+
+		/**
+		 * Time in ms spent in the negotiation phase. For non-bidirectional
+		 * transports (e.g., HTTP), this is only for the final request that
+		 * sends back the pack file.
+		 *
+		 * @since 4.11
+		 */
+		public long timeNegotiating;
+
 		/** The set of objects to be included in the pack. */
 		public Set<ObjectId> interestingObjects;
 
@@ -271,6 +301,48 @@
 	}
 
 	/**
+	 * Get the count of references in the ref advertisement.
+	 *
+	 * @return count of refs in the ref advertisement.
+	 * @since 4.11
+	 */
+	public long getAdvertised() {
+		return statistics.advertised;
+	}
+
+	/**
+	 * Get the count of client wants.
+	 *
+	 * @return count of client wants.
+	 * @since 4.11
+	 */
+	public long getWants() {
+		return statistics.wants;
+	}
+
+	/**
+	 * Get the count of client haves.
+	 *
+	 * @return count of client haves.
+	 * @since 4.11
+	 */
+	public long getHaves() {
+		return statistics.haves;
+	}
+
+	/**
+	 * Time in ms spent in the negotiation phase. For non-bidirectional
+	 * transports (e.g., HTTP), this is only for the final request that sends
+	 * back the pack file.
+	 *
+	 * @return time for ref advertisement in ms.
+	 * @since 4.11
+	 */
+	public long getTimeNegotiating() {
+		return statistics.timeNegotiating;
+	}
+
+	/**
 	 * Get unmodifiable collection of objects to be included in the pack.
 	 *
 	 * @return unmodifiable collection of objects to be included in the pack.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleConflict.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleConflict.java
new file mode 100644
index 0000000..856eb72
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleConflict.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017, Two Sigma Open Source
+ * 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.submodule;
+
+import org.eclipse.jgit.diff.Sequence;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Merges expect that conflicts will consist of Sequences, but that doesn't
+ * really make sense for submodules. So this represents a submodule conflict.
+ *
+ * @since 4.11
+ */
+public class SubmoduleConflict extends Sequence {
+    private final ObjectId objectId;
+
+    /**
+     * Create a SubmoduleConflict for the given submodule object id
+     * @param objectId
+     */
+    public SubmoduleConflict(ObjectId objectId) {
+        super();
+        this.objectId = objectId;
+    }
+
+    @Override
+    public int size() {
+        return 1;
+    }
+
+    /**
+     * @return the object id for the conflicting submodule
+     */
+    public ObjectId getObjectId() {
+        return objectId;
+    }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index fee71eb..d7c5b9d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -402,9 +402,9 @@
 			// We have to copy to produce the cipher text anyway so use
 			// the large object code path as it supports that behavior.
 			//
-			final OutputStream os = beginPut(bucket, key, null, null);
-			os.write(data);
-			os.close();
+			try (OutputStream os = beginPut(bucket, key, null, null)) {
+				os.write(data);
+			}
 			return;
 		}
 
@@ -418,11 +418,8 @@
 			authorize(c);
 			c.setDoOutput(true);
 			c.setFixedLengthStreamingMode(data.length);
-			final OutputStream os = c.getOutputStream();
-			try {
+			try (OutputStream os = c.getOutputStream()) {
 				os.write(data);
-			} finally {
-				os.close();
 			}
 
 			switch (HttpSupport.response(c)) {
@@ -503,12 +500,10 @@
 			authorize(c);
 			c.setDoOutput(true);
 			monitor.beginTask(monitorTask, (int) (len / 1024));
-			final OutputStream os = c.getOutputStream();
-			try {
+			try (OutputStream os = c.getOutputStream()) {
 				buf.writeTo(os, monitor);
 			} finally {
 				monitor.endTask();
-				os.close();
 			}
 
 			switch (HttpSupport.response(c)) {
@@ -655,11 +650,8 @@
 	static Properties properties(final File authFile)
 			throws FileNotFoundException, IOException {
 		final Properties p = new Properties();
-		final FileInputStream in = new FileInputStream(authFile);
-		try {
+		try (FileInputStream in = new FileInputStream(authFile)) {
 			p.load(in);
-		} finally {
-			in.close();
 		}
 		return p;
 	}
@@ -702,16 +694,13 @@
 						throw new IOException(JGitText.get().noXMLParserAvailable);
 					}
 					xr.setContentHandler(this);
-					final InputStream in = c.getInputStream();
-					try {
+					try (InputStream in = c.getInputStream()) {
 						xr.parse(new InputSource(in));
 					} catch (SAXException parsingError) {
 						throw new IOException(
 								MessageFormat.format(
 										JGitText.get().errorListing, prefix),
 								parsingError);
-					} finally {
-						in.close();
 					}
 					return;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 19d8abe..1383045 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -54,6 +54,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -230,6 +231,8 @@
 
 	private boolean noProgress;
 
+	private Set<AnyObjectId> minimalNegotiationSet;
+
 	private String lockMessage;
 
 	private PackLock packLock;
@@ -249,8 +252,11 @@
 		super(packTransport);
 
 		if (local != null) {
-			final FetchConfig cfg = local.getConfig().get(FetchConfig::new);
+			final FetchConfig cfg = getFetchConfig();
 			allowOfsDelta = cfg.allowOfsDelta;
+			if (cfg.minimalNegotiation) {
+				minimalNegotiationSet = new HashSet<>();
+			}
 		} else {
 			allowOfsDelta = true;
 		}
@@ -277,11 +283,20 @@
 		}
 	}
 
-	private static class FetchConfig {
+	static class FetchConfig {
 		final boolean allowOfsDelta;
 
+		final boolean minimalNegotiation;
+
 		FetchConfig(final Config c) {
 			allowOfsDelta = c.getBoolean("repack", "usedeltabaseoffset", true); //$NON-NLS-1$ //$NON-NLS-2$
+			minimalNegotiation = c.getBoolean("fetch", "useminimalnegotiation", //$NON-NLS-1$ //$NON-NLS-2$
+					false);
+		}
+
+		FetchConfig(boolean allowOfsDelta, boolean minimalNegotiation) {
+			this.allowOfsDelta = allowOfsDelta;
+			this.minimalNegotiation = minimalNegotiation;
 		}
 	}
 
@@ -391,6 +406,10 @@
 		super.close();
 	}
 
+	FetchConfig getFetchConfig() {
+		return local.getConfig().get(FetchConfig::new);
+	}
+
 	private int maxTimeWanted(final Collection<Ref> wants) {
 		int maxTime = 0;
 		for (final Ref r : wants) {
@@ -492,9 +511,19 @@
 			}
 			line.append('\n');
 			p.writeString(line.toString());
+			if (minimalNegotiationSet != null) {
+				Ref current = local.exactRef(r.getName());
+				if (current != null) {
+					ObjectId o = current.getObjectId();
+					if (o != null && !o.equals(ObjectId.zeroId())) {
+						minimalNegotiationSet.add(o);
+					}
+				}
+			}
 		}
-		if (first)
+		if (first) {
 			return false;
+		}
 		p.end();
 		outNeedsEnd = false;
 		return true;
@@ -549,18 +578,24 @@
 		boolean receivedAck = false;
 		boolean receivedReady = false;
 
-		if (statelessRPC)
+		if (statelessRPC) {
 			state.writeTo(out, null);
+		}
 
 		negotiateBegin();
 		SEND_HAVES: for (;;) {
 			final RevCommit c = walk.next();
-			if (c == null)
+			if (c == null) {
 				break SEND_HAVES;
+			}
 
-			pckOut.writeString("have " + c.getId().name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			ObjectId o = c.getId();
+			pckOut.writeString("have " + o.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
 			havesSent++;
 			havesSinceLastContinue++;
+			if (minimalNegotiationSet != null) {
+				minimalNegotiationSet.remove(o);
+			}
 
 			if ((31 & havesSent) != 0) {
 				// We group the have lines into blocks of 32, each marked
@@ -570,8 +605,9 @@
 				continue;
 			}
 
-			if (monitor.isCancelled())
+			if (monitor.isCancelled()) {
 				throw new CancelledException();
+			}
 
 			pckOut.end();
 			resultsPending++; // Each end will cause a result to come back.
@@ -593,6 +629,16 @@
 					// pack on the remote side. Keep doing that.
 					//
 					resultsPending--;
+					if (minimalNegotiationSet != null
+							&& minimalNegotiationSet.isEmpty()) {
+						// Minimal negotiation was requested and we sent out our
+						// current reference values for our wants, so terminate
+						// negotiation early.
+						if (statelessRPC) {
+							state.writeTo(out, null);
+						}
+						break SEND_HAVES;
+					}
 					break READ_RESULT;
 
 				case ACK:
@@ -603,8 +649,9 @@
 					multiAck = MultiAck.OFF;
 					resultsPending = 0;
 					receivedAck = true;
-					if (statelessRPC)
+					if (statelessRPC) {
 						state.writeTo(out, null);
+					}
 					break SEND_HAVES;
 
 				case ACK_CONTINUE:
@@ -619,19 +666,31 @@
 					receivedAck = true;
 					receivedContinue = true;
 					havesSinceLastContinue = 0;
-					if (anr == AckNackResult.ACK_READY)
+					if (anr == AckNackResult.ACK_READY) {
 						receivedReady = true;
+					}
+					if (minimalNegotiationSet != null && minimalNegotiationSet.isEmpty()) {
+						// Minimal negotiation was requested and we sent out our current reference
+						// values for our wants, so terminate negotiation early.
+						if (statelessRPC) {
+							state.writeTo(out, null);
+						}
+						break SEND_HAVES;
+					}
 					break;
 				}
 
-				if (monitor.isCancelled())
+				if (monitor.isCancelled()) {
 					throw new CancelledException();
+				}
 			}
 
-			if (noDone & receivedReady)
+			if (noDone & receivedReady) {
 				break SEND_HAVES;
-			if (statelessRPC)
+			}
+			if (statelessRPC) {
 				state.writeTo(out, null);
+			}
 
 			if (receivedContinue && havesSinceLastContinue > MAX_HAVES) {
 				// Our history must be really different from the remote's.
@@ -645,8 +704,9 @@
 
 		// Tell the remote side we have run out of things to talk about.
 		//
-		if (monitor.isCancelled())
+		if (monitor.isCancelled()) {
 			throw new CancelledException();
+		}
 
 		if (!receivedReady || !noDone) {
 			// When statelessRPC is true we should always leave SEND_HAVES
@@ -691,8 +751,9 @@
 				break;
 			}
 
-			if (monitor.isCancelled())
+			if (monitor.isCancelled()) {
 				throw new CancelledException();
+			}
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java
index e0ec775..712eb22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java
@@ -150,23 +150,15 @@
 			throws IOException, ServiceNotEnabledException,
 			ServiceNotAuthorizedException {
 		final String name = commandLine.substring(command.length() + 1);
-		Repository db;
-		try {
-			db = client.getDaemon().openRepository(client, name);
+		try (Repository db = client.getDaemon().openRepository(client, name)) {
+			if (isEnabledFor(db)) {
+				execute(client, db);
+			}
 		} catch (ServiceMayNotContinueException e) {
 			// An error when opening the repo means the client is expecting a ref
 			// advertisement, so use that style of error.
 			PacketLineOut pktOut = new PacketLineOut(client.getOutputStream());
 			pktOut.writeString("ERR " + e.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
-			db = null;
-		}
-		if (db == null)
-			return;
-		try {
-			if (isEnabledFor(db))
-				execute(client, db);
-		} finally {
-			db.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index ed10f44..117e844 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -336,14 +336,12 @@
 		final LockFile lock = new LockFile(new File(meta, "FETCH_HEAD")); //$NON-NLS-1$
 		try {
 			if (lock.lock()) {
-				final Writer w = new OutputStreamWriter(lock.getOutputStream());
-				try {
+				try (Writer w = new OutputStreamWriter(
+						lock.getOutputStream())) {
 					for (final FetchHeadRecord h : fetchHeadUpdates) {
 						h.write(w);
 						result.add(h);
 					}
-				} finally {
-					w.close();
 				}
 				lock.commit();
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
index c381827..2a222fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
@@ -362,13 +362,8 @@
 		if (home == null)
 			return;
 		final File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); //$NON-NLS-1$ //$NON-NLS-2$
-		try {
-			final FileInputStream in = new FileInputStream(known_hosts);
-			try {
-				sch.setKnownHosts(in);
-			} finally {
-				in.close();
-			}
+		try (FileInputStream in = new FileInputStream(known_hosts)) {
+			sch.setKnownHosts(in);
 		} catch (FileNotFoundException none) {
 			// Oh well. They don't have a known hosts in home.
 		} catch (IOException err) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
index 25486cb..15338a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
@@ -76,8 +76,9 @@
  * an unrecoverable error.
  *
  * @see SideBandOutputStream
+ * @since 4.11
  */
-class SideBandInputStream extends InputStream {
+public class SideBandInputStream extends InputStream {
 	static final int CH_DATA = 1;
 	static final int CH_PROGRESS = 2;
 	static final int CH_ERROR = 3;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
index b186f9e..cdcd2a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
@@ -54,6 +54,7 @@
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.BasePackFetchConnection.FetchConfig;
 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
 import org.eclipse.jgit.transport.resolver.UploadPackFactory;
 
@@ -78,6 +79,8 @@
 public class TestProtocol<C> extends TransportProtocol {
 	private static final String SCHEME = "test"; //$NON-NLS-1$
 
+	private static FetchConfig fetchConfig;
+
 	private class Handle {
 		final C req;
 		final Repository remote;
@@ -147,6 +150,10 @@
 		return Collections.emptySet();
 	}
 
+	static void setFetchConfig(FetchConfig c) {
+		fetchConfig = c;
+	}
+
 	/**
 	 * Register a repository connection over the internal test protocol.
 	 *
@@ -184,8 +191,14 @@
 		public FetchConnection openFetch() throws NotSupportedException,
 				TransportException {
 			handle.remote.incrementOpen();
-			return new InternalFetchConnection<>(
-					this, uploadPackFactory, handle.req, handle.remote);
+			return new InternalFetchConnection<C>(this, uploadPackFactory,
+					handle.req, handle.remote) {
+				@Override
+				FetchConfig getFetchConfig() {
+					return fetchConfig != null ? fetchConfig
+							: super.getFetchConfig();
+				}
+			};
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index db43edc..a182623 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -371,12 +371,9 @@
 	private WalkFetchConnection newDumbConnection(InputStream in)
 			throws IOException, PackProtocolException {
 		HttpObjectDB d = new HttpObjectDB(objectsUrl);
-		BufferedReader br = toBufferedReader(in);
 		Map<String, Ref> refs;
-		try {
+		try (BufferedReader br = toBufferedReader(in)) {
 			refs = d.readAdvertisedImpl(br);
-		} finally {
-			br.close();
 		}
 
 		if (!refs.containsKey(HEAD)) {
@@ -391,8 +388,8 @@
 			int status = HttpSupport.response(conn);
 			switch (status) {
 			case HttpConnection.HTTP_OK: {
-				br = toBufferedReader(openInputStream(conn));
-				try {
+				try (BufferedReader br = toBufferedReader(
+						openInputStream(conn))) {
 					String line = br.readLine();
 					if (line != null && line.startsWith(RefDirectory.SYMREF)) {
 						String target = line.substring(RefDirectory.SYMREF.length());
@@ -406,8 +403,6 @@
 								HEAD, ObjectId.fromString(line));
 						refs.put(r.getName(), r);
 					}
-				} finally {
-					br.close();
 				}
 				break;
 			}
@@ -438,8 +433,7 @@
 		final String service = SVC_RECEIVE_PACK;
 		try {
 			final HttpConnection c = connect(service);
-			final InputStream in = openInputStream(c);
-			try {
+			try (InputStream in = openInputStream(c)) {
 				if (isSmartHttp(c, service)) {
 					return smartPush(service, c, in);
 				} else if (!useSmartHttp) {
@@ -450,8 +444,6 @@
 					final String msg = JGitText.get().remoteDoesNotSupportSmartHTTPPush;
 					throw new NotSupportedException(msg);
 				}
-			} finally {
-				in.close();
 			}
 		} catch (NotSupportedException err) {
 			throw err;
@@ -966,21 +958,16 @@
 		@Override
 		Collection<String> getPackNames() throws IOException {
 			final Collection<String> packs = new ArrayList<>();
-			try {
-				final BufferedReader br = openReader(INFO_PACKS);
-				try {
-					for (;;) {
-						final String s = br.readLine();
-						if (s == null || s.length() == 0)
-							break;
-						if (!s.startsWith("P pack-") || !s.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
-							throw invalidAdvertisement(s);
-						packs.add(s.substring(2));
-					}
-					return packs;
-				} finally {
-					br.close();
+			try (BufferedReader br = openReader(INFO_PACKS)) {
+				for (;;) {
+					final String s = br.readLine();
+					if (s == null || s.length() == 0)
+						break;
+					if (!s.startsWith("P pack-") || !s.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
+						throw invalidAdvertisement(s);
+					packs.add(s.substring(2));
 				}
+				return packs;
 			} catch (FileNotFoundException err) {
 				return packs;
 			}
@@ -1165,10 +1152,8 @@
 			// Try to compress the content, but only if that is smaller.
 			TemporaryBuffer buf = new TemporaryBuffer.Heap(
 					http.getPostBuffer());
-			try {
-				GZIPOutputStream gzip = new GZIPOutputStream(buf);
+			try (GZIPOutputStream gzip = new GZIPOutputStream(buf)) {
 				out.writeTo(gzip, null);
-				gzip.close();
 				if (out.length() < buf.length())
 					buf = out;
 			} catch (IOException err) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index 20eb898..4f1880b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -172,7 +172,9 @@
 
 	private Repository openRepo() throws TransportException {
 		try {
-			return new RepositoryBuilder().setGitDir(remoteGitDir).build();
+			return new RepositoryBuilder()
+					.setFS(local != null ? local.getFS() : FS.DETECTED)
+					.setGitDir(remoteGitDir).build();
 		} catch (IOException err) {
 			throw new TransportException(uri, JGitText.get().notAGitDirectory);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
index e2d08b6..39f9897 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
@@ -429,13 +429,8 @@
 		private Ref readRef(final TreeMap<String, Ref> avail,
 				final String path, final String name) throws TransportException {
 			final String line;
-			try {
-				final BufferedReader br = openReader(path);
-				try {
-					line = br.readLine();
-				} finally {
-					br.close();
-				}
+			try (BufferedReader br = openReader(path)) {
+				line = br.readLine();
 			} catch (FileNotFoundException noRef) {
 				return null;
 			} catch (IOException err) {
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 822d47c..0209be1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -773,6 +773,7 @@
 		boolean sendPack = false;
 		// If it's a non-bidi request, we need to read the entire request before
 		// writing a response. Buffer the response until then.
+		PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
 		try {
 			if (biDirectionalPipe)
 				sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@@ -781,12 +782,15 @@
 			else
 				advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
 
+			long negotiateStart = System.currentTimeMillis();
+			accumulator.advertised = advertised.size();
 			recvWants();
 			if (wantIds.isEmpty()) {
 				preUploadHook.onBeginNegotiateRound(this, wantIds, 0);
 				preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false);
 				return;
 			}
+			accumulator.wants = wantIds.size();
 
 			if (options.contains(OPTION_MULTI_ACK_DETAILED)) {
 				multiAck = MultiAck.DETAILED;
@@ -802,7 +806,10 @@
 				processShallow();
 			if (!clientShallowCommits.isEmpty())
 				walk.assumeShallow(clientShallowCommits);
-			sendPack = negotiate();
+			sendPack = negotiate(accumulator);
+			accumulator.timeNegotiating += System.currentTimeMillis()
+					- negotiateStart;
+
 			if (sendPack && !biDirectionalPipe) {
 				// Ensure the request was fully consumed. Any remaining input must
 				// be a protocol error. If we aren't at EOF the implementation is broken.
@@ -849,7 +856,7 @@
 		}
 
 		if (sendPack)
-			sendPack();
+			sendPack(accumulator);
 	}
 
 	private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
@@ -1093,7 +1100,8 @@
 		return UserAgent.getAgent(options, userAgent);
 	}
 
-	private boolean negotiate() throws IOException {
+	private boolean negotiate(PackStatistics.Accumulator accumulator)
+			throws IOException {
 		okToGiveUp = Boolean.FALSE;
 
 		ObjectId last = ObjectId.zeroId();
@@ -1127,7 +1135,7 @@
 
 			} else if (line.startsWith("have ") && line.length() == 45) { //$NON-NLS-1$
 				peerHas.add(ObjectId.fromString(line.substring(5)));
-
+				accumulator.haves++;
 			} else if (line.equals("done")) { //$NON-NLS-1$
 				last = processHaveLines(peerHas, last);
 
@@ -1485,12 +1493,13 @@
 		return false;
 	}
 
-	private void sendPack() throws IOException {
+	private void sendPack(PackStatistics.Accumulator accumulator)
+			throws IOException {
 		final boolean sideband = options.contains(OPTION_SIDE_BAND)
 				|| options.contains(OPTION_SIDE_BAND_64K);
 		if (sideband) {
 			try {
-				sendPack(true);
+				sendPack(true, accumulator);
 			} catch (ServiceMayNotContinueException noPack) {
 				// This was already reported on (below).
 				throw noPack;
@@ -1511,7 +1520,7 @@
 					throw err;
 			}
 		} else {
-			sendPack(false);
+			sendPack(false, accumulator);
 		}
 	}
 
@@ -1532,7 +1541,8 @@
 	}
 
 	@SuppressWarnings("deprecation")
-	private void sendPack(final boolean sideband) throws IOException {
+	private void sendPack(final boolean sideband,
+			PackStatistics.Accumulator accumulator) throws IOException {
 		ProgressMonitor pm = NullProgressMonitor.INSTANCE;
 		OutputStream packOut = rawOut;
 
@@ -1573,7 +1583,8 @@
 		PackConfig cfg = packConfig;
 		if (cfg == null)
 			cfg = new PackConfig(db);
-		final PackWriter pw = new PackWriter(cfg, walk.getObjectReader());
+		final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
+				accumulator);
 		try {
 			pw.setIndexDisabled(true);
 			pw.setUseCachedPacks(true);
@@ -1617,6 +1628,10 @@
 			if (options.contains(OPTION_INCLUDE_TAG) && refs != null) {
 				for (Ref ref : refs.values()) {
 					ObjectId objectId = ref.getObjectId();
+					if (objectId == null) {
+						// skip unborn branch
+						continue;
+					}
 
 					// If the object was already requested, skip it.
 					if (wantAll.isEmpty()) {
@@ -1632,12 +1647,13 @@
 						ref = db.peel(ref);
 
 					ObjectId peeledId = ref.getPeeledObjectId();
-					if (peeledId == null)
+					objectId = ref.getObjectId();
+					if (peeledId == null || objectId == null)
 						continue;
 
-					objectId = ref.getObjectId();
-					if (pw.willInclude(peeledId) && !pw.willInclude(objectId))
+					if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
 						pw.addObject(rw.parseAny(objectId));
+					}
 				}
 			}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
index a0ad2f3..965be50 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
@@ -264,11 +264,8 @@
 	 *             failed, possibly due to permissions or remote disk full, etc.
 	 */
 	void writeFile(final String path, final byte[] data) throws IOException {
-		final OutputStream os = writeFile(path, null, null);
-		try {
+		try (OutputStream os = writeFile(path, null, null)) {
 			os.write(data);
-		} finally {
-			os.close();
 		}
 	}
 
@@ -394,8 +391,7 @@
 	 */
 	Collection<WalkRemoteObjectDatabase> readAlternates(final String listPath)
 			throws IOException {
-		final BufferedReader br = openReader(listPath);
-		try {
+		try (BufferedReader br = openReader(listPath)) {
 			final Collection<WalkRemoteObjectDatabase> alts = new ArrayList<>();
 			for (;;) {
 				String line = br.readLine();
@@ -406,8 +402,6 @@
 				alts.add(openAlternate(line));
 			}
 			return alts;
-		} finally {
-			br.close();
 		}
 	}
 
@@ -422,14 +416,8 @@
 	 */
 	protected void readPackedRefs(final Map<String, Ref> avail)
 			throws TransportException {
-		try {
-			final BufferedReader br = openReader(ROOT_DIR
-					+ Constants.PACKED_REFS);
-			try {
-				readPackedRefsImpl(avail, br);
-			} finally {
-				br.close();
-			}
+		try (BufferedReader br = openReader(ROOT_DIR + Constants.PACKED_REFS)) {
+			readPackedRefsImpl(avail, br);
 		} catch (FileNotFoundException notPacked) {
 			// Perhaps it wasn't worthwhile, or is just an older repository.
 		} catch (IOException e) {
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 3e06f04..5560f77 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -187,7 +187,7 @@
 				//
 				t.matches = minRef;
 			} else if (fastMinHasMatch && isTree(t) && !isTree(minRef)
-					&& nameEqual(t, minRef)) {
+					&& !isGitlink(minRef) && nameEqual(t, minRef)) {
 				// The minimum is a file (non-tree) but the next entry
 				// of this iterator is a tree whose name matches our file.
 				// This is a classic D/F conflict and commonly occurs like
@@ -218,6 +218,10 @@
 		return a.pathCompare(b, TREE_MODE) == 0;
 	}
 
+	private boolean isGitlink(AbstractTreeIterator p) {
+		return FileMode.GITLINK.equals(p.mode);
+	}
+
 	private static boolean isTree(final AbstractTreeIterator p) {
 		return FileMode.TREE.equals(p.mode);
 	}
@@ -306,8 +310,9 @@
 				if (t.matches == minRef)
 					t.matches = treeMatch;
 
-			if (dfConflict == null)
+			if (dfConflict == null && !isGitlink(minRef)) {
 				dfConflict = treeMatch;
+			}
 
 			return treeMatch;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index a91ad59..8872689 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -1424,7 +1424,7 @@
 
 	/**
 	 * Inspect config and attributes to return a filtercommand applicable for
-	 * the current path
+	 * the current path, but without expanding %f occurences
 	 *
 	 * @param filterCommandType
 	 *            which type of filterCommand should be executed. E.g. "clean",
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 8d02f90..68cc7cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -60,6 +60,8 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.eclipse.jgit.api.errors.FilterFailedException;
 import org.eclipse.jgit.attributes.AttributesNode;
@@ -474,7 +476,7 @@
 				while (command.run() != -1) {
 					// loop as long as command.run() tells there is work to do
 				}
-				return buffer.openInputStream();
+				return buffer.openInputStreamWithAutoDestroy();
 			}
 			FS fs = repository.getFS();
 			ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand,
@@ -497,7 +499,7 @@
 						RawParseUtils.decode(result.getStderr()
 								.toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
 			}
-			return result.getStdout().openInputStream();
+			return result.getStdout().openInputStreamWithAutoDestroy();
 		}
 		return in;
 	}
@@ -661,54 +663,60 @@
 	 *             a relevant ignore rule file exists but cannot be read.
 	 */
 	protected boolean isEntryIgnored(final int pLen) throws IOException {
-		return isEntryIgnored(pLen, mode, false);
+		return isEntryIgnored(pLen, mode);
 	}
 
 	/**
-	 * Determine if the entry path is ignored by an ignore rule. Consider
-	 * possible rule negation from child iterator.
+	 * Determine if the entry path is ignored by an ignore rule.
 	 *
 	 * @param pLen
 	 *            the length of the path in the path buffer.
 	 * @param fileMode
 	 *            the original iterator file mode
-	 * @param negatePrevious
-	 *            true if the previous matching iterator rule was negation
 	 * @return true if the entry is ignored by an ignore rule.
 	 * @throws IOException
 	 *             a relevant ignore rule file exists but cannot be read.
 	 */
-	private boolean isEntryIgnored(final int pLen, int fileMode,
-			boolean negatePrevious)
+	private boolean isEntryIgnored(final int pLen, int fileMode)
 			throws IOException {
-		IgnoreNode rules = getIgnoreNode();
-		if (rules != null) {
-			// The ignore code wants path to start with a '/' if possible.
-			// If we have the '/' in our path buffer because we are inside
-			// a subdirectory include it in the range we convert to string.
-			//
-			int pOff = pathOffset;
-			if (0 < pOff)
-				pOff--;
-			String p = TreeWalk.pathOf(path, pOff, pLen);
-			switch (rules.isIgnored(p, FileMode.TREE.equals(fileMode),
-					negatePrevious)) {
-			case IGNORED:
-				return true;
-			case NOT_IGNORED:
-				return false;
-			case CHECK_PARENT:
-				negatePrevious = false;
-				break;
-			case CHECK_PARENT_NEGATE_FIRST_MATCH:
-				negatePrevious = true;
-				break;
-			}
+		// The ignore code wants path to start with a '/' if possible.
+		// If we have the '/' in our path buffer because we are inside
+		// a sub-directory include it in the range we convert to string.
+		//
+		final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset;
+		String pathRel = TreeWalk.pathOf(this.path, pOff, pLen);
+		String parentRel = getParentPath(pathRel);
+
+		// CGit is processing .gitignore files by starting at the root of the
+		// repository and then recursing into subdirectories. With this
+		// approach, top-level ignored directories will be processed first which
+		// allows to skip entire subtrees and further .gitignore-file processing
+		// within these subtrees.
+		//
+		// We will follow the same approach by marking directories as "ignored"
+		// here. This allows to have a simplified FastIgnore.checkIgnore()
+		// implementation (both in terms of code and computational complexity):
+		//
+		// Without the "ignored" flag, we would have to apply the ignore-check
+		// to a path and all of its parents always(!), to determine whether a
+		// path is ignored directly or by one of its parent directories; with
+		// the "ignored" flag, we know at this point that the parent directory
+		// is definitely not ignored, thus the path can only become ignored if
+		// there is a rule matching the path itself.
+		if (isDirectoryIgnored(parentRel)) {
+			return true;
 		}
-		if (parent instanceof WorkingTreeIterator)
-			return ((WorkingTreeIterator) parent).isEntryIgnored(pLen, fileMode,
-					negatePrevious);
-		return false;
+
+		IgnoreNode rules = getIgnoreNode();
+		final Boolean ignored = rules != null
+				? rules.checkIgnored(pathRel, FileMode.TREE.equals(fileMode))
+				: null;
+		if (ignored != null) {
+			return ignored.booleanValue();
+		}
+		return parent instanceof WorkingTreeIterator
+				&& ((WorkingTreeIterator) parent).isEntryIgnored(pLen,
+						fileMode);
 	}
 
 	private IgnoreNode getIgnoreNode() throws IOException {
@@ -952,7 +960,19 @@
 			}
 			return false;
 		case DIFFER_BY_METADATA:
-			if (mode == FileMode.SYMLINK.getBits())
+			if (mode == FileMode.TREE.getBits()
+					&& entry.getFileMode().equals(FileMode.GITLINK)) {
+				byte[] idBuffer = idBuffer();
+				int idOffset = idOffset();
+				if (entry.getObjectId().compareTo(idBuffer, idOffset) == 0) {
+					return true;
+				} else if (ObjectId.zeroId().compareTo(idBuffer,
+						idOffset) == 0) {
+					return new File(repository.getWorkTree(),
+							entry.getPathString()).list().length > 0;
+				}
+				return false;
+			} else if (mode == FileMode.SYMLINK.getBits())
 				return contentCheck(entry, reader);
 			return true;
 		default:
@@ -1360,6 +1380,8 @@
 		/** Position of the matching {@link DirCacheIterator}. */
 		int dirCacheTree;
 
+		final Map<String, Boolean> directoryToIgnored = new HashMap<>();
+
 		IteratorState(WorkingTreeOptions options) {
 			this.options = options;
 			this.nameEncoder = Constants.CHARSET.newEncoder();
@@ -1436,4 +1458,67 @@
 		}
 		return eolStreamTypeHolder.get();
 	}
+
+	private boolean isDirectoryIgnored(String pathRel) throws IOException {
+		final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset;
+		final String base = TreeWalk.pathOf(this.path, 0, pOff);
+		final String pathAbs = concatPath(base, pathRel);
+		return isDirectoryIgnored(pathRel, pathAbs);
+	}
+
+	private boolean isDirectoryIgnored(String pathRel, String pathAbs)
+			throws IOException {
+		assert pathRel.length() == 0 || (pathRel.charAt(0) != '/'
+				&& pathRel.charAt(pathRel.length() - 1) != '/');
+		assert pathAbs.length() == 0 || (pathAbs.charAt(0) != '/'
+				&& pathAbs.charAt(pathAbs.length() - 1) != '/');
+		assert pathAbs.endsWith(pathRel);
+
+		Boolean ignored = state.directoryToIgnored.get(pathAbs);
+		if (ignored != null) {
+			return ignored.booleanValue();
+		}
+
+		final String parentRel = getParentPath(pathRel);
+		if (parentRel != null && isDirectoryIgnored(parentRel)) {
+			state.directoryToIgnored.put(pathAbs, Boolean.TRUE);
+			return true;
+		}
+
+		final IgnoreNode node = getIgnoreNode();
+		for (String p = pathRel; node != null
+				&& !"".equals(p); p = getParentPath(p)) { //$NON-NLS-1$
+			ignored = node.checkIgnored(p, true);
+			if (ignored != null) {
+				state.directoryToIgnored.put(pathAbs, ignored);
+				return ignored.booleanValue();
+			}
+		}
+
+		if (!(this.parent instanceof WorkingTreeIterator)) {
+			state.directoryToIgnored.put(pathAbs, Boolean.FALSE);
+			return false;
+		}
+
+		final WorkingTreeIterator wtParent = (WorkingTreeIterator) this.parent;
+		final String parentRelPath = concatPath(
+				TreeWalk.pathOf(this.path, wtParent.pathOffset, pathOffset - 1),
+				pathRel);
+		assert concatPath(TreeWalk.pathOf(wtParent.path, 0,
+				Math.max(0, wtParent.pathOffset - 1)), parentRelPath)
+						.equals(pathAbs);
+		return wtParent.isDirectoryIgnored(parentRelPath, pathAbs);
+	}
+
+	private static String getParentPath(String path) {
+		final int slashIndex = path.lastIndexOf('/', path.length() - 2);
+		if (slashIndex > 0) {
+			return path.substring(path.charAt(0) == '/' ? 1 : 0, slashIndex);
+		}
+		return path.length() > 0 ? "" : null; //$NON-NLS-1$
+	}
+
+	private static String concatPath(String p1, String p2) {
+		return p1 + (p1.length() > 0 && p2.length() > 0 ? "/" : "") + p2; //$NON-NLS-1$ //$NON-NLS-2$
+	}
 }
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 b442b3c..56f7acb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -1301,14 +1301,11 @@
 	 */
 	public ExecutionResult execute(ProcessBuilder pb, InputStream in)
 			throws IOException, InterruptedException {
-		TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null);
-		TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024);
-		try {
+		try (TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null);
+				TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024,
+						1024 * 1024)) {
 			int rc = runProcess(pb, stdout, stderr, in);
 			return new ExecutionResult(stdout, stderr, rc);
-		} finally {
-			stdout.close();
-			stderr.close();
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
new file mode 100644
index 0000000..6d60ef3
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018, Markus Duft <markus.duft@ssi-schaefer.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.text.MessageFormat;
+import java.util.concurrent.Callable;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.Attribute;
+import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.hooks.PrePushHook;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+
+/**
+ * Represents an optionally present LFS support implementation
+ *
+ * @since 4.11
+ */
+public class LfsFactory {
+
+	private static LfsFactory instance = new LfsFactory();
+
+	/**
+	 * Constructor
+	 */
+	protected LfsFactory() {
+	}
+
+	/**
+	 * @return the current LFS implementation
+	 */
+	public static LfsFactory getInstance() {
+		return instance;
+	}
+
+	/**
+	 * @param instance
+	 *            register a {@link LfsFactory} instance as the
+	 *            {@link LfsFactory} implementation to use.
+	 */
+	public static void setInstance(LfsFactory instance) {
+		LfsFactory.instance = instance;
+	}
+
+	/**
+	 * @return whether LFS support is available
+	 */
+	public boolean isAvailable() {
+		return false;
+	}
+
+	/**
+	 * Apply clean filtering to the given stream, writing the file content to
+	 * the LFS storage if required and returning a stream to the LFS pointer
+	 * instead.
+	 *
+	 * @param db
+	 *            the repository
+	 * @param input
+	 *            the original input
+	 * @param length
+	 *            the expected input stream length
+	 * @param attribute
+	 *            the attribute used to check for LFS enablement (i.e. "merge",
+	 *            "diff", "filter" from .gitattributes).
+	 * @return a stream to the content that should be written to the object
+	 *         store along with the expected length of the stream. the original
+	 *         stream is not applicable.
+	 * @throws IOException
+	 *             in case of an error
+	 */
+	public LfsInputStream applyCleanFilter(Repository db,
+			InputStream input, long length, Attribute attribute)
+			throws IOException {
+		return new LfsInputStream(input, length);
+	}
+
+	/**
+	 * Apply smudge filtering to a given loader, potentially redirecting it to a
+	 * LFS blob which is downloaded on demand.
+	 *
+	 * @param db
+	 *            the repository
+	 * @param loader
+	 *            the loader for the blob
+	 * @param attribute
+	 *            the attribute used to check for LFS enablement (i.e. "merge",
+	 *            "diff", "filter" from .gitattributes).
+	 * @return a loader for the actual data of a blob, or the original loader in
+	 *         case LFS is not applicable.
+	 * @throws IOException
+	 */
+	public ObjectLoader applySmudgeFilter(Repository db,
+			ObjectLoader loader, Attribute attribute) throws IOException {
+		return loader;
+	}
+
+	/**
+	 * Retrieve a pre-push hook to be applied.
+	 *
+	 * @param repo
+	 *            the {@link Repository} the hook is applied to.
+	 * @param outputStream
+	 * @return a {@link PrePushHook} implementation or <code>null</code>
+	 */
+	public @Nullable PrePushHook getPrePushHook(Repository repo,
+			PrintStream outputStream) {
+		return null;
+	}
+
+	/**
+	 * Retrieve an {@link LfsInstallCommand} which can be used to enable LFS
+	 * support (if available) either per repository or for the user.
+	 *
+	 * @return a command to install LFS support.
+	 */
+	public @Nullable LfsInstallCommand getInstallCommand() {
+		return null;
+	}
+
+	/**
+	 * @param db
+	 *            the repository to check
+	 * @return whether LFS is enabled for the given repository locally or
+	 *         globally.
+	 */
+	public boolean isEnabled(Repository db) {
+		return false;
+	}
+
+	/**
+	 * @param db
+	 *            the repository
+	 * @param path
+	 *            the path to find attributes for
+	 * @return the {@link Attributes} for the given path.
+	 * @throws IOException
+	 *             in case of an error
+	 */
+	public static Attributes getAttributesForPath(Repository db, String path)
+			throws IOException {
+		try (TreeWalk walk = new TreeWalk(db)) {
+			walk.addTree(new FileTreeIterator(db));
+			PathFilter f = PathFilter.create(path);
+			walk.setFilter(f);
+			walk.setRecursive(false);
+			Attributes attr = null;
+			while (walk.next()) {
+				if (f.isDone(walk)) {
+					attr = walk.getAttributes();
+					break;
+				} else if (walk.isSubtree()) {
+					walk.enterSubtree();
+				}
+			}
+			if (attr == null) {
+				throw new IOException(MessageFormat
+						.format(JGitText.get().noPathAttributesFound, path));
+			}
+
+			return attr;
+		}
+	}
+
+	/**
+	 * Get attributes for given path and commit
+	 *
+	 * @param db
+	 *            the repository
+	 * @param path
+	 *            the path to find attributes for
+	 * @param commit
+	 *            the commit to inspect.
+	 * @return the {@link Attributes} for the given path.
+	 * @throws IOException
+	 *             in case of an error
+	 */
+	public static Attributes getAttributesForPath(Repository db, String path,
+			RevCommit commit) throws IOException {
+		if (commit == null) {
+			return getAttributesForPath(db, path);
+		}
+
+		try (TreeWalk walk = TreeWalk.forPath(db, path, commit.getTree())) {
+			Attributes attr = walk == null ? null : walk.getAttributes();
+			if (attr == null) {
+				throw new IOException(MessageFormat
+						.format(JGitText.get().noPathAttributesFound, path));
+			}
+
+			return attr;
+		}
+	}
+
+	/**
+	 * Encapsulate a potentially exchanged {@link InputStream} along with the
+	 * expected stream content length.
+	 */
+	public static final class LfsInputStream extends InputStream {
+		/**
+		 * The actual stream.
+		 */
+		private InputStream stream;
+
+		/**
+		 * The expected stream content length.
+		 */
+		private long length;
+
+		/**
+		 * Create a new wrapper around a certain stream
+		 *
+		 * @param stream
+		 *            the stream to wrap. the stream will be closed on
+		 *            {@link #close()}.
+		 * @param length
+		 *            the expected length of the stream
+		 */
+		public LfsInputStream(InputStream stream, long length) {
+			this.stream = stream;
+			this.length = length;
+		}
+
+		/**
+		 * Create a new wrapper around a temporary buffer.
+		 *
+		 * @param buffer
+		 *            the buffer to initialize stream and length from. The
+		 *            buffer will be destroyed on {@link #close()}
+		 * @throws IOException
+		 *             in case of an error opening the stream to the buffer.
+		 */
+		public LfsInputStream(TemporaryBuffer buffer) throws IOException {
+			this.stream = buffer.openInputStreamWithAutoDestroy();
+			this.length = buffer.length();
+		}
+
+		@Override
+		public void close() throws IOException {
+			stream.close();
+		}
+
+		@Override
+		public int read() throws IOException {
+			return stream.read();
+		}
+
+		/**
+		 * @return the length of the stream
+		 */
+		public long getLength() {
+			return length;
+		}
+	}
+
+	/**
+	 * A command to enable LFS. Optionally set a {@link Repository} to enable
+	 * locally on the repository only.
+	 */
+	public interface LfsInstallCommand extends Callable<Void> {
+		/**
+		 * @param repo
+		 *            the repository to enable support for.
+		 * @return The {@link LfsInstallCommand} for chaining.
+		 */
+		public LfsInstallCommand setRepository(Repository repo);
+	}
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index 31abb7c..dd933a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -314,6 +314,26 @@
 	}
 
 	/**
+	 * Same as {@link #openInputStream()} but handling destruction of any
+	 * associated resources automatically when closing the returned stream.
+	 *
+	 * @return an InputStream which will automatically destroy any associated
+	 *         temporary file on {@link #close()}
+	 * @throws IOException
+	 *             in case of an error.
+	 * @since 4.11
+	 */
+	public InputStream openInputStreamWithAutoDestroy() throws IOException {
+		return new BlockInputStream() {
+			@Override
+			public void close() throws IOException {
+				super.close();
+				destroy();
+			}
+		};
+	}
+
+	/**
 	 * Reset this buffer for reuse, purging all buffered content.
 	 */
 	public void reset() {
@@ -506,6 +526,20 @@
 		}
 
 		@Override
+		public InputStream openInputStreamWithAutoDestroy() throws IOException {
+			if (onDiskFile == null) {
+				return super.openInputStreamWithAutoDestroy();
+			}
+			return new FileInputStream(onDiskFile) {
+				@Override
+				public void close() throws IOException {
+					super.close();
+					destroy();
+				}
+			};
+		}
+
+		@Override
 		public void destroy() {
 			super.destroy();
 
diff --git a/pom.xml b/pom.xml
index 47325f2..cc94f39 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.10.1-SNAPSHOT</version>
+  <version>4.11.3-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -89,6 +89,9 @@
       <name>Dave Borowitz</name>
     </developer>
     <developer>
+      <name>David Pursehouse</name>
+    </developer>
+    <developer>
       <name>Gunnar Wagenknecht</name>
     </developer>
     <developer>
@@ -118,6 +121,9 @@
     <developer>
       <name>Stefan Lay</name>
     </developer>
+    <developer>
+      <name>Thomas Wolf</name>
+    </developer>
   </developers>
 
   <mailingLists>
@@ -191,13 +197,14 @@
     <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.9.2.201712150930-r</jgit-last-release-version>
+    <jgit-last-release-version>4.11.0.201803080745-r</jgit-last-release-version>
     <jsch-version>0.1.54</jsch-version>
+    <jzlib-version>1.1.1</jzlib-version>
     <javaewah-version>1.1.6</javaewah-version>
     <junit-version>4.12</junit-version>
     <test-fork-count>1C</test-fork-count>
     <args4j-version>2.33</args4j-version>
-    <commons-compress-version>1.6</commons-compress-version>
+    <commons-compress-version>1.15</commons-compress-version>
     <osgi-core-version>4.3.1</osgi-core-version>
     <servlet-api-version>3.1.0</servlet-api-version>
     <jetty-version>9.4.8.v20171121</jetty-version>
@@ -207,9 +214,9 @@
     <slf4j-version>1.7.2</slf4j-version>
     <log4j-version>1.2.15</log4j-version>
     <maven-javadoc-plugin-version>3.0.0</maven-javadoc-plugin-version>
-    <tycho-extras-version>1.0.0</tycho-extras-version>
-    <gson-version>2.2.4</gson-version>
-    <spotbugs-maven-plugin-version>3.1.0</spotbugs-maven-plugin-version>
+    <tycho-extras-version>1.1.0</tycho-extras-version>
+    <gson-version>2.8.2</gson-version>
+    <spotbugs-maven-plugin-version>3.1.2</spotbugs-maven-plugin-version>
     <maven-surefire-report-plugin-version>2.20.1</maven-surefire-report-plugin-version>
 
     <!-- Properties to enable jacoco code coverage analysis -->
@@ -311,7 +318,7 @@
             <dependency>
               <groupId>com.google.errorprone</groupId>
               <artifactId>error_prone_core</artifactId>
-              <version>2.1.3</version>
+              <version>2.2.0</version>
             </dependency>
           </dependencies>
         </plugin>
@@ -624,6 +631,12 @@
       </dependency>
 
       <dependency>
+        <groupId>com.jcraft</groupId>
+        <artifactId>jzlib</artifactId>
+        <version>${jzlib-version}</version>
+      </dependency>
+
+      <dependency>
         <groupId>com.googlecode.javaewah</groupId>
         <artifactId>JavaEWAH</artifactId>
         <version>${javaewah-version}</version>
@@ -654,6 +667,13 @@
       </dependency>
 
       <dependency>
+        <groupId>org.tukaani</groupId>
+        <artifactId>xz</artifactId>
+        <version>1.6</version>
+        <optional>true</optional>
+      </dependency>
+
+      <dependency>
         <groupId>org.eclipse.jetty</groupId>
         <artifactId>jetty-servlet</artifactId>
         <version>${jetty-version}</version>