Merge branch 'stable-4.2' into stable-4.3

* stable-4.2:
  JGit v4.0.3.201509231615-r

Change-Id: Ic90ef74497afee9da4b49dcb53302b4efa5b9f26
Signed-off-by: David Pursehouse <david.pursehouse@gmail.com>
diff --git a/.buckconfig b/.buckconfig
index b2e07ac..8d2572b 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -1,3 +1,12 @@
+[alias]
+  all = //:all
+  jgit = //org.eclipse.jgit:jgit
+  jgit-archive = //org.eclipse.jgit.archive:jgit-archive
+  jgit_bin = //:jgit_bin
+  jgit-lfs = //org.eclipse.jgit.lfs:jgit-lfs
+  jgit-lfs-server = //org.eclipse.jgit.lfs.server:jgit-lfs-server
+  jgit-servlet = //org.eclipse.jgit.http.server:jgit-servlet
+
 [buildfile]
   includes = //tools/default.defs
 
diff --git a/.buckversion b/.buckversion
index 9daac2c..561a769 100644
--- a/.buckversion
+++ b/.buckversion
@@ -1 +1 @@
-1b03b4313b91b634bd604fc3487a05f877e59dee
+ca8d6cbac373a690f543c5159eec0116e76187a9
diff --git a/BUCK b/BUCK
index f19b7bd..4578e17 100644
--- a/BUCK
+++ b/BUCK
@@ -1,45 +1,18 @@
-java_library(
-  name = 'jgit',
-  exported_deps = ['//org.eclipse.jgit:jgit'],
-  visibility = ['PUBLIC'],
+DEPS = [
+  '//org.eclipse.jgit:jgit',
+  '//org.eclipse.jgit.archive:jgit-archive',
+  '//org.eclipse.jgit.http.server:jgit-servlet',
+  '//org.eclipse.jgit.lfs:jgit-lfs',
+  '//org.eclipse.jgit.lfs.server:jgit-lfs-server',
+  '//org.eclipse.jgit.pgm:jgit',
+]
+
+zip_file(
+  name = 'all',
+  srcs = DEPS,
 )
 
-genrule(
-  name = 'jgit_src',
-  cmd = 'ln -s $(location //org.eclipse.jgit:jgit_src) $OUT',
-  out = 'jgit_src.zip',
-  visibility = ['PUBLIC'],
-)
-
-java_library(
-  name = 'jgit-servlet',
-  exported_deps = [
-    ':jgit',
-    '//org.eclipse.jgit.http.server:jgit-servlet'
-  ],
-  visibility = ['PUBLIC'],
-)
-
-java_library(
-  name = 'jgit-archive',
-  exported_deps = [
-    ':jgit',
-    '//org.eclipse.jgit.archive:jgit-archive'
-  ],
-  visibility = ['PUBLIC'],
-)
-
-java_library(
-  name = 'junit',
-  exported_deps = [
-    ':jgit',
-    '//org.eclipse.jgit.junit:junit'
-  ],
-  visibility = ['PUBLIC'],
-)
-
-genrule(
+sh_binary(
   name = 'jgit_bin',
-  cmd = 'ln -s $(location //org.eclipse.jgit.pgm:jgit) $OUT',
-  out = 'jgit_bin',
+  main = '//org.eclipse.jgit.pgm:jgit',
 )
diff --git a/lib/BUCK b/lib/BUCK
index 524612b..2421b40 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -123,3 +123,12 @@
   artifact = 'hamcrest-core',
   version = '1.3',
 )
+
+maven_jar(
+  name = 'gson',
+  bin_sha1 = 'a60a5e993c98c864010053cb901b7eab25306568',
+  src_sha1 = 'a6dc5db8a12928e583bd3f23e72d3ab611ecd58f',
+  group = 'com.google.code.gson',
+  artifact = 'gson',
+  version = '2.2.4',
+)
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index cfaefcd..afa0f5c 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.junit;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
+ org.eclipse.jgit.ant.tasks;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
  org.hamcrest;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index f1e7e36..fc1257d 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 74720fe..98da2c0 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -2,11 +2,11 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)"
+  org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="4.2.1";
+Export-Package: org.eclipse.jgit.ant.tasks;version="4.3.2";
  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 0e69457..6fc806f 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.2.1-SNAPSHOT</version>
+		<version>4.3.2-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index 4f3a61f..a185d1f 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
@@ -12,14 +12,14 @@
  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.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
+ org.eclipse.jgit.api;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.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.2.1";
+Export-Package: org.eclipse.jgit.archive;version="4.3.2";
   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 61fbb7e..cf164a9 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.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.2.1.qualifier";roots="."
+Bundle-Version: 4.3.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.3.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index b9c509f..7b9c6ac 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.http.apache/BUCK b/org.eclipse.jgit.http.apache/BUCK
index f48f33a..3b7a823 100644
--- a/org.eclipse.jgit.http.apache/BUCK
+++ b/org.eclipse.jgit.http.apache/BUCK
@@ -4,7 +4,6 @@
   resources = glob(['resources/**']),
   deps = [
     '//org.eclipse.jgit:jgit',
-    '//lib:commons-logging',
     '//lib:httpcomponents',
     '//lib:httpcore',
   ],
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index b775549..0872d49 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -2,13 +2,12 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
 Bundle-ActivationPolicy: lazy
-Import-Package: org.apache.commons.logging;version="[1.1.1,2.0.0)",
- org.apache.http;version="[4.1.0,5.0.0)",
+Import-Package: org.apache.http;version="[4.1.0,5.0.0)",
  org.apache.http.client;version="[4.1.0,5.0.0)",
  org.apache.http.client.methods;version="[4.1.0,5.0.0)",
  org.apache.http.client.params;version="[4.1.0,5.0.0)",
@@ -20,10 +19,10 @@
  org.apache.http.impl.client;version="[4.1.0,5.0.0)",
  org.apache.http.impl.client.cache;version="[4.1.0,5.0.0)",
  org.apache.http.params;version="[4.1.0,5.0.0)",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.http;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="4.2.1";
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.3.2";
   uses:="org.eclipse.jgit.transport.http,
    javax.net.ssl,
    org.apache.http.client,
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index e2df65b..2b0b58d 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.2.1-SNAPSHOT</version>
+		<version>4.3.2-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
index de81bf8..a0eeef8 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
@@ -42,6 +42,11 @@
  */
 package org.eclipse.jgit.transport.http.apache;
 
+import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
+import static org.eclipse.jgit.util.HttpSupport.METHOD_HEAD;
+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.OutputStream;
@@ -76,6 +81,7 @@
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.methods.HttpPut;
 import org.apache.http.client.methods.HttpUriRequest;
@@ -250,13 +256,15 @@
 
 	public void setRequestMethod(String method) throws ProtocolException {
 		this.method = method;
-		if ("GET".equalsIgnoreCase(method)) //$NON-NLS-1$
+		if (METHOD_GET.equalsIgnoreCase(method)) {
 			req = new HttpGet(url.toString());
-		else if ("PUT".equalsIgnoreCase(method)) //$NON-NLS-1$
+		} else if (METHOD_HEAD.equalsIgnoreCase(method)) {
+			req = new HttpHead(url.toString());
+		} else if (METHOD_PUT.equalsIgnoreCase(method)) {
 			req = new HttpPut(url.toString());
-		else if ("POST".equalsIgnoreCase(method)) //$NON-NLS-1$
+		} else if (METHOD_POST.equalsIgnoreCase(method)) {
 			req = new HttpPost(url.toString());
-		else {
+		} else {
 			this.method = null;
 			throw new UnsupportedOperationException();
 		}
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 9f87aab..848b55e 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -2,13 +2,13 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="4.2.1",
- org.eclipse.jgit.http.server.glue;version="4.2.1";
+Export-Package: org.eclipse.jgit.http.server;version="4.3.2",
+ org.eclipse.jgit.http.server.glue;version="4.3.2";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="4.2.1";
+ org.eclipse.jgit.http.server.resolver;version="4.3.2";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -17,12 +17,12 @@
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 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.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)"
+ org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index e13e9fa..c6de937 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.test/BUCK b/org.eclipse.jgit.http.test/BUCK
index d2ced7a..d65cdad 100644
--- a/org.eclipse.jgit.http.test/BUCK
+++ b/org.eclipse.jgit.http.test/BUCK
@@ -23,6 +23,7 @@
       '//lib/jetty:servlet',
       '//lib/jetty:security',
       '//lib/jetty:util',
+      '//lib:commons-logging',
     ],
     source_under_test = ['//org.eclipse.jgit.http.server:jgit-servlet'],
   )
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 05e4f81..fefdb66 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
@@ -22,23 +22,23 @@
  org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
- org.eclipse.jgit.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.http.server;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.http.server.glue;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.http.server.resolver;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.junit;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.junit.http;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.http;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
+ org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.http.server;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.junit.http;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 44f7a38..8fc34f7 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 82861ed..0f3d3c6 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -56,6 +56,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -89,6 +90,8 @@
 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.ObjectInserter;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.ReflogEntry;
 import org.eclipse.jgit.lib.ReflogReader;
@@ -487,6 +490,25 @@
 	}
 
 	@Test
+	public void testInvalidWant() throws Exception {
+		@SuppressWarnings("resource")
+		ObjectId id = new ObjectInserter.Formatter().idFor(Constants.OBJ_BLOB,
+				"testInvalidWant".getBytes(StandardCharsets.UTF_8));
+
+		Repository dst = createBareRepository();
+		try (Transport t = Transport.open(dst, remoteURI);
+				FetchConnection c = t.openFetch()) {
+			Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(),
+					id);
+			c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want),
+					Collections.<ObjectId> emptySet());
+			fail("Server accepted want " + id.name());
+		} catch (TransportException err) {
+			assertEquals("want " + id.name() + " not valid", err.getMessage());
+		}
+	}
+
+	@Test
 	public void testPush_NotAuthorized() throws Exception {
 		final TestRepository src = createTestRepository();
 		final RevBlob Q_txt = src.blob("new text");
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index bec4178..bd26ac0 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -20,16 +20,16 @@
  org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
- org.eclipse.jgit.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.http.server;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.junit;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)",
+ org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.http.server;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="4.2.1";
+Export-Package: org.eclipse.jgit.junit.http;version="4.3.2";
   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 47be76b..43fdb8a 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
index ce04bdf..c36c297 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
@@ -104,6 +104,15 @@
 	private final TestRequestLog log;
 
 	public AppServer() {
+		this(0);
+	}
+
+	/**
+	 * @param port
+	 *            the http port number
+	 * @since 4.2
+	 */
+	public AppServer(int port) {
 		server = new Server();
 
 		HttpConfiguration http_config = new HttpConfiguration();
@@ -113,7 +122,7 @@
 
 		connector = new ServerConnector(server,
 				new HttpConnectionFactory(http_config));
-		connector.setPort(0);
+		connector.setPort(port);
 		try {
 			final InetAddress me = InetAddress.getByName("localhost");
 			connector.setHost(me.getHostAddress());
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 794e3d3..89171bc 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -2,27 +2,27 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Import-Package: org.eclipse.jgit.api;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.merge;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="4.2.1";
+Export-Package: org.eclipse.jgit.junit;version="4.3.2";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index 4c7a85b..bb2b677 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
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 0db71f7..86b87b4 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
@@ -144,10 +144,10 @@
 
 	protected static void checkFile(File f, final String checkData)
 			throws IOException {
-		Reader r = new InputStreamReader(new FileInputStream(f), "ISO-8859-1");
+		Reader r = new InputStreamReader(new FileInputStream(f), "UTF-8");
 		try {
-			char[] data = new char[(int) f.length()];
-			if (f.length() !=  r.read(data))
+			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 {
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index e259156..1119e63 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -112,23 +112,18 @@
  *            type of Repository the test data is stored on.
  */
 public class TestRepository<R extends Repository> {
-	private static final PersonIdent defaultAuthor;
 
-	private static final PersonIdent defaultCommitter;
+	public static final String AUTHOR = "J. Author";
 
-	static {
-		final MockSystemReader m = new MockSystemReader();
-		final long now = m.getCurrentTime();
-		final int tz = m.getTimezone(now);
+	public static final String AUTHOR_EMAIL = "jauthor@example.com";
 
-		final String an = "J. Author";
-		final String ae = "jauthor@example.com";
-		defaultAuthor = new PersonIdent(an, ae, now, tz);
+	public static final String COMMITTER = "J. Committer";
 
-		final String cn = "J. Committer";
-		final String ce = "jcommitter@example.com";
-		defaultCommitter = new PersonIdent(cn, ce, now, tz);
-	}
+	public static final String COMMITTER_EMAIL = "jcommitter@example.com";
+
+	private final PersonIdent defaultAuthor;
+
+	private final PersonIdent defaultCommitter;
 
 	private final R db;
 
@@ -184,6 +179,10 @@
 		this.pool = rw;
 		this.inserter = db.newObjectInserter();
 		this.mockSystemReader = reader;
+		long now = mockSystemReader.getCurrentTime();
+		int tz = mockSystemReader.getTimezone(now);
+		defaultAuthor = new PersonIdent(AUTHOR, AUTHOR_EMAIL, now, tz);
+		defaultCommitter = new PersonIdent(COMMITTER, COMMITTER_EMAIL, now, tz);
 	}
 
 	/** @return the repository this helper class operates against. */
diff --git a/org.eclipse.jgit.lfs.server.test/.classpath b/org.eclipse.jgit.lfs.server.test/.classpath
new file mode 100644
index 0000000..2fdcc94
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="tst"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.lfs.server.test/.gitignore b/org.eclipse.jgit.lfs.server.test/.gitignore
new file mode 100644
index 0000000..4dc0091
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.gitignore
@@ -0,0 +1,2 @@
+/target
+/bin
diff --git a/org.eclipse.jgit.lfs.server.test/.project b/org.eclipse.jgit.lfs.server.test/.project
new file mode 100644
index 0000000..e3a4748
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.lfs.server.test</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..4824b80
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1

+encoding/<project>=UTF-8

diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..5a0ad22
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
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
new file mode 100644
index 0000000..dcc0d3a
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,398 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..c336cce
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000..3dec4d9
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000..ce7a0f0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,2 @@
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..d585687
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,93 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..923c37f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.lfs.server.test/BUCK b/org.eclipse.jgit.lfs.server.test/BUCK
new file mode 100644
index 0000000..9d6e72f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/BUCK
@@ -0,0 +1,35 @@
+TEST_BASE = ['tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java']
+TESTS = glob(['tst/**/*.java'],
+    excludes = TEST_BASE
+)
+
+for t in TESTS:
+  n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
+  java_test(
+    name = n,
+    labels = ['lfs-server'],
+    srcs = [t] + TEST_BASE,
+    deps = [
+      '//org.eclipse.jgit.lfs.test:helpers',
+      '//org.eclipse.jgit:jgit',
+      '//org.eclipse.jgit.junit:junit',
+      '//org.eclipse.jgit.junit.http:junit-http',
+      '//org.eclipse.jgit.lfs:jgit-lfs',
+      '//org.eclipse.jgit.lfs.server:jgit-lfs-server',
+      '//lib:hamcrest-core',
+      '//lib:hamcrest-library',
+      '//lib:httpcore',
+      '//lib:httpcomponents',
+      '//lib:junit',
+      '//lib/jetty:http',
+      '//lib/jetty:io',
+      '//lib/jetty:server',
+      '//lib/jetty:servlet',
+      '//lib/jetty:security',
+      '//lib/jetty:util',
+      '//lib:servlet-api',
+      '//lib:commons-logging',
+    ],
+    source_under_test = ['//org.eclipse.jgit.lfs.server:jgit-lfs-server'],
+    vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'],
+  )
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..53d74cf
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -0,0 +1,38 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
+Bundle-Version: 4.3.2.qualifier
+Bundle-Vendor: %provider_name
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: javax.servlet;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.apache.http.client.methods;version="[4.3.0,5.0.0)",
+ org.apache.http.entity;version="[4.3.0,5.0.0)",
+ org.apache.http.impl.client;version="[4.3.0,5.0.0)",
+ org.eclipse.jetty.continuation;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.http;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.io;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
+ org.eclipse.jgit.junit.http;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.test;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
+ org.hamcrest.core;version="[1.1.0,2.0.0)",
+ org.junit;version="[4.0.0,5.0.0)",
+ org.junit.runner;version="[4.0.0,5.0.0)",
+ org.junit.runners;version="[4.0.0,5.0.0)"
diff --git a/org.eclipse.jgit.lfs.server.test/build.properties b/org.eclipse.jgit.lfs.server.test/build.properties
new file mode 100644
index 0000000..9ffa0ca
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/build.properties
@@ -0,0 +1,5 @@
+source.. = tst/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties
diff --git a/org.eclipse.jgit.lfs.server.test/plugin.properties b/org.eclipse.jgit.lfs.server.test/plugin.properties
new file mode 100644
index 0000000..4a8e642
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit LFS Server Tests
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
new file mode 100644
index 0000000..05ee6f2
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2015 Matthias Sohn <matthias.sohn@sap.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>4.3.2-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
+  <name>JGit - LFS Server Tests</name>
+
+  <description>
+    Tests for the LFS server.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit.http</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>4.3.6</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <testSourceDirectory>tst/</testSourceDirectory>
+
+    <testResources>
+      <testResource>
+        <directory>tst-rsrc/</directory>
+      </testResource>
+    </testResources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>-Djava.io.tmpdir=${project.build.directory}  -Xmx300m</argLine>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/DownloadTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/DownloadTest.java
new file mode 100644
index 0000000..6c4f3cb
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/DownloadTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@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.fail;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+
+import org.apache.http.client.ClientProtocolException;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.Test;
+
+public class DownloadTest extends LfsServerTest {
+
+	@Test
+	public void testDownload() throws Exception {
+		String TEXT = "test";
+		AnyLongObjectId id = putContent(TEXT);
+		Path f = Paths.get(getTempDirectory().toString(), "download");
+		long len = getContent(id, f);
+		assertEquals(TEXT.length(), len);
+		FileUtils.delete(f.toFile(), FileUtils.RETRY);
+	}
+
+	@Test
+	public void testDownloadInvalidPathInfo()
+			throws ClientProtocolException, IOException {
+		String TEXT = "test";
+		AnyLongObjectId id = putContent(TEXT);
+		Path f = Paths.get(getTempDirectory().toString(), "download");
+		try {
+			getContent(id.name().substring(0, 60), f);
+			fail("expected RuntimeException");
+		} catch (RuntimeException e) {
+			assertEquals("Status: 400 Bad Request",
+					e.getMessage());
+		}
+	}
+
+	@Test
+	public void testDownloadInvalidId()
+			throws ClientProtocolException, IOException {
+		String TEXT = "test";
+		AnyLongObjectId id = putContent(TEXT);
+		Path f = Paths.get(getTempDirectory().toString(), "download");
+		try {
+			getContent(id.name().replace('f', 'z'), f);
+			fail("expected RuntimeException");
+		} catch (RuntimeException e) {
+			assertEquals("Status: 400 Bad Request",
+					e.getMessage());
+		}
+	}
+
+	@Test
+	public void testDownloadNotFound()
+			throws ClientProtocolException, IOException {
+		String TEXT = "test";
+		AnyLongObjectId id = LongObjectIdTestUtils.hash(TEXT);
+		Path f = Paths.get(getTempDirectory().toString(), "download");
+		try {
+			getContent(id, f);
+			fail("expected RuntimeException");
+		} catch (RuntimeException e) {
+			assertEquals("Status: 404 Not Found",
+					e.getMessage());
+		}
+	}
+
+	@SuppressWarnings("boxing")
+	@Test
+	public void testLargeFileDownload() throws Exception {
+		Path f = Paths.get(getTempDirectory().toString(), "largeRandomFile");
+		long expectedLen = createPseudoRandomContentFile(f, 5 * MiB);
+		AnyLongObjectId id = putContent(f);
+		Path f2 = Paths.get(getTempDirectory().toString(), "download");
+		long start = System.nanoTime();
+		long len = getContent(id, f2);
+		System.out.println(
+				MessageFormat.format("dowloaded 10 MiB random data in {0}ms",
+						(System.nanoTime() - start) / 1e6));
+		assertEquals(expectedLen, len);
+		FileUtils.delete(f.toFile(), FileUtils.RETRY);
+
+	}
+}
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
new file mode 100644
index 0000000..8c266d4
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@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 java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.security.DigestInputStream;
+import java.security.SecureRandom;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.junit.http.AppServer;
+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.test.LongObjectIdTestUtils;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+
+@SuppressWarnings("restriction")
+public abstract class LfsServerTest {
+
+	private static final long timeout = /* 10 sec */ 10 * 1000;
+
+	protected static final int MiB = 1024 * 1024;
+
+	/** In-memory application server; subclass must start. */
+	protected AppServer server;
+
+	private Path tmp;
+
+	private Path dir;
+
+	protected FileLfsRepository repository;
+
+	protected FileLfsServlet servlet;
+
+	public LfsServerTest() {
+		super();
+	}
+
+	public Path getTempDirectory() {
+		return tmp;
+	}
+
+	public Path getDir() {
+		return dir;
+	}
+
+	@Before
+	public void setup() throws Exception {
+		tmp = Files.createTempDirectory("jgit_test_");
+		server = new AppServer();
+		ServletContextHandler app = server.addContext("/lfs");
+		dir = Paths.get(tmp.toString(), "lfs");
+		this.repository = new FileLfsRepository(null, dir);
+		servlet = new FileLfsServlet(repository, timeout);
+		app.addServlet(new ServletHolder(servlet), "/objects/*");
+		server.setUp();
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		server.tearDown();
+		FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE | FileUtils.RETRY);
+	}
+
+	protected AnyLongObjectId putContent(String s)
+			throws IOException, ClientProtocolException {
+		AnyLongObjectId id = LongObjectIdTestUtils.hash(s);
+		return putContent(id, s);
+	}
+
+	protected AnyLongObjectId putContent(AnyLongObjectId id, String s)
+			throws ClientProtocolException, IOException {
+		try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+			HttpEntity entity = new StringEntity(s,
+					ContentType.APPLICATION_OCTET_STREAM);
+			String hexId = id.name();
+			HttpPut request = new HttpPut(
+					server.getURI() + "/lfs/objects/" + hexId);
+			request.setEntity(entity);
+			try (CloseableHttpResponse response = client.execute(request)) {
+				StatusLine statusLine = response.getStatusLine();
+				int status = statusLine.getStatusCode();
+				if (status >= 400) {
+					throw new RuntimeException("Status: " + status + ". "
+							+ statusLine.getReasonPhrase());
+				}
+			}
+			return id;
+		}
+	}
+
+	protected LongObjectId putContent(Path f)
+			throws FileNotFoundException, IOException {
+		try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+			LongObjectId id1, id2;
+			String hexId1, hexId2;
+			try (DigestInputStream in = new DigestInputStream(
+					new BufferedInputStream(Files.newInputStream(f)),
+					Constants.newMessageDigest())) {
+				InputStreamEntity entity = new InputStreamEntity(in,
+						Files.size(f), ContentType.APPLICATION_OCTET_STREAM);
+				id1 = LongObjectIdTestUtils.hash(f);
+				hexId1 = id1.name();
+				HttpPut request = new HttpPut(
+						server.getURI() + "/lfs/objects/" + hexId1);
+				request.setEntity(entity);
+				HttpResponse response = client.execute(request);
+				checkResponseStatus(response);
+				id2 = LongObjectId.fromRaw(in.getMessageDigest().digest());
+				hexId2 = id2.name();
+				assertEquals(hexId1, hexId2);
+			}
+			return id1;
+		}
+	}
+
+	private void checkResponseStatus(HttpResponse response) {
+		StatusLine statusLine = response.getStatusLine();
+		int status = statusLine.getStatusCode();
+		if (statusLine.getStatusCode() >= 400) {
+			throw new RuntimeException("Status: " + status + " "
+					+ statusLine.getReasonPhrase());
+		}
+		assertEquals(200, status);
+	}
+
+	protected long getContent(AnyLongObjectId id, Path f) throws IOException {
+		String hexId = id.name();
+		return getContent(hexId, f);
+	}
+
+	protected long getContent(String hexId, Path f) throws IOException {
+		try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+			HttpGet request = new HttpGet(
+					server.getURI() + "/lfs/objects/" + hexId);
+			HttpResponse response = client.execute(request);
+			checkResponseStatus(response);
+			HttpEntity entity = response.getEntity();
+			long pos = 0;
+			try (InputStream in = entity.getContent();
+					ReadableByteChannel inChannel = Channels.newChannel(in);
+					FileChannel outChannel = FileChannel.open(f,
+							StandardOpenOption.CREATE_NEW,
+							StandardOpenOption.WRITE)) {
+				long transferred;
+				do {
+					transferred = outChannel.transferFrom(inChannel, pos, MiB);
+					pos += transferred;
+				} while (transferred > 0);
+			}
+			return pos;
+		}
+	}
+
+	/**
+	 * Creates a file with random content, repeatedly writing a random string of
+	 * 4k length to the file until the file has at least the specified length.
+	 *
+	 * @param f
+	 *            file to fill
+	 * @param size
+	 *            size of the file to generate
+	 * @return length of the generated file in bytes
+	 * @throws IOException
+	 */
+	protected long createPseudoRandomContentFile(Path f, long size)
+			throws IOException {
+		SecureRandom rnd = new SecureRandom();
+		byte[] buf = new byte[4096];
+		rnd.nextBytes(buf);
+		ByteBuffer bytebuf = ByteBuffer.wrap(buf);
+		try (FileChannel outChannel = FileChannel.open(f,
+				StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
+			long len = 0;
+			do {
+				len += outChannel.write(bytebuf);
+				if (bytebuf.position() == 4096) {
+					bytebuf.rewind();
+				}
+			} while (len < size);
+		}
+		return Files.size(f);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java
new file mode 100644
index 0000000..1fb91bd
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@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 static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
+import org.junit.Test;
+
+public class UploadTest extends LfsServerTest {
+
+	@Test
+	public void testUpload() throws Exception {
+		String TEXT = "test";
+		AnyLongObjectId id = putContent(TEXT);
+		assertTrue("expect object " + id.name() + " to exist",
+				repository.getSize(id) >= 0);
+		assertEquals("expected object length " + TEXT.length(), TEXT.length(),
+				repository.getSize(id));
+	}
+
+	@Test
+	public void testCorruptUpload() throws Exception {
+		String TEXT = "test";
+		AnyLongObjectId id = LongObjectIdTestUtils.hash("wrongHash");
+		try {
+			putContent(id, TEXT);
+			fail("expected RuntimeException(\"Status 400\")");
+		} catch (RuntimeException e) {
+			assertEquals("Status: 400. Bad Request", e.getMessage());
+		}
+		assertFalse("expect object " + id.name() + " not to exist",
+				repository.getSize(id) >= 0);
+	}
+
+	@SuppressWarnings("boxing")
+	@Test
+	public void testLargeFileUpload() throws Exception {
+		Path f = Paths.get(getTempDirectory().toString(), "largeRandomFile");
+		createPseudoRandomContentFile(f, 5 * MiB);
+		long start = System.nanoTime();
+		LongObjectId id = putContent(f);
+		System.out.println(
+				MessageFormat.format("uploaded 10 MiB random data in {0}ms",
+						(System.nanoTime() - start) / 1e6));
+		assertTrue("expect object " + id.name() + " to exist",
+				repository.getSize(id) >= 0);
+		assertEquals("expected object length " + Files.size(f), Files.size(f),
+				repository.getSize(id));
+	}
+
+	@Test
+	public void testParallelUploads() throws Exception {
+		int count = 10;
+		List<Path> paths = new ArrayList<>(count);
+
+		for (int i = 0; i < count; i++) {
+			Path f = Paths.get(getTempDirectory().toString(),
+					"largeRandomFile_" + i);
+			createPseudoRandomContentFile(f, 1 * MiB);
+			paths.add(f);
+		}
+
+		final CyclicBarrier barrier = new CyclicBarrier(count);
+
+		ExecutorService e = Executors.newFixedThreadPool(count);
+		try {
+			for (final Path p : paths) {
+				e.submit(new Callable<Void>() {
+					@Override
+					public Void call() throws Exception {
+						barrier.await();
+						putContent(p);
+						return null;
+					}
+				});
+			}
+		} finally {
+			e.shutdown();
+			e.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.lfs.server/.classpath b/org.eclipse.jgit.lfs.server/.classpath
new file mode 100644
index 0000000..04a2be7
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.lfs.server/.fbprefs b/org.eclipse.jgit.lfs.server/.fbprefs
new file mode 100644
index 0000000..81a0767
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.fbprefs
@@ -0,0 +1,125 @@
+#FindBugs User Preferences
+#Mon May 04 16:24:13 PDT 2009
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|false
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUseObjectEquals=UseObjectEquals|false
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=default
+excludefilter0=findBugs/FindBugsExcludeFilter.xml
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
+run_at_full_build=true
diff --git a/org.eclipse.jgit.lfs.server/.gitignore b/org.eclipse.jgit.lfs.server/.gitignore
new file mode 100644
index 0000000..934e0e0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/org.eclipse.jgit.lfs.server/.project b/org.eclipse.jgit.lfs.server/.project
new file mode 100644
index 0000000..8379fea
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.lfs.server</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..99f26c0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..5a0ad22
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
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
new file mode 100644
index 0000000..ff39d16
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,398 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..c336cce
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000..3dec4d9
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000..ce7a0f0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,2 @@
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..3294d4f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,98 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..923c37f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.lfs.server/BUCK b/org.eclipse.jgit.lfs.server/BUCK
new file mode 100644
index 0000000..6b40b7c
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/BUCK
@@ -0,0 +1,22 @@
+SRCS = glob(['src/**'])
+RESOURCES = glob(['resources/**'])
+
+java_library(
+  name = 'jgit-lfs-server',
+  srcs = SRCS,
+  resources = RESOURCES,
+  deps = [
+    '//org.eclipse.jgit.http.apache:http-apache',
+    '//org.eclipse.jgit:jgit',
+    '//org.eclipse.jgit.lfs:jgit-lfs',
+    '//lib:gson',
+    '//lib:httpcore',
+    '//lib:servlet-api'
+  ],
+  visibility = ['PUBLIC'],
+)
+
+java_sources(
+  name = 'jgit-lfs-server_src',
+  srcs = SRCS + RESOURCES,
+)
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..d156900
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -0,0 +1,35 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.lfs.server
+Bundle-Version: 4.3.2.qualifier
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Export-Package: org.eclipse.jgit.lfs.server;version="4.3.2";
+  uses:="javax.servlet.http,
+   org.eclipse.jgit.lfs.lib",
+ org.eclipse.jgit.lfs.server.fs;version="4.3.2";
+  uses:="javax.servlet,
+   javax.servlet.http,
+   org.eclipse.jgit.lfs.server,
+   org.eclipse.jgit.lfs.lib",
+ org.eclipse.jgit.lfs.server.internal;version="4.3.2";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="4.3.2";
+  uses:="org.eclipse.jgit.lfs.server,
+   org.eclipse.jgit.lfs.lib"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: com.google.gson;version="[2.2.4,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.3.2,4.4.0)",
+ org.eclipse.jgit.internal;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)"
diff --git a/org.eclipse.jgit.lfs.server/about.html b/org.eclipse.jgit.lfs.server/about.html
new file mode 100644
index 0000000..01a2671
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/about.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+  body {
+    size: 8.5in 11.0in;
+    margin: 0.25in 0.5in 0.25in 0.5in;
+    tab-interval: 0.5in;
+    }
+  p {  	
+    margin-left: auto;
+    margin-top:  0.5em;
+    margin-bottom: 0.5em;
+    }
+  p.list {
+  	margin-left: 0.5in;
+    margin-top:  0.05em;
+    margin-bottom: 0.05em;
+    }
+  </style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification, 
+	are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice, 
+	this list of conditions and the following disclaimer. </li>
+<li>Redistributions in binary form must reproduce the above copyright notice, 
+	this list of conditions and the following disclaimer in the documentation 
+	and/or other materials provided with the distribution. </li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its 
+	contributors may be used to endorse or promote products derived from 
+	this software without specific prior written permission. </li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.lfs.server/build.properties b/org.eclipse.jgit.lfs.server/build.properties
new file mode 100644
index 0000000..8148271
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/build.properties
@@ -0,0 +1,7 @@
+source.. = src/,\
+           resources/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties,\
+               about.html
diff --git a/org.eclipse.jgit.lfs.server/plugin.properties b/org.eclipse.jgit.lfs.server/plugin.properties
new file mode 100644
index 0000000..59f0139
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit Large File Storage Server
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
new file mode 100644
index 0000000..0a81e33
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>4.3.2-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+  <name>JGit - Large File Storage Server</name>
+
+  <description>
+    JGit Large File Storage Server implementation.
+  </description>
+
+  <properties>
+    <translate-qualifier/>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.http.apache</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <sourceDirectory>src/</sourceDirectory>
+
+    <resources>
+      <resource>
+        <directory>.</directory>
+        <includes>
+          <include>plugin.properties</include>
+          <include>about.html</include>
+        </includes>
+      </resource>
+      <resource>
+        <directory>resources/</directory>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${bundle-manifest}</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <version>${clirr-version}</version>
+        <configuration>
+          <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
+          <minSeverity>info</minSeverity>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
diff --git a/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties b/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties
new file mode 100644
index 0000000..451064d
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties
@@ -0,0 +1,12 @@
+corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted.
+failedToCalcSignature=Failed to calculate a request signature: {0}
+invalidPathInfo=Invalid pathInfo ''{0}'' does not match ''/'{'SHA-256'}'''
+objectNotFound=Object ''{0}'' not found
+undefinedS3AccessKey=S3 configuration: ''accessKey'' is undefined
+undefinedS3Bucket=S3 configuration: ''bucket'' is undefined
+undefinedS3Region=S3 configuration: ''region'' is undefined
+undefinedS3SecretKey=S3 configuration: ''secretKey'' is undefined
+undefinedS3StorageClass=S3 configuration: ''storageClass'' is undefined
+unparsableEndpoint=Unable to parse service endpoint: {0}
+unsupportedOperation=Operation ''{0}'' is not supported
+unsupportedUtf8=UTF-8 encoding is not supported.
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java
new file mode 100644
index 0000000..3bdf8d0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.server;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+
+/**
+ * Abstraction of a repository for storing large objects
+ *
+ * @since 4.3
+ */
+public interface LargeFileRepository {
+
+	/**
+	 * @param id
+	 *            id of the object to download
+	 * @return Action for downloading the object
+	 */
+	public Response.Action getDownloadAction(AnyLongObjectId id);
+
+	/**
+	 * @param id
+	 *            id of the object to upload
+	 * @param size
+	 *            size of the object to be uploaded
+	 * @return Action for uploading the object
+	 */
+	public Response.Action getUploadAction(AnyLongObjectId id, long size);
+
+	/**
+	 * @param id
+	 *            id of the object to be verified
+	 * @return Action for verifying the object, or {@code null} if the server
+	 *         doesn't support or require verification
+	 */
+	public @Nullable Response.Action getVerifyAction(AnyLongObjectId id);
+
+	/**
+	 * @param id
+	 *            id of the object
+	 * @return length of the object content in bytes, -1 if the object doesn't
+	 *         exist
+	 * @throws IOException
+	 */
+	public long getSize(AnyLongObjectId id) throws IOException;
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsObject.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsObject.java
new file mode 100644
index 0000000..30ba22e
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsObject.java
@@ -0,0 +1,48 @@
+/*
+ * 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.server;
+
+class LfsObject {
+	String oid;
+	long size;
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
new file mode 100644
index 0000000..394137c
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
@@ -0,0 +1,126 @@
+/*
+ * 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.server;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * LFS protocol handler implementing the LFS batch API [1]
+ *
+ * [1] https://github.com/github/git-lfs/blob/master/docs/api/http-v1-batch.md
+ *
+ * @since 4.3
+ */
+public abstract class LfsProtocolServlet extends HttpServlet {
+
+	private static final long serialVersionUID = 1L;
+
+	private static final String CONTENTTYPE_VND_GIT_LFS_JSON = "application/vnd.git-lfs+json"; //$NON-NLS-1$
+
+	private Gson gson = createGson();
+
+	/**
+	 * Get the large file repository
+	 *
+	 * @return the large file repository storing large files
+	 */
+	protected abstract LargeFileRepository getLargeFileRepository();
+
+	@Override
+	protected void doPost(HttpServletRequest req, HttpServletResponse res)
+			throws ServletException, IOException {
+		res.setStatus(SC_OK);
+		res.setContentType(CONTENTTYPE_VND_GIT_LFS_JSON);
+
+		Writer w = new BufferedWriter(
+				new OutputStreamWriter(res.getOutputStream(), UTF_8));
+
+		Reader r = new BufferedReader(new InputStreamReader(req.getInputStream(), UTF_8));
+		LfsRequest request = gson.fromJson(r, LfsRequest.class);
+
+		LargeFileRepository repo = getLargeFileRepository();
+		if (repo == null) {
+			res.setStatus(SC_SERVICE_UNAVAILABLE);
+			return;
+		}
+
+		TransferHandler handler = TransferHandler
+				.forOperation(request.operation, repo, request.objects);
+		gson.toJson(handler.process(), w);
+		w.flush();
+	}
+
+	private static class LfsRequest {
+		String operation;
+
+		List<LfsObject> objects;
+	}
+
+	private static Gson createGson() {
+		GsonBuilder gb = new GsonBuilder()
+				.setFieldNamingPolicy(
+						FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+				.setPrettyPrinting().disableHtmlEscaping();
+		return gb.create();
+	}
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/Response.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/Response.java
new file mode 100644
index 0000000..dc972e0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/Response.java
@@ -0,0 +1,82 @@
+/*
+ * 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.server;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * POJOs for Gson serialization/deserialization
+ *
+ * See
+ * {@link <a href="https://github.com/github/git-lfs/tree/master/docs/api">LFS
+ * API specification</a>}
+ *
+ * @since 4.3
+ */
+public interface Response {
+	/** Describes an action the client can execute on a single object */
+	class Action {
+		public String href;
+		public Map<String, String> header;
+	}
+
+	/** Describes an error to be returned by the LFS batch API */
+	class Error {
+		public int code;
+		public String message;
+	}
+
+	/** Describes the actions the LFS server offers for a single object */
+	class ObjectInfo {
+		public String oid;
+		public long size;
+		public Map<String, Action> actions;
+		public Error error;
+	}
+
+	/** Describes the body of a LFS batch API response */
+	class Body {
+		public List<ObjectInfo> objects;
+	}
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java
new file mode 100644
index 0000000..bf5b61c
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java
@@ -0,0 +1,168 @@
+/*
+ * 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.server;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+import org.eclipse.jgit.lfs.server.Response.Action;
+import org.eclipse.jgit.lfs.server.Response.Body;
+import org.eclipse.jgit.lfs.server.internal.LfsServerText;
+
+abstract class TransferHandler {
+
+	private static final String DOWNLOAD = "download"; //$NON-NLS-1$
+	private static final String UPLOAD = "upload"; //$NON-NLS-1$
+	private static final String VERIFY = "verify"; //$NON-NLS-1$
+
+	static TransferHandler forOperation(String operation,
+			LargeFileRepository repository, List<LfsObject> objects) {
+		switch (operation) {
+		case TransferHandler.UPLOAD:
+			return new Upload(repository, objects);
+		case TransferHandler.DOWNLOAD:
+			return new Download(repository, objects);
+		case TransferHandler.VERIFY:
+		default:
+			throw new UnsupportedOperationException(MessageFormat.format(
+					LfsServerText.get().unsupportedOperation, operation));
+		}
+	}
+
+	private static class Upload extends TransferHandler {
+		Upload(LargeFileRepository repository,
+				List<LfsObject> objects) {
+			super(repository, objects);
+		}
+
+		@Override
+		Body process() throws IOException {
+			Response.Body body = new Response.Body();
+			if (objects.size() > 0) {
+				body.objects = new ArrayList<>();
+				for (LfsObject o : objects) {
+					addObjectInfo(body, o);
+				}
+			}
+			return body;
+		}
+
+		private void addObjectInfo(Response.Body body, LfsObject o)
+				throws IOException {
+			Response.ObjectInfo info = new Response.ObjectInfo();
+			body.objects.add(info);
+			info.oid = o.oid;
+			info.size = o.size;
+
+			LongObjectId oid = LongObjectId.fromString(o.oid);
+			if (repository.getSize(oid) == -1) {
+				info.actions = new HashMap<>();
+				info.actions.put(UPLOAD,
+						repository.getUploadAction(oid, o.size));
+				Action verify = repository.getVerifyAction(oid);
+				if (verify != null) {
+					info.actions.put(VERIFY, verify);
+				}
+			}
+		}
+	}
+
+	private static class Download extends TransferHandler {
+		Download(LargeFileRepository repository,
+				List<LfsObject> objects) {
+			super(repository, objects);
+		}
+
+		@Override
+		Body process() throws IOException {
+			Response.Body body = new Response.Body();
+			if (objects.size() > 0) {
+				body.objects = new ArrayList<>();
+				for (LfsObject o : objects) {
+					addObjectInfo(body, o);
+				}
+			}
+			return body;
+		}
+
+		private void addObjectInfo(Response.Body body, LfsObject o)
+				throws IOException {
+			Response.ObjectInfo info = new Response.ObjectInfo();
+			body.objects.add(info);
+			info.oid = o.oid;
+			info.size = o.size;
+
+			LongObjectId oid = LongObjectId.fromString(o.oid);
+			if (repository.getSize(oid) >= 0) {
+				info.actions = new HashMap<>();
+				info.actions.put(DOWNLOAD,
+						repository.getDownloadAction(oid));
+			} else {
+				info.error = new Response.Error();
+				info.error.code = SC_NOT_FOUND;
+				info.error.message = MessageFormat.format(
+						LfsServerText.get().objectNotFound,
+						oid.getName());
+			}
+		}
+	}
+
+	final LargeFileRepository repository;
+
+	final List<LfsObject> objects;
+
+	TransferHandler(LargeFileRepository repository,
+			List<LfsObject> objects) {
+		this.repository = repository;
+		this.objects = objects;
+	}
+
+	abstract Response.Body process() throws IOException;
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/AtomicObjectOutputStream.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/AtomicObjectOutputStream.java
new file mode 100644
index 0000000..ecc7c1f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/AtomicObjectOutputStream.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.server.fs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.security.DigestOutputStream;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lfs.errors.CorruptLongObjectException;
+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.internal.LfsServerText;
+
+/**
+ * Output stream writing content to a {@link LockFile} which is committed on
+ * close(). The stream checks if the hash of the stream content matches the
+ * id.
+ */
+class AtomicObjectOutputStream extends OutputStream {
+
+	private LockFile locked;
+
+	private DigestOutputStream out;
+
+	private boolean aborted;
+
+	private AnyLongObjectId id;
+
+	AtomicObjectOutputStream(Path path, AnyLongObjectId id)
+			throws IOException {
+		locked = new LockFile(path.toFile());
+		locked.lock();
+		this.id = id;
+		out = new DigestOutputStream(locked.getOutputStream(),
+				Constants.newMessageDigest());
+	}
+
+	@Override
+	public void write(int b) throws IOException {
+		out.write(b);
+	}
+
+	@Override
+	public void write(byte[] b) throws IOException {
+		out.write(b);
+	}
+
+	@Override
+	public void write(byte[] b, int off, int len) throws IOException {
+		out.write(b, off, len);
+	}
+
+	@Override
+	public void close() throws IOException {
+		out.close();
+		if (!aborted) {
+			verifyHash();
+			locked.commit();
+		}
+	}
+
+	private void verifyHash() {
+		AnyLongObjectId contentHash = LongObjectId
+				.fromRaw(out.getMessageDigest().digest());
+		if (!contentHash.equals(id)) {
+			abort();
+			throw new CorruptLongObjectException(id, contentHash,
+					MessageFormat.format(LfsServerText.get().corruptLongObject,
+							contentHash, id));
+		}
+	}
+
+	void abort() {
+		locked.unlock();
+		aborted = true;
+	}
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2e71c04
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.server.fs;
+
+import static org.eclipse.jgit.util.HttpSupport.HDR_AUTHORIZATION;
+
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Collections;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lfs.server.LargeFileRepository;
+import org.eclipse.jgit.lfs.server.Response;
+import org.eclipse.jgit.lfs.server.Response.Action;
+
+/**
+ * Repository storing large objects in the file system
+ *
+ * @since 4.3
+ */
+public class FileLfsRepository implements LargeFileRepository {
+
+	private final String url;
+	private final Path dir;
+
+	/**
+	 * @param url
+	 *            external URL of this repository
+	 * @param dir
+	 *            storage directory
+	 * @throws IOException
+	 */
+	public FileLfsRepository(String url, Path dir) throws IOException {
+		this.url = url;
+		this.dir = dir;
+		Files.createDirectories(dir);
+	}
+
+	@Override
+	public Response.Action getDownloadAction(AnyLongObjectId id) {
+		return getAction(id);
+	}
+
+	@Override
+	public Action getUploadAction(AnyLongObjectId id, long size) {
+		return getAction(id);
+	}
+
+	@Override
+	public @Nullable Action getVerifyAction(AnyLongObjectId id) {
+		return null;
+	}
+
+	@Override
+	public long getSize(AnyLongObjectId id) throws IOException {
+		Path p = getPath(id);
+		if (Files.exists(p)) {
+			return Files.size(p);
+		} else {
+			return -1;
+		}
+	}
+
+	/**
+	 * Get the storage directory
+	 *
+	 * @return the path of the storage directory
+	 */
+	public Path getDir() {
+		return dir;
+	}
+
+	/**
+	 * Get the path where the given object is stored
+	 *
+	 * @param id
+	 *            id of a large object
+	 * @return path the object's storage path
+	 */
+	protected Path getPath(AnyLongObjectId id) {
+		StringBuilder s = new StringBuilder(
+				Constants.LONG_OBJECT_ID_STRING_LENGTH + 6);
+		s.append(toHexCharArray(id.getFirstByte())).append('/');
+		s.append(toHexCharArray(id.getSecondByte())).append('/');
+		s.append(id.name());
+		return dir.resolve(s.toString());
+	}
+
+	private Response.Action getAction(AnyLongObjectId id) {
+		Response.Action a = new Response.Action();
+		a.href = url + id.getName();
+		a.header = Collections.singletonMap(HDR_AUTHORIZATION, "not:required"); //$NON-NLS-1$
+		return a;
+	}
+
+	ReadableByteChannel getReadChannel(AnyLongObjectId id)
+			throws IOException {
+		return FileChannel.open(getPath(id), StandardOpenOption.READ);
+	}
+
+	AtomicObjectOutputStream getOutputStream(AnyLongObjectId id)
+			throws IOException {
+		Path path = getPath(id);
+		Files.createDirectories(path.getParent());
+		return new AtomicObjectOutputStream(path, id);
+	}
+
+	private static char[] toHexCharArray(int b) {
+		final char[] dst = new char[2];
+		formatHexChar(dst, 0, b);
+		return dst;
+	}
+
+	private static final char[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
+			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+	private static void formatHexChar(final char[] dst, final int p, int b) {
+		int o = p + 1;
+		while (o >= p && b != 0) {
+			dst[o--] = hexchar[b & 0xf];
+			b >>>= 4;
+		}
+		while (o >= p)
+			dst[o--] = '0';
+	}
+}
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
new file mode 100644
index 0000000..8864af8
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.server.fs;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException;
+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.internal.LfsServerText;
+
+/**
+ * Servlet supporting upload and download of large objects as defined by the
+ * GitHub Large File Storage extension API extending git to allow separate
+ * storage of large files
+ * (https://github.com/github/git-lfs/tree/master/docs/api).
+ *
+ * @since 4.3
+ */
+@WebServlet(asyncSupported = true)
+public class FileLfsServlet extends HttpServlet {
+
+	private static final long serialVersionUID = 1L;
+
+	private final FileLfsRepository repository;
+
+	private final long timeout;
+
+	/**
+	 * @param repository
+	 *            the repository storing the large objects
+	 * @param timeout
+	 *            timeout for object upload / download in milliseconds
+	 */
+	public FileLfsServlet(FileLfsRepository repository, long timeout) {
+		this.repository = repository;
+		this.timeout = timeout;
+	}
+
+	/**
+	 * Handles object downloads
+	 *
+	 * @param req
+	 *            servlet request
+	 * @param rsp
+	 *            servlet response
+	 * @throws ServletException
+	 *             if a servlet-specific error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 */
+	@Override
+	protected void doGet(HttpServletRequest req,
+			HttpServletResponse rsp) throws ServletException, IOException {
+		AnyLongObjectId obj = getObjectToTransfer(req, rsp);
+		if (obj != null) {
+			if (repository.getSize(obj) == -1) {
+				sendError(rsp, HttpStatus.SC_NOT_FOUND, MessageFormat
+						.format(LfsServerText.get().objectNotFound, obj));
+				return;
+			}
+			AsyncContext context = req.startAsync();
+			context.setTimeout(timeout);
+			rsp.getOutputStream()
+					.setWriteListener(new ObjectDownloadListener(repository,
+							context, rsp, obj));
+		}
+	}
+
+	private AnyLongObjectId getObjectToTransfer(HttpServletRequest req,
+			HttpServletResponse rsp) throws IOException {
+		String info = req.getPathInfo();
+		if (info.length() != 1 + Constants.LONG_OBJECT_ID_STRING_LENGTH) {
+			sendError(rsp, HttpStatus.SC_BAD_REQUEST, MessageFormat
+					.format(LfsServerText.get().invalidPathInfo, info));
+			return null;
+		}
+		try {
+			return LongObjectId.fromString(info.substring(1, 65));
+		} catch (InvalidLongObjectIdException e) {
+			sendError(rsp, HttpStatus.SC_BAD_REQUEST, e.getMessage());
+			return null;
+		}
+	}
+
+	/**
+	 * Handle object uploads
+	 *
+	 * @param req
+	 *            servlet request
+	 * @param rsp
+	 *            servlet response
+	 * @throws ServletException
+	 *             if a servlet-specific error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 */
+	@Override
+	protected void doPut(HttpServletRequest req,
+			HttpServletResponse rsp) throws ServletException, IOException {
+		AnyLongObjectId id = getObjectToTransfer(req, rsp);
+		if (id != null) {
+			AsyncContext context = req.startAsync();
+			context.setTimeout(timeout);
+			req.getInputStream().setReadListener(new ObjectUploadListener(
+					repository, context, req, rsp, id));
+		}
+	}
+
+	static void sendError(HttpServletResponse rsp, int status, String message)
+			throws IOException {
+		rsp.setStatus(status);
+		// TODO return message in response body in json format as specified in
+		// https://github.com/github/git-lfs/blob/master/docs/api/http-v1-batch.md
+		rsp.flushBuffer();
+	}
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
new file mode 100644
index 0000000..bfdea4f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@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 java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.util.HttpSupport;
+
+/**
+ * Handle asynchronous large object download
+ */
+class ObjectDownloadListener implements WriteListener {
+
+	private static Logger LOG = Logger
+			.getLogger(ObjectDownloadListener.class.getName());
+
+	private final AsyncContext context;
+
+	private final HttpServletResponse response;
+
+	private final ServletOutputStream out;
+
+	private final ReadableByteChannel in;
+
+	private final WritableByteChannel outChannel;
+
+	private final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
+
+	/**
+	 * @param repository
+	 *            the repository storing large objects
+	 * @param context
+	 *            the servlet asynchronous context
+	 * @param response
+	 *            the servlet response
+	 * @param id
+	 *            id of the object to be downloaded
+	 * @throws IOException
+	 */
+	public ObjectDownloadListener(FileLfsRepository repository,
+			AsyncContext context, HttpServletResponse response,
+			AnyLongObjectId id) throws IOException {
+		this.context = context;
+		this.response = response;
+		this.in = repository.getReadChannel(id);
+		this.out = response.getOutputStream();
+		this.outChannel = Channels.newChannel(out);
+
+		response.addHeader(HttpSupport.HDR_CONTENT_LENGTH,
+				String.valueOf(repository.getSize(id)));
+		response.setContentType(Constants.HDR_APPLICATION_OCTET_STREAM);
+	}
+
+	/**
+	 * Write file content
+	 *
+	 * @throws IOException
+	 */
+	@Override
+	public void onWritePossible() throws IOException {
+		while (out.isReady()) {
+			if (in.read(buffer) != -1) {
+				buffer.flip();
+				outChannel.write(buffer);
+				buffer.compact();
+			} else {
+				in.close();
+				buffer.flip();
+				while (out.isReady()) {
+					if (buffer.hasRemaining()) {
+						outChannel.write(buffer);
+					} else {
+						context.complete();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Handle errors
+	 *
+	 * @param e
+	 *            the cause
+	 */
+	@Override
+	public void onError(Throwable e) {
+		try {
+			FileLfsServlet.sendError(response,
+					HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+			context.complete();
+			in.close();
+		} catch (IOException ex) {
+			LOG.log(Level.SEVERE, ex.getMessage(), ex);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java
new file mode 100644
index 0000000..e524ac6
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@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 java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.lfs.errors.CorruptLongObjectException;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+
+/**
+ * Handle asynchronous object upload
+ */
+class ObjectUploadListener implements ReadListener {
+
+	private static Logger LOG = Logger
+			.getLogger(ObjectUploadListener.class.getName());
+
+	private final AsyncContext context;
+
+	private final HttpServletResponse response;
+
+	private final ServletInputStream in;
+
+	private final ReadableByteChannel inChannel;
+
+	private final AtomicObjectOutputStream out;
+
+	private WritableByteChannel channel;
+
+	private final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
+
+	/**
+	 * @param repository
+	 *            the repository storing large objects
+	 * @param context
+	 * @param request
+	 * @param response
+	 * @param id
+	 * @throws FileNotFoundException
+	 * @throws IOException
+	 */
+	public ObjectUploadListener(FileLfsRepository repository,
+			AsyncContext context, HttpServletRequest request,
+			HttpServletResponse response, AnyLongObjectId id)
+					throws FileNotFoundException, IOException {
+		this.context = context;
+		this.response = response;
+		this.in = request.getInputStream();
+		this.inChannel = Channels.newChannel(in);
+		this.out = repository.getOutputStream(id);
+		this.channel = Channels.newChannel(out);
+		response.setContentType(Constants.CONTENT_TYPE_GIT_LFS_JSON);
+	}
+
+	/**
+	 * Writes all the received data to the output channel
+	 *
+	 * @throws IOException
+	 */
+	@Override
+	public void onDataAvailable() throws IOException {
+		while (in.isReady()) {
+			if (inChannel.read(buffer) > 0) {
+				buffer.flip();
+				channel.write(buffer);
+				buffer.compact();
+			} else {
+				buffer.flip();
+				while (buffer.hasRemaining()) {
+					channel.write(buffer);
+				}
+				close();
+				return;
+			}
+		}
+	}
+
+	/**
+	 * @throws IOException
+	 */
+	@Override
+	public void onAllDataRead() throws IOException {
+		close();
+	}
+
+	protected void close() throws IOException {
+		try {
+			inChannel.close();
+			channel.close();
+			// TODO check if status 200 is ok for PUT request, HTTP foresees 204
+			// for successful PUT without response body
+			response.setStatus(HttpServletResponse.SC_OK);
+		} finally {
+			context.complete();
+		}
+	}
+
+	/**
+	 * @param e
+	 *            the exception that caused the problem
+	 */
+	@Override
+	public void onError(Throwable e) {
+		try {
+			out.abort();
+			inChannel.close();
+			channel.close();
+			int status;
+			if (e instanceof CorruptLongObjectException) {
+				status = HttpStatus.SC_BAD_REQUEST;
+				LOG.log(Level.WARNING, e.getMessage(), e);
+			} else {
+				status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
+				LOG.log(Level.SEVERE, e.getMessage(), e);
+			}
+			FileLfsServlet.sendError(response, status, e.getMessage());
+		} catch (IOException ex) {
+			LOG.log(Level.SEVERE, ex.getMessage(), ex);
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.java
new file mode 100644
index 0000000..c5ad53a
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.server.internal;
+
+import org.eclipse.jgit.nls.NLS;
+import org.eclipse.jgit.nls.TranslationBundle;
+
+/**
+ * Translation bundle for JGit LFS server
+ */
+public class LfsServerText extends TranslationBundle {
+
+	/**
+	 * @return an instance of this translation bundle
+	 */
+	public static LfsServerText get() {
+		return NLS.getBundleFor(LfsServerText.class);
+	}
+
+	// @formatter:off
+	/***/ public String corruptLongObject;
+	/***/ public String failedToCalcSignature;
+	/***/ public String invalidPathInfo;
+	/***/ public String objectNotFound;
+	/***/ public String undefinedS3AccessKey;
+	/***/ public String undefinedS3Bucket;
+	/***/ public String undefinedS3Region;
+	/***/ public String undefinedS3SecretKey;
+	/***/ public String undefinedS3StorageClass;
+	/***/ public String unparsableEndpoint;
+	/***/ public String unsupportedOperation;
+	/***/ public String unsupportedUtf8;
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Config.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Config.java
new file mode 100644
index 0000000..4876fb8
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Config.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@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.server.s3;
+
+/**
+ * Configuration for an Amazon AWS S3 bucket
+ *
+ * @since 4.3
+ */
+public class S3Config {
+	private final String region;
+	private final String bucket;
+	private final String storageClass;
+	private final String accessKey;
+	private final String secretKey;
+	private final int expirationSeconds;
+	private final boolean disableSslVerify;
+
+	/**
+	 * @param region
+	 *            AWS region
+	 * @param bucket
+	 *            S3 storage bucket
+	 * @param storageClass
+	 *            S3 storage class
+	 * @param accessKey
+	 *            access key for authenticating to AWS
+	 * @param secretKey
+	 *            secret key for authenticating to AWS
+	 * @param expirationSeconds
+	 *            period in seconds after which requests signed for this bucket
+	 *            will expire
+	 * @param disableSslVerify
+	 *            if {@code true} disable Amazon server certificate and hostname
+	 *            verification
+	 */
+	public S3Config(String region, String bucket, String storageClass,
+			String accessKey, String secretKey, int expirationSeconds,
+			boolean disableSslVerify) {
+		this.region = region;
+		this.bucket = bucket;
+		this.storageClass = storageClass;
+		this.accessKey = accessKey;
+		this.secretKey = secretKey;
+		this.expirationSeconds = expirationSeconds;
+		this.disableSslVerify = disableSslVerify;
+	}
+
+	/**
+	 * @return Get name of AWS region this bucket resides in
+	 */
+	public String getRegion() {
+		return region;
+	}
+
+	/**
+	 * @return Get S3 storage bucket name
+	 */
+	public String getBucket() {
+		return bucket;
+	}
+
+	/**
+	 * @return S3 storage class to use for objects stored in this bucket
+	 */
+	public String getStorageClass() {
+		return storageClass;
+	}
+
+	/**
+	 * @return access key for authenticating to AWS
+	 */
+	public String getAccessKey() {
+		return accessKey;
+	}
+
+	/**
+	 * @return secret key for authenticating to AWS
+	 */
+	public String getSecretKey() {
+		return secretKey;
+	}
+
+	/**
+	 * @return period in seconds after which requests signed for this bucket
+	 *         will expire
+	 */
+	public int getExpirationSeconds() {
+		return expirationSeconds;
+	}
+
+	/**
+	 * @return {@code true} if Amazon server certificate and hostname
+	 *         verification is disabled
+	 */
+	boolean isDisableSslVerify() {
+		return disableSslVerify;
+	}
+
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java
new file mode 100644
index 0000000..c229758
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@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.server.s3;
+
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static org.eclipse.jgit.lfs.server.s3.SignerV4.UNSIGNED_PAYLOAD;
+import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_CONTENT_SHA256;
+import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_EXPIRES;
+import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_STORAGE_CLASS;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
+import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
+import static org.eclipse.jgit.util.HttpSupport.METHOD_HEAD;
+import static org.eclipse.jgit.util.HttpSupport.METHOD_PUT;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.server.LargeFileRepository;
+import org.eclipse.jgit.lfs.server.Response;
+import org.eclipse.jgit.lfs.server.Response.Action;
+import org.eclipse.jgit.lfs.server.internal.LfsServerText;
+import org.eclipse.jgit.transport.http.HttpConnection;
+import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
+import org.eclipse.jgit.util.HttpSupport;
+
+/**
+ * Repository storing LFS objects in Amazon S3
+ *
+ * @since 4.3
+ */
+public class S3Repository implements LargeFileRepository {
+
+	private S3Config s3Config;
+
+	/**
+	 * Construct a LFS repository storing large objects in Amazon S3
+	 *
+	 * @param config
+	 *            AWS S3 storage bucket configuration
+	 */
+	public S3Repository(S3Config config) {
+		validateConfig(config);
+		this.s3Config = config;
+	}
+
+	@Override
+	public Response.Action getDownloadAction(AnyLongObjectId oid) {
+		URL endpointUrl = getObjectUrl(oid);
+		Map<String, String> queryParams = new HashMap<String, String>();
+		queryParams.put(X_AMZ_EXPIRES,
+				Integer.toString(s3Config.getExpirationSeconds()));
+		Map<String, String> headers = new HashMap<String, String>();
+		String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
+				s3Config, endpointUrl, METHOD_GET, headers, queryParams,
+				UNSIGNED_PAYLOAD);
+
+		Response.Action a = new Response.Action();
+		a.href = endpointUrl.toString() + "?" + authorizationQueryParameters; //$NON-NLS-1$
+		return a;
+	}
+
+	@Override
+	public Response.Action getUploadAction(AnyLongObjectId oid, long size) {
+		cacheObjectMetaData(oid, size);
+		URL objectUrl = getObjectUrl(oid);
+		Map<String, String> headers = new HashMap<String, String>();
+		headers.put(X_AMZ_CONTENT_SHA256, oid.getName());
+		headers.put(HDR_CONTENT_LENGTH, Long.toString(size));
+		headers.put(X_AMZ_STORAGE_CLASS, s3Config.getStorageClass());
+		headers.put(HttpSupport.HDR_CONTENT_TYPE, "application/octet-stream"); //$NON-NLS-1$
+		headers = SignerV4.createHeaderAuthorization(s3Config, objectUrl,
+				METHOD_PUT, headers, oid.getName());
+
+		Response.Action a = new Response.Action();
+		a.href = objectUrl.toString();
+		a.header = new HashMap<>();
+		a.header.putAll(headers);
+		return a;
+	}
+
+	@Override
+	public Action getVerifyAction(AnyLongObjectId id) {
+		return null; // TODO(ms) implement this
+	}
+
+	@Override
+	public long getSize(AnyLongObjectId oid) throws IOException {
+		URL endpointUrl = getObjectUrl(oid);
+		Map<String, String> queryParams = new HashMap<String, String>();
+		queryParams.put(X_AMZ_EXPIRES,
+				Integer.toString(s3Config.getExpirationSeconds()));
+		Map<String, String> headers = new HashMap<String, String>();
+
+		String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
+				s3Config, endpointUrl, METHOD_HEAD, headers, queryParams,
+				UNSIGNED_PAYLOAD);
+		String href = endpointUrl.toString() + "?" //$NON-NLS-1$
+				+ authorizationQueryParameters;
+
+		Proxy proxy = HttpSupport.proxyFor(ProxySelector.getDefault(),
+				endpointUrl);
+		HttpClientConnectionFactory f = new HttpClientConnectionFactory();
+		HttpConnection conn = f.create(new URL(href), proxy);
+		if (s3Config.isDisableSslVerify()) {
+			HttpSupport.disableSslVerify(conn);
+		}
+		conn.setRequestMethod(METHOD_HEAD);
+		conn.connect();
+		int status = conn.getResponseCode();
+		if (status == SC_OK) {
+			String contentLengthHeader = conn
+					.getHeaderField(HDR_CONTENT_LENGTH);
+			if (contentLengthHeader != null) {
+				return Integer.parseInt(contentLengthHeader);
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Cache metadata (size) for an object to avoid extra roundtrip to S3 in
+	 * order to retrieve this metadata for a given object. Subclasses can
+	 * implement a local cache and override {{@link #getSize(AnyLongObjectId)}
+	 * to retrieve the object size from the local cache to eliminate the need
+	 * for another roundtrip to S3
+	 *
+	 * @param oid
+	 *            the object id identifying the object to be cached
+	 * @param size
+	 *            the object's size (in bytes)
+	 */
+	protected void cacheObjectMetaData(AnyLongObjectId oid, long size) {
+		// no caching
+	}
+
+	private void validateConfig(S3Config config) {
+		assertNotEmpty(LfsServerText.get().undefinedS3AccessKey,
+				config.getAccessKey());
+		assertNotEmpty(LfsServerText.get().undefinedS3Bucket,
+				config.getBucket());
+		assertNotEmpty(LfsServerText.get().undefinedS3Region,
+				config.getRegion());
+		assertNotEmpty(LfsServerText.get().undefinedS3SecretKey,
+				config.getSecretKey());
+		assertNotEmpty(LfsServerText.get().undefinedS3StorageClass,
+				config.getStorageClass());
+	}
+
+	private void assertNotEmpty(String message, String value) {
+		if (value == null || value.trim().length() == 0) {
+			throw new IllegalArgumentException(message);
+		}
+	}
+
+	private URL getObjectUrl(AnyLongObjectId oid) {
+		try {
+			return new URL(String.format("https://s3-%s.amazonaws.com/%s/%s", //$NON-NLS-1$
+					s3Config.getRegion(), s3Config.getBucket(),
+					getPath(oid)));
+		} catch (MalformedURLException e) {
+			throw new IllegalArgumentException(MessageFormat.format(
+					LfsServerText.get().unparsableEndpoint, e.getMessage()));
+		}
+	}
+
+	private String getPath(AnyLongObjectId oid) {
+		return oid.getName();
+	}
+}
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
new file mode 100644
index 0000000..08bb4b9
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@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.server.s3;
+
+import static org.eclipse.jgit.util.HttpSupport.HDR_AUTHORIZATION;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SimpleTimeZone;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lfs.server.internal.LfsServerText;
+
+/**
+ * Signing support for Amazon AWS signing V4
+ * <p>
+ * See
+ * http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
+ */
+class SignerV4 {
+	static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD"; //$NON-NLS-1$
+
+	private static final String ALGORITHM = "HMAC-SHA256"; //$NON-NLS-1$
+	private static final String DATE_STRING_FORMAT = "yyyyMMdd"; //$NON-NLS-1$
+	private static final String HEX = "0123456789abcdef"; //$NON-NLS-1$
+	private static final String HMACSHA256 = "HmacSHA256"; //$NON-NLS-1$
+	private static final String ISO8601_BASIC_FORMAT = "yyyyMMdd'T'HHmmss'Z'"; //$NON-NLS-1$
+	private static final String S3 = "s3"; //$NON-NLS-1$
+	private static final String SCHEME = "AWS4"; //$NON-NLS-1$
+	private static final String TERMINATOR = "aws4_request"; //$NON-NLS-1$
+	private static final String UTC = "UTC"; //$NON-NLS-1$
+	private static final String X_AMZ_ALGORITHM = "X-Amz-Algorithm"; //$NON-NLS-1$
+	private static final String X_AMZ_CREDENTIAL = "X-Amz-Credential"; //$NON-NLS-1$
+	private static final String X_AMZ_DATE = "X-Amz-Date"; //$NON-NLS-1$
+	private static final String X_AMZ_SIGNATURE = "X-Amz-Signature"; //$NON-NLS-1$
+	private static final String X_AMZ_SIGNED_HEADERS = "X-Amz-SignedHeaders"; //$NON-NLS-1$
+
+	static final String X_AMZ_CONTENT_SHA256 = "x-amz-content-sha256"; //$NON-NLS-1$
+	static final String X_AMZ_EXPIRES = "X-Amz-Expires"; //$NON-NLS-1$
+	static final String X_AMZ_STORAGE_CLASS = "x-amz-storage-class"; //$NON-NLS-1$
+
+	/**
+	 * Create an AWSV4 authorization for a request, suitable for embedding in
+	 * query parameters.
+	 *
+	 * @param bucketConfig
+	 *            configuration of S3 storage bucket this request should be
+	 *            signed for
+	 * @param url
+	 *            HTTP request URL
+	 * @param httpMethod
+	 *            HTTP method
+	 * @param headers
+	 *            The HTTP request headers; 'Host' and 'X-Amz-Date' will be
+	 *            added to this set.
+	 * @param queryParameters
+	 *            Any query parameters that will be added to the endpoint. The
+	 *            parameters should be specified in canonical format.
+	 * @param bodyHash
+	 *            Pre-computed SHA256 hash of the request body content; this
+	 *            value should also be set as the header 'X-Amz-Content-SHA256'
+	 *            for non-streaming uploads.
+	 * @return The computed authorization string for the request. This value
+	 *         needs to be set as the header 'Authorization' on the subsequent
+	 *         HTTP request.
+	 */
+	static String createAuthorizationQuery(S3Config bucketConfig, URL url,
+			String httpMethod, Map<String, String> headers,
+			Map<String, String> queryParameters, String bodyHash) {
+		addHostHeader(url, headers);
+
+		queryParameters.put(X_AMZ_ALGORITHM, SCHEME + "-" + ALGORITHM); //$NON-NLS-1$
+
+		Date now = new Date();
+		String dateStamp = dateStamp(now);
+		String scope = scope(bucketConfig.getRegion(), dateStamp);
+		queryParameters.put(X_AMZ_CREDENTIAL,
+				bucketConfig.getAccessKey() + "/" + scope); //$NON-NLS-1$
+
+		String dateTimeStampISO8601 = dateTimeStampISO8601(now);
+		queryParameters.put(X_AMZ_DATE, dateTimeStampISO8601);
+
+		String canonicalizedHeaderNames = canonicalizeHeaderNames(headers);
+		queryParameters.put(X_AMZ_SIGNED_HEADERS, canonicalizedHeaderNames);
+
+		String canonicalizedQueryParameters = canonicalizeQueryString(
+				queryParameters);
+		String canonicalizedHeaders = canonicalizeHeaderString(headers);
+		String canonicalRequest = canonicalRequest(url, httpMethod,
+				canonicalizedQueryParameters, canonicalizedHeaderNames,
+				canonicalizedHeaders, bodyHash);
+		byte[] signature = createSignature(bucketConfig, dateTimeStampISO8601,
+				dateStamp, scope, canonicalRequest);
+		queryParameters.put(X_AMZ_SIGNATURE, toHex(signature));
+
+		return formatAuthorizationQuery(queryParameters);
+	}
+
+	private static String formatAuthorizationQuery(
+			Map<String, String> queryParameters) {
+		StringBuilder s = new StringBuilder();
+		for (String key : queryParameters.keySet()) {
+			appendQuery(s, key, queryParameters.get(key));
+		}
+		return s.toString();
+	}
+
+	private static void appendQuery(StringBuilder s, String key,
+			String value) {
+		if (s.length() != 0) {
+			s.append("&"); //$NON-NLS-1$
+		}
+		s.append(key).append("=").append(value); //$NON-NLS-1$
+	}
+
+	/**
+	 * Sign headers for given bucket, url and HTTP method and add signature in
+	 * Authorization header.
+	 *
+	 * @param bucketConfig
+	 *            configuration of S3 storage bucket this request should be
+	 *            signed for
+	 * @param url
+	 *            HTTP request URL
+	 * @param httpMethod
+	 *            HTTP method
+	 * @param headers
+	 *            HTTP headers to sign
+	 * @param bodyHash
+	 *            Pre-computed SHA256 hash of the request body content; this
+	 *            value should also be set as the header 'X-Amz-Content-SHA256'
+	 *            for non-streaming uploads.
+	 * @return HTTP headers signd by an Authorization header added to the
+	 *         headers
+	 */
+	static Map<String, String> createHeaderAuthorization(
+			S3Config bucketConfig, URL url, String httpMethod,
+			Map<String, String> headers, String bodyHash) {
+		addHostHeader(url, headers);
+
+		Date now = new Date();
+		String dateTimeStamp = dateTimeStampISO8601(now);
+		headers.put(X_AMZ_DATE, dateTimeStamp);
+
+		String canonicalizedHeaderNames = canonicalizeHeaderNames(headers);
+		String canonicalizedHeaders = canonicalizeHeaderString(headers);
+		String canonicalRequest = canonicalRequest(url, httpMethod, "", //$NON-NLS-1$
+				canonicalizedHeaderNames, canonicalizedHeaders, bodyHash);
+		String dateStamp = dateStamp(now);
+		String scope = scope(bucketConfig.getRegion(), dateStamp);
+
+		byte[] signature = createSignature(bucketConfig, dateTimeStamp,
+				dateStamp, scope, canonicalRequest);
+
+		headers.put(HDR_AUTHORIZATION, formatAuthorizationHeader(bucketConfig,
+				canonicalizedHeaderNames, scope, signature)); // $NON-NLS-1$
+
+		return headers;
+	}
+
+	private static String formatAuthorizationHeader(
+			S3Config bucketConfig, String canonicalizedHeaderNames,
+			String scope, byte[] signature) {
+		StringBuilder s = new StringBuilder();
+		s.append(SCHEME).append("-").append(ALGORITHM).append(" "); //$NON-NLS-1$ //$NON-NLS-2$
+		s.append("Credential=").append(bucketConfig.getAccessKey()).append("/") //$NON-NLS-1$//$NON-NLS-2$
+				.append(scope).append(","); //$NON-NLS-1$
+		s.append("SignedHeaders=").append(canonicalizedHeaderNames).append(","); //$NON-NLS-1$ //$NON-NLS-2$
+		s.append("Signature=").append(toHex(signature)); //$NON-NLS-1$
+		return s.toString();
+	}
+
+	private static void addHostHeader(URL url,
+			Map<String, String> headers) {
+		String hostHeader = url.getHost();
+		int port = url.getPort();
+		if (port > -1) {
+			hostHeader.concat(":" + Integer.toString(port)); //$NON-NLS-1$
+		}
+		headers.put("Host", hostHeader); //$NON-NLS-1$
+	}
+
+	private static String canonicalizeHeaderNames(
+			Map<String, String> headers) {
+		List<String> sortedHeaders = new ArrayList<String>();
+		sortedHeaders.addAll(headers.keySet());
+		Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER);
+
+		StringBuilder buffer = new StringBuilder();
+		for (String header : sortedHeaders) {
+			if (buffer.length() > 0)
+				buffer.append(";"); //$NON-NLS-1$
+			buffer.append(header.toLowerCase());
+		}
+
+		return buffer.toString();
+	}
+
+	private static String canonicalizeHeaderString(
+			Map<String, String> headers) {
+		if (headers == null || headers.isEmpty()) {
+			return ""; //$NON-NLS-1$
+		}
+
+		List<String> sortedHeaders = new ArrayList<String>();
+		sortedHeaders.addAll(headers.keySet());
+		Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER);
+
+		StringBuilder buffer = new StringBuilder();
+		for (String key : sortedHeaders) {
+			buffer.append(key.toLowerCase().replaceAll("\\s+", " ") + ":" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					+ headers.get(key).replaceAll("\\s+", " ")); //$NON-NLS-1$//$NON-NLS-2$
+			buffer.append("\n"); //$NON-NLS-1$
+		}
+
+		return buffer.toString();
+	}
+
+	private static String dateStamp(Date now) {
+		// TODO(ms) cache and reuse DateFormat instances
+		SimpleDateFormat dateStampFormat = new SimpleDateFormat(
+				DATE_STRING_FORMAT);
+		dateStampFormat.setTimeZone(new SimpleTimeZone(0, UTC));
+		String dateStamp = dateStampFormat.format(now);
+		return dateStamp;
+	}
+
+	private static String dateTimeStampISO8601(Date now) {
+		// TODO(ms) cache and reuse DateFormat instances
+		SimpleDateFormat dateTimeFormat = new SimpleDateFormat(
+				ISO8601_BASIC_FORMAT);
+		dateTimeFormat.setTimeZone(new SimpleTimeZone(0, UTC));
+		String dateTimeStamp = dateTimeFormat.format(now);
+		return dateTimeStamp;
+	}
+
+	private static String scope(String region, String dateStamp) {
+		String scope = String.format("%s/%s/%s/%s", dateStamp, region, S3, //$NON-NLS-1$
+				TERMINATOR);
+		return scope;
+	}
+
+	private static String canonicalizeQueryString(
+			Map<String, String> parameters) {
+		if (parameters == null || parameters.isEmpty()) {
+			return ""; //$NON-NLS-1$
+		}
+
+		SortedMap<String, String> sorted = new TreeMap<String, String>();
+
+		Iterator<Map.Entry<String, String>> pairs = parameters.entrySet()
+				.iterator();
+		while (pairs.hasNext()) {
+			Map.Entry<String, String> pair = pairs.next();
+			String key = pair.getKey();
+			String value = pair.getValue();
+			sorted.put(urlEncode(key, false), urlEncode(value, false));
+		}
+
+		StringBuilder builder = new StringBuilder();
+		pairs = sorted.entrySet().iterator();
+		while (pairs.hasNext()) {
+			Map.Entry<String, String> pair = pairs.next();
+			builder.append(pair.getKey());
+			builder.append("="); //$NON-NLS-1$
+			builder.append(pair.getValue());
+			if (pairs.hasNext()) {
+				builder.append("&"); //$NON-NLS-1$
+			}
+		}
+
+		return builder.toString();
+	}
+
+	private static String canonicalRequest(URL endpoint, String httpMethod,
+			String queryParameters, String canonicalizedHeaderNames,
+			String canonicalizedHeaders, String bodyHash) {
+		return String.format("%s\n%s\n%s\n%s\n%s\n%s", //$NON-NLS-1$
+				httpMethod, canonicalizeResourcePath(endpoint),
+				queryParameters, canonicalizedHeaders, canonicalizedHeaderNames,
+				bodyHash);
+	}
+
+	private static String canonicalizeResourcePath(URL endpoint) {
+		if (endpoint == null) {
+			return "/"; //$NON-NLS-1$
+		}
+		String path = endpoint.getPath();
+		if (path == null || path.isEmpty()) {
+			return "/"; //$NON-NLS-1$
+		}
+
+		String encodedPath = urlEncode(path, true);
+		if (encodedPath.startsWith("/")) { //$NON-NLS-1$
+			return encodedPath;
+		} else {
+			return "/" + encodedPath; //$NON-NLS-1$
+		}
+	}
+
+	private static byte[] hash(String s) {
+		MessageDigest md = Constants.newMessageDigest();
+		md.update(s.getBytes(StandardCharsets.UTF_8));
+		return md.digest();
+	}
+
+	private static byte[] sign(String stringData, byte[] key) {
+		try {
+			byte[] data = stringData.getBytes("UTF-8"); //$NON-NLS-1$
+			Mac mac = Mac.getInstance(HMACSHA256);
+			mac.init(new SecretKeySpec(key, HMACSHA256));
+			return mac.doFinal(data);
+		} catch (Exception e) {
+			throw new RuntimeException(MessageFormat.format(
+					LfsServerText.get().failedToCalcSignature, e.getMessage()),
+					e);
+		}
+	}
+
+	private static String stringToSign(String scheme, String algorithm,
+			String dateTime, String scope, String canonicalRequest) {
+		return String.format("%s-%s\n%s\n%s\n%s", //$NON-NLS-1$
+				scheme, algorithm, dateTime, scope,
+				toHex(hash(canonicalRequest)));
+	}
+
+	private static String toHex(byte[] bytes) {
+		StringBuilder builder = new StringBuilder(2 * bytes.length);
+		for (byte b : bytes) {
+			builder.append(HEX.charAt((b & 0xF0) >> 4));
+			builder.append(HEX.charAt(b & 0xF));
+		}
+		return builder.toString();
+	}
+
+	private static String urlEncode(String url, boolean keepPathSlash) {
+		String encoded;
+		try {
+			encoded = URLEncoder.encode(url, StandardCharsets.UTF_8.name());
+		} catch (UnsupportedEncodingException e) {
+			throw new RuntimeException(LfsServerText.get().unsupportedUtf8, e);
+		}
+		if (keepPathSlash) {
+			encoded = encoded.replace("%2F", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		return encoded;
+	}
+
+	private static byte[] createSignature(S3Config bucketConfig,
+			String dateTimeStamp, String dateStamp,
+			String scope, String canonicalRequest) {
+		String stringToSign = stringToSign(SCHEME, ALGORITHM, dateTimeStamp,
+				scope, canonicalRequest);
+
+		byte[] signature = (SCHEME + bucketConfig.getSecretKey()).getBytes();
+		signature = sign(dateStamp, signature);
+		signature = sign(bucketConfig.getRegion(), signature);
+		signature = sign(S3, signature);
+		signature = sign(TERMINATOR, signature);
+		signature = sign(stringToSign, signature);
+		return signature;
+	}
+}
diff --git a/org.eclipse.jgit.lfs.test/.classpath b/org.eclipse.jgit.lfs.test/.classpath
new file mode 100644
index 0000000..efc781d
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="tst"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.lfs.test/.gitignore b/org.eclipse.jgit.lfs.test/.gitignore
new file mode 100644
index 0000000..4dc0091
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.gitignore
@@ -0,0 +1,2 @@
+/target
+/bin
diff --git a/org.eclipse.jgit.lfs.test/.project b/org.eclipse.jgit.lfs.test/.project
new file mode 100644
index 0000000..c5dddb0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.lfs.test</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..4824b80
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1

+encoding/<project>=UTF-8

diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..5a0ad22
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
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
new file mode 100644
index 0000000..dcc0d3a
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,398 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..c336cce
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000..3dec4d9
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000..ce7a0f0
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,2 @@
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..d585687
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,93 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..923c37f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.lfs.test/BUCK b/org.eclipse.jgit.lfs.test/BUCK
new file mode 100644
index 0000000..d0fb96f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/BUCK
@@ -0,0 +1,31 @@
+TESTS = glob(['tst/**/*.java'])
+
+for t in TESTS:
+  n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
+  java_test(
+    name = n,
+    labels = ['lfs'],
+    srcs = [t],
+    deps = [
+      ':helpers',
+      '//org.eclipse.jgit.junit:junit',
+      '//org.eclipse.jgit.lfs:jgit-lfs',
+      '//org.eclipse.jgit:jgit',
+      '//lib:hamcrest-core',
+      '//lib:hamcrest-library',
+      '//lib:junit',
+    ],
+    source_under_test = ['//org.eclipse.jgit.lfs:jgit-lfs'],
+  )
+
+java_library(
+  name = 'helpers',
+  srcs = glob(['src/**/*.java']),
+  deps = [
+    '//org.eclipse.jgit:jgit',
+    '//org.eclipse.jgit.lfs:jgit-lfs',
+    '//org.eclipse.jgit.junit:junit',
+    '//lib:junit',
+  ],
+  visibility = ['PUBLIC']
+)
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..173b0eb
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.lfs.test
+Bundle-Version: 4.3.2.qualifier
+Bundle-Vendor: %provider_name
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.junit;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
+ org.hamcrest.core;version="[1.1.0,2.0.0)",
+ org.junit;version="[4.0.0,5.0.0)",
+ org.junit.runner;version="[4.0.0,5.0.0)",
+ org.junit.runners;version="[4.0.0,5.0.0)"
+Export-Package: org.eclipse.jgit.lfs.test;version="4.3.2";x-friends:="org.eclipse.jgit.lfs.server.test"
+
diff --git a/org.eclipse.jgit.lfs.test/build.properties b/org.eclipse.jgit.lfs.test/build.properties
new file mode 100644
index 0000000..47ae091
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/build.properties
@@ -0,0 +1,5 @@
+source.. = tst/, src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties
diff --git a/org.eclipse.jgit.lfs.test/plugin.properties b/org.eclipse.jgit.lfs.test/plugin.properties
new file mode 100644
index 0000000..f900780
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit LFS Tests
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
new file mode 100644
index 0000000..8e5d28b
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2015 Matthias Sohn <matthias.sohn@sap.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>4.3.2-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.eclipse.jgit.lfs.test</artifactId>
+  <name>JGit - Large File Storage Tests</name>
+
+  <description>
+    Tests for the Large File Extension (LFS).
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <testSourceDirectory>tst/</testSourceDirectory>
+    <sourceDirectory>src/</sourceDirectory>
+
+    <testResources>
+      <testResource>
+        <directory>tst-rsrc/</directory>
+      </testResource>
+    </testResources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <argLine>-Djava.io.tmpdir=${project.build.directory}  -Xmx300m</argLine>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.eclipse.jgit.lfs.test/src/org/eclipse/jgit/lfs/test/LongObjectIdTestUtils.java b/org.eclipse.jgit.lfs.test/src/org/eclipse/jgit/lfs/test/LongObjectIdTestUtils.java
new file mode 100644
index 0000000..6c6400f
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/src/org/eclipse/jgit/lfs/test/LongObjectIdTestUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.test;
+
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.MessageDigest;
+
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+
+public class LongObjectIdTestUtils {
+
+	/**
+	 * Create id as hash of the given string.
+	 *
+	 * @param s
+	 *            the string to hash
+	 * @return id calculated by hashing string
+	 */
+	public static LongObjectId hash(String s) {
+		MessageDigest md = Constants.newMessageDigest();
+		md.update(s.getBytes(StandardCharsets.UTF_8));
+		return LongObjectId.fromRaw(md.digest());
+	}
+
+	/**
+	 * Create id as hash of a file content
+	 *
+	 * @param file
+	 *            the file to hash
+	 * @return id calculated by hashing file content
+	 * @throws FileNotFoundException
+	 *             if file doesn't exist
+	 * @throws IOException
+	 */
+	public static LongObjectId hash(Path file)
+			throws FileNotFoundException, IOException {
+		MessageDigest md = Constants.newMessageDigest();
+		try (InputStream is = new BufferedInputStream(
+				Files.newInputStream(file))) {
+			final byte[] buffer = new byte[4096];
+			for (int read = 0; (read = is.read(buffer)) != -1;) {
+				md.update(buffer, 0, read);
+			}
+		}
+		return LongObjectId.fromRaw(md.digest());
+	}
+}
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectIdTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectIdTest.java
new file mode 100644
index 0000000..8938a78
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectIdTest.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.lib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
+import org.junit.Test;
+
+/*
+ * Ported to SHA-256 from org.eclipse.jgit.lib.AbbreviatedObjectIdTest
+ */
+public class AbbreviatedLongObjectIdTest {
+	@Test
+	public void testEmpty_FromByteArray() {
+		final AbbreviatedLongObjectId i;
+		i = AbbreviatedLongObjectId.fromString(new byte[] {}, 0, 0);
+		assertNotNull(i);
+		assertEquals(0, i.length());
+		assertFalse(i.isComplete());
+		assertEquals("", i.name());
+	}
+
+	@Test
+	public void testEmpty_FromString() {
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId
+				.fromString("");
+		assertNotNull(i);
+		assertEquals(0, i.length());
+		assertFalse(i.isComplete());
+		assertEquals("", i.name());
+	}
+
+	@Test
+	public void testFull_FromByteArray() {
+		final String s = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final byte[] b = org.eclipse.jgit.lib.Constants.encodeASCII(s);
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(b,
+				0, b.length);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertTrue(i.isComplete());
+		assertEquals(s, i.name());
+
+		final LongObjectId f = i.toLongObjectId();
+		assertNotNull(f);
+		assertEquals(LongObjectId.fromString(s), f);
+		assertEquals(f.hashCode(), i.hashCode());
+	}
+
+	@Test
+	public void testFull_FromString() {
+		final String s = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertTrue(i.isComplete());
+		assertEquals(s, i.name());
+
+		final LongObjectId f = i.toLongObjectId();
+		assertNotNull(f);
+		assertEquals(LongObjectId.fromString(s), f);
+		assertEquals(f.hashCode(), i.hashCode());
+	}
+
+	@Test
+	public void test1_FromString() {
+		final String s = "2";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test2_FromString() {
+		final String s = "27";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test3_FromString() {
+		final String s = "27e";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test4_FromString() {
+		final String s = "27e1";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test5_FromString() {
+		final String s = "27e15";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test6_FromString() {
+		final String s = "27e15b";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test7_FromString() {
+		final String s = "27e15b7";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test8_FromString() {
+		final String s = "27e15b72";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test9_FromString() {
+		final String s = "27e15b729";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test15_FromString() {
+		final String s = "27e15b72937fc8f";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test16_FromString() {
+		final String s = "27e15b72937fc8f5";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test17_FromString() {
+		final String s = "27e15b72937fc8f55";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void test33_FromString() {
+		final String s = "27e15b72937fc8f558da24ac3d50ec203";
+		final AbbreviatedLongObjectId i = AbbreviatedLongObjectId.fromString(s);
+		assertNotNull(i);
+		assertEquals(s.length(), i.length());
+		assertFalse(i.isComplete());
+		assertEquals(s, i.name());
+		assertNull(i.toLongObjectId());
+	}
+
+	@Test
+	public void testEquals_Short() {
+		final String s = "27e15b72";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId.fromString(s);
+		final AbbreviatedLongObjectId b = AbbreviatedLongObjectId.fromString(s);
+		assertNotSame(a, b);
+		assertTrue(a.hashCode() == b.hashCode());
+		assertEquals(b, a);
+		assertEquals(a, b);
+	}
+
+	@Test
+	public void testEquals_Full() {
+		final String s = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId.fromString(s);
+		final AbbreviatedLongObjectId b = AbbreviatedLongObjectId.fromString(s);
+		assertNotSame(a, b);
+		assertTrue(a.hashCode() == b.hashCode());
+		assertEquals(b, a);
+		assertEquals(a, b);
+	}
+
+	@Test
+	public void testNotEquals_SameLength() {
+		final String sa = "27e15b72";
+		final String sb = "27e15b7f";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+		final AbbreviatedLongObjectId b = AbbreviatedLongObjectId
+				.fromString(sb);
+		assertFalse(a.equals(b));
+		assertFalse(b.equals(a));
+	}
+
+	@Test
+	public void testNotEquals_DiffLength() {
+		final String sa = "27e15b72abcd";
+		final String sb = "27e15b72";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+		final AbbreviatedLongObjectId b = AbbreviatedLongObjectId
+				.fromString(sb);
+		assertFalse(a.equals(b));
+		assertFalse(b.equals(a));
+	}
+
+	@Test
+	public void testPrefixCompare_Full() {
+		final String s1 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(s1);
+		final LongObjectId i1 = LongObjectId.fromString(s1);
+		assertEquals(0, a.prefixCompare(i1));
+		assertTrue(i1.startsWith(a));
+
+		final String s2 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b11";
+		final LongObjectId i2 = LongObjectId.fromString(s2);
+		assertTrue(a.prefixCompare(i2) < 0);
+		assertFalse(i2.startsWith(a));
+
+		final String s3 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b0f";
+		final LongObjectId i3 = LongObjectId.fromString(s3);
+		assertTrue(a.prefixCompare(i3) > 0);
+		assertFalse(i3.startsWith(a));
+	}
+
+	@Test
+	public void testPrefixCompare_1() {
+		final String sa = "2";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+
+		final String s1 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i1 = LongObjectId.fromString(s1);
+		assertEquals(0, a.prefixCompare(i1));
+		assertTrue(i1.startsWith(a));
+
+		final String s2 = "37e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i2 = LongObjectId.fromString(s2);
+		assertTrue(a.prefixCompare(i2) < 0);
+		assertFalse(i2.startsWith(a));
+
+		final String s3 = "17e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i3 = LongObjectId.fromString(s3);
+		assertTrue(a.prefixCompare(i3) > 0);
+		assertFalse(i3.startsWith(a));
+	}
+
+	@Test
+	public void testPrefixCompare_15() {
+		final String sa = "27e15b72937fc8f";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+
+		final String s1 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i1 = LongObjectId.fromString(s1);
+		assertEquals(0, a.prefixCompare(i1));
+		assertTrue(i1.startsWith(a));
+
+		final String s2 = "27e15b72937fc90558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i2 = LongObjectId.fromString(s2);
+		assertTrue(a.prefixCompare(i2) < 0);
+		assertFalse(i2.startsWith(a));
+
+		final String s3 = "27e15b72937fc8e558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i3 = LongObjectId.fromString(s3);
+		assertTrue(a.prefixCompare(i3) > 0);
+		assertFalse(i3.startsWith(a));
+	}
+
+	@Test
+	public void testPrefixCompare_16() {
+		final String sa = "27e15b72937fc8f5";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+
+		final String s1 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i1 = LongObjectId.fromString(s1);
+		assertEquals(0, a.prefixCompare(i1));
+		assertTrue(i1.startsWith(a));
+
+		final String s2 = "27e15b72937fc8f658da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i2 = LongObjectId.fromString(s2);
+		assertTrue(a.prefixCompare(i2) < 0);
+		assertFalse(i2.startsWith(a));
+
+		final String s3 = "27e15b72937fc8f458da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i3 = LongObjectId.fromString(s3);
+		assertTrue(a.prefixCompare(i3) > 0);
+		assertFalse(i3.startsWith(a));
+	}
+
+	@Test
+	public void testPrefixCompare_17() {
+		final String sa = "27e15b72937fc8f55";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+
+		final String s1 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i1 = LongObjectId.fromString(s1);
+		assertEquals(0, a.prefixCompare(i1));
+		assertTrue(i1.startsWith(a));
+
+		final String s2 = "27e15b72937fc8f568da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i2 = LongObjectId.fromString(s2);
+		assertTrue(a.prefixCompare(i2) < 0);
+		assertFalse(i2.startsWith(a));
+
+		final String s3 = "27e15b72937fc8f548da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i3 = LongObjectId.fromString(s3);
+		assertTrue(a.prefixCompare(i3) > 0);
+		assertFalse(i3.startsWith(a));
+	}
+
+	@Test
+	public void testPrefixCompare_33() {
+		final String sa = "27e15b72937fc8f558da24ac3d50ec203";
+		final AbbreviatedLongObjectId a = AbbreviatedLongObjectId
+				.fromString(sa);
+
+		final String s1 = "27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i1 = LongObjectId.fromString(s1);
+		assertEquals(0, a.prefixCompare(i1));
+		assertTrue(i1.startsWith(a));
+
+		final String s2 = "27e15b72937fc8f558da24ac3d50ec20402a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i2 = LongObjectId.fromString(s2);
+		assertTrue(a.prefixCompare(i2) < 0);
+		assertFalse(i2.startsWith(a));
+
+		final String s3 = "27e15b72937fc8f558da24ac3d50ec20202a4cf21e33b87ae8e4ce90e89c4b10";
+		final LongObjectId i3 = LongObjectId.fromString(s3);
+		assertTrue(a.prefixCompare(i3) > 0);
+		assertFalse(i3.startsWith(a));
+	}
+
+	@Test
+	public void testIsId() {
+		// These are all too short.
+		assertFalse(AbbreviatedLongObjectId.isId(""));
+		assertFalse(AbbreviatedLongObjectId.isId("a"));
+
+		// These are too long.
+		assertFalse(AbbreviatedLongObjectId.isId(LongObjectId
+				.fromString(
+						"27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10")
+				.name() + "0"));
+		assertFalse(AbbreviatedLongObjectId.isId(LongObjectId
+				.fromString(
+						"27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10")
+				.name() + "c0ffee"));
+
+		// These contain non-hex characters.
+		assertFalse(AbbreviatedLongObjectId.isId("01notahexstring"));
+
+		// These should all work.
+		assertTrue(AbbreviatedLongObjectId.isId("ab"));
+		assertTrue(AbbreviatedLongObjectId.isId("abc"));
+		assertTrue(AbbreviatedLongObjectId.isId("abcd"));
+		assertTrue(AbbreviatedLongObjectId.isId("abcd0"));
+		assertTrue(AbbreviatedLongObjectId.isId("abcd09"));
+		assertTrue(AbbreviatedLongObjectId.isId(LongObjectId
+				.fromString(
+						"27e15b72937fc8f558da24ac3d50ec20302a4cf21e33b87ae8e4ce90e89c4b10")
+				.name()));
+	}
+
+	@Test
+	public void testAbbreviate() {
+		AnyLongObjectId id = LongObjectIdTestUtils.hash("test");
+		assertEquals(
+				"abbreviated id should match the id it was abbreviated from", 0,
+				id.abbreviate(10).prefixCompare(id));
+	}
+
+	@Test
+	public void testFromStringByteWrongLength() {
+		byte[] buf = new byte[65];
+		try {
+			AbbreviatedLongObjectId.fromString(buf, 0, 65);
+			fail("expected IllegalArgumentException for too long AbbreviatedLongObjectId");
+		} catch (IllegalArgumentException e) {
+			// expected
+		}
+	}
+
+	@Test
+	public void testFromStringWrongLength() {
+		AnyLongObjectId id = LongObjectIdTestUtils.hash("test");
+		try {
+			AbbreviatedLongObjectId.fromString(id.name() + "c0ffee");
+			fail("expected IllegalArgumentException for too long AbbreviatedLongObjectId");
+		} catch (IllegalArgumentException e) {
+			// expected
+		}
+	}
+
+	@Test
+	public void testFromLongObjectId() {
+		AnyLongObjectId id = LongObjectIdTestUtils.hash("test");
+		assertEquals(0,
+				AbbreviatedLongObjectId.fromLongObjectId(id).prefixCompare(id));
+	}
+
+	@Test
+	public void testPrefixCompareByte() {
+		AnyLongObjectId id = LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
+		byte[] buf = new byte[32];
+		id.copyRawTo(buf, 0);
+
+		AbbreviatedLongObjectId a = id.abbreviate(62);
+		assertEquals(0, a.prefixCompare(buf, 0));
+
+		a = LongObjectId
+				.fromString(
+						"0023456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+				.abbreviate(16);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+		a = LongObjectId
+				.fromString(
+						"0123456789abcdef0023456789abcdef0123456789abcdef0123456789abcdef")
+				.abbreviate(32);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+		a = LongObjectId
+				.fromString(
+						"0123456789abcdef0123456789abcdef0023456789abcdef0123456789abcdef")
+				.abbreviate(48);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+		a = LongObjectId
+				.fromString(
+						"0123456789abcdef0123456789abcdef0123456789abcdef0023456789abcdef")
+				.abbreviate(64);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+
+		a = LongObjectId
+				.fromString(
+						"1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+				.abbreviate(16);
+		assertEquals(1, a.prefixCompare(buf, 0));
+		a = LongObjectId
+				.fromString(
+						"0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef")
+				.abbreviate(32);
+		assertEquals(1, a.prefixCompare(buf, 0));
+		a = LongObjectId
+				.fromString(
+						"0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef")
+				.abbreviate(48);
+		assertEquals(1, a.prefixCompare(buf, 0));
+		a = LongObjectId
+				.fromString(
+						"0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef")
+				.abbreviate(64);
+		assertEquals(1, a.prefixCompare(buf, 0));
+	}
+
+	@Test
+	public void testPrefixCompareLong() {
+		AnyLongObjectId id = new LongObjectId(1L, 2L, 3L, 4L);
+		long[] buf = new long[4];
+		id.copyRawTo(buf, 0);
+
+		AbbreviatedLongObjectId a = id.abbreviate(62);
+		assertEquals(0, a.prefixCompare(buf, 0));
+
+		a = new LongObjectId(0L, 2L, 3L, 4L).abbreviate(16);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+		a = new LongObjectId(1L, 1L, 3L, 4L).abbreviate(32);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+		a = new LongObjectId(1L, 2L, 2L, 4L).abbreviate(48);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+		a = new LongObjectId(1L, 2L, 3L, 3L).abbreviate(64);
+		assertEquals(-1, a.prefixCompare(buf, 0));
+
+		a = new LongObjectId(2L, 2L, 3L, 4L).abbreviate(16);
+		assertEquals(1, a.prefixCompare(buf, 0));
+		a = new LongObjectId(1L, 3L, 3L, 4L).abbreviate(32);
+		assertEquals(1, a.prefixCompare(buf, 0));
+		a = new LongObjectId(1L, 2L, 4L, 4L).abbreviate(48);
+		assertEquals(1, a.prefixCompare(buf, 0));
+		a = new LongObjectId(1L, 2L, 3L, 5L).abbreviate(64);
+		assertEquals(1, a.prefixCompare(buf, 0));
+	}
+
+	@Test
+	public void testGetFirstByte() {
+		AnyLongObjectId id = LongObjectId.fromString(
+				"f423456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef");
+		AbbreviatedLongObjectId a = id.abbreviate(10);
+		assertEquals(0xf4, a.getFirstByte());
+		assertEquals(id.getFirstByte(), a.getFirstByte());
+	}
+
+	@Test
+	public void testNotEquals() {
+		AbbreviatedLongObjectId a = new LongObjectId(1L, 2L, 3L, 4L)
+				.abbreviate(10);
+		assertFalse(a.equals("different"));
+	}
+}
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
new file mode 100644
index 0000000..435a2a3
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.lib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException;
+import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/*
+ * Ported to SHA-256 from org.eclipse.jgit.lib.ObjectIdTest
+ */
+public class LongObjectIdTest {
+	private static Path tmp;
+
+	@BeforeClass
+	public static void setup() throws IOException {
+		tmp = Files.createTempDirectory("jgit_test_");
+	}
+
+	@AfterClass
+	public static void tearDown() throws IOException {
+		FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE | FileUtils.RETRY);
+	}
+
+	@Test
+	public void test001_toString() {
+		final String x = "8367b0edc81df80e6b42eb1b71f783111224e058cb3da37894d065d2deb7ab0a";
+		final LongObjectId oid = LongObjectId.fromString(x);
+		assertEquals(x, oid.name());
+	}
+
+	@Test
+	public void test002_toString() {
+		final String x = "140ce71d628cceb78e3709940ba52a651a0c4a9c1400f2e15e998a1a43887edf";
+		final LongObjectId oid = LongObjectId.fromString(x);
+		assertEquals(x, oid.name());
+	}
+
+	@Test
+	public void test003_equals() {
+		final String x = "8367b0edc81df80e6b42eb1b71f783111224e058cb3da37894d065d2deb7ab0a";
+		final LongObjectId a = LongObjectId.fromString(x);
+		final LongObjectId b = LongObjectId.fromString(x);
+		assertEquals(a.hashCode(), b.hashCode());
+		assertEquals("a and b should be equal", b, a);
+	}
+
+	@Test
+	public void test004_isId() {
+		assertTrue("valid id", LongObjectId.isId(
+				"8367b0edc81df80e6b42eb1b71f783111224e058cb3da37894d065d2deb7ab0a"));
+	}
+
+	@Test
+	public void test005_notIsId() {
+		assertFalse("bob is not an id", LongObjectId.isId("bob"));
+	}
+
+	@Test
+	public void test006_notIsId() {
+		assertFalse("63 digits is not an id", LongObjectId.isId(
+				"8367b0edc81df80e6b42eb1b71f783111224e058cb3da37894d065d2deb7ab0"));
+	}
+
+	@Test
+	public void test007_isId() {
+		assertTrue("uppercase is accepted", LongObjectId.isId(
+				"8367b0edc81df80e6b42eb1b71f783111224e058cb3da37894d065d2dEb7ab0A"));
+	}
+
+	@Test
+	public void test008_notIsId() {
+		assertFalse("g is not a valid hex digit", LongObjectId.isId(
+				"g367b0edc81df80e6b42eb1b71f783111224e058cb3da37894d065d2deb7ab0a"));
+	}
+
+	@Test
+	public void test009_toString() {
+		final String x = "140ce71d628cceb78e3709940ba52a651a0c4a9c1400f2e15e998a1a43887edf";
+		final LongObjectId oid = LongObjectId.fromString(x);
+		assertEquals(x, LongObjectId.toString(oid));
+	}
+
+	@Test
+	public void test010_toString() {
+		final String x = "0000000000000000000000000000000000000000000000000000000000000000";
+		assertEquals(x, LongObjectId.toString(null));
+	}
+
+	@Test
+	public void test011_toString() {
+		final String x = "0123456789ABCDEFabcdef01234567890123456789ABCDEFabcdef0123456789";
+		final LongObjectId oid = LongObjectId.fromString(x);
+		assertEquals(x.toLowerCase(), oid.name());
+	}
+
+	@Test
+	public void testGetByte() {
+		byte[] raw = new byte[32];
+		for (int i = 0; i < 32; i++)
+			raw[i] = (byte) (0xa0 + i);
+		LongObjectId id = LongObjectId.fromRaw(raw);
+
+		assertEquals(raw[0] & 0xff, id.getFirstByte());
+		assertEquals(raw[0] & 0xff, id.getByte(0));
+		assertEquals(raw[1] & 0xff, id.getByte(1));
+		assertEquals(raw[1] & 0xff, id.getSecondByte());
+
+		for (int i = 2; i < 32; i++) {
+			assertEquals("index " + i, raw[i] & 0xff, id.getByte(i));
+		}
+		try {
+			id.getByte(32);
+			fail("LongObjectId has 32 byte only");
+		} catch (ArrayIndexOutOfBoundsException e) {
+			// expected
+		}
+	}
+
+	@Test
+	public void testSetByte() {
+		byte[] exp = new byte[32];
+		for (int i = 0; i < 32; i++) {
+			exp[i] = (byte) (0xa0 + i);
+		}
+
+		MutableLongObjectId id = new MutableLongObjectId();
+		id.fromRaw(exp);
+		assertEquals(LongObjectId.fromRaw(exp).name(), id.name());
+
+		id.setByte(0, 0x10);
+		assertEquals(0x10, id.getByte(0));
+		exp[0] = 0x10;
+		assertEquals(LongObjectId.fromRaw(exp).name(), id.name());
+
+		for (int p = 1; p < 32; p++) {
+			id.setByte(p, 0x10 + p);
+			assertEquals(0x10 + p, id.getByte(p));
+			exp[p] = (byte) (0x10 + p);
+			assertEquals(LongObjectId.fromRaw(exp).name(), id.name());
+		}
+
+		for (int p = 0; p < 32; p++) {
+			id.setByte(p, 0x80 + p);
+			assertEquals(0x80 + p, id.getByte(p));
+			exp[p] = (byte) (0x80 + p);
+			assertEquals(LongObjectId.fromRaw(exp).name(), id.name());
+		}
+	}
+
+	@Test
+	public void testZeroId() {
+		AnyLongObjectId zero = new LongObjectId(0L, 0L, 0L, 0L);
+		assertEquals(zero, LongObjectId.zeroId());
+		assertEquals(
+				"0000000000000000000000000000000000000000000000000000000000000000",
+				LongObjectId.zeroId().name());
+	}
+
+	@Test
+	public void testEquals() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		assertTrue("id should equal itself", id1.equals(id1));
+		AnyLongObjectId id2 = new LongObjectId(id1);
+		assertEquals("objects should be equals", id1, id2);
+
+		id2 = LongObjectIdTestUtils.hash("other");
+		assertNotEquals("objects should be not equal", id1, id2);
+	}
+
+	@Test
+	public void testCopyRawBytes() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		AnyLongObjectId id2 = new LongObjectId(id1);
+
+		byte[] buf = new byte[64];
+		id1.copyRawTo(buf, 0);
+		id2.copyRawTo(buf, 32);
+		assertTrue("objects should be equals",
+				LongObjectId.equals(buf, 0, buf, 32));
+	}
+
+	@Test
+	public void testCopyRawLongs() {
+		long[] a = new long[4];
+		a[0] = 1L;
+		a[1] = 2L;
+		a[2] = 3L;
+		a[3] = 4L;
+		AnyLongObjectId id1 = new LongObjectId(a[0], a[1], a[2], a[3]);
+		AnyLongObjectId id2 = LongObjectId.fromRaw(a);
+		assertEquals("objects should be equals", id1, id2);
+	}
+
+	@Test
+	public void testCopyFromStringInvalid() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		try {
+			LongObjectId.fromString(id1.name() + "01234");
+			fail("expected InvalidLongObjectIdException");
+		} catch (InvalidLongObjectIdException e) {
+			assertEquals("Invalid id: " + id1.name() + "01234",
+					e.getMessage());
+		}
+	}
+
+	@Test
+	public void testCopyFromStringByte() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		byte[] buf = new byte[64];
+		Charset cs = StandardCharsets.US_ASCII;
+		cs.encode(id1.name()).get(buf);
+		AnyLongObjectId id2 = LongObjectId.fromString(buf, 0);
+		assertEquals("objects should be equals", id1, id2);
+	}
+
+	@Test
+	public void testHashFile() throws IOException {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		Path f = tmp.resolve("test");
+		JGitTestUtil.write(f.toFile(), "test");
+		AnyLongObjectId id2 = LongObjectIdTestUtils.hash(f);
+		assertEquals("objects should be equals", id1, id2);
+	}
+
+	@Test
+	public void testCompareTo() {
+		AnyLongObjectId id1 = LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
+		assertEquals(0, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
+		assertEquals(0, id1.compareTo(id1));
+
+		assertEquals(-1, id1.compareTo(LongObjectId.fromString(
+				"1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
+		assertEquals(-1, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef1123456789abcdef0123456789abcdef0123456789abcdef")));
+		assertEquals(-1, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef1123456789abcdef0123456789abcdef")));
+		assertEquals(-1, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0123456789abcdef1123456789abcdef")));
+
+		assertEquals(1, id1.compareTo(LongObjectId.fromString(
+				"0023456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
+		assertEquals(1, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef0023456789abcdef0123456789abcdef0123456789abcdef")));
+		assertEquals(1, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0023456789abcdef0123456789abcdef")));
+		assertEquals(1, id1.compareTo(LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0123456789abcdef0023456789abcdef")));
+	}
+
+	@Test
+	public void testCompareToByte() {
+		AnyLongObjectId id1 = LongObjectId.fromString(
+				"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
+		byte[] buf = new byte[32];
+		id1.copyRawTo(buf, 0);
+		assertEquals(0, id1.compareTo(buf, 0));
+
+		LongObjectId
+				.fromString(
+						"1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+				.copyRawTo(buf, 0);
+		assertEquals(-1, id1.compareTo(buf, 0));
+
+		LongObjectId
+				.fromString(
+						"0023456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+				.copyRawTo(buf, 0);
+		assertEquals(1, id1.compareTo(buf, 0));
+	}
+
+	@Test
+	public void testCompareToLong() {
+		AnyLongObjectId id1 = new LongObjectId(1L, 2L, 3L, 4L);
+		long[] buf = new long[4];
+		id1.copyRawTo(buf, 0);
+		assertEquals(0, id1.compareTo(buf, 0));
+
+		new LongObjectId(2L, 2L, 3L, 4L).copyRawTo(buf, 0);
+		assertEquals(-1, id1.compareTo(buf, 0));
+
+		new LongObjectId(0L, 2L, 3L, 4L).copyRawTo(buf, 0);
+		assertEquals(1, id1.compareTo(buf, 0));
+	}
+
+	@Test
+	public void testCopyToByte() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		byte[] buf = new byte[64];
+		id1.copyTo(buf, 0);
+		assertEquals(id1, LongObjectId.fromString(buf, 0));
+	}
+
+	@Test
+	public void testCopyRawToByteBuffer() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		ByteBuffer buf = ByteBuffer.allocate(32);
+		id1.copyRawTo(buf);
+		assertEquals(id1, LongObjectId.fromRaw(buf.array(), 0));
+	}
+
+	@Test
+	public void testCopyToByteBuffer() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		ByteBuffer buf = ByteBuffer.allocate(64);
+		id1.copyTo(buf);
+		assertEquals(id1, LongObjectId.fromString(buf.array(), 0));
+	}
+
+	@Test
+	public void testCopyRawToOutputStream() throws IOException {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		ByteArrayOutputStream os = new ByteArrayOutputStream(32);
+		id1.copyRawTo(os);
+		assertEquals(id1, LongObjectId.fromRaw(os.toByteArray(), 0));
+	}
+
+	@Test
+	public void testCopyToOutputStream() throws IOException {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		ByteArrayOutputStream os = new ByteArrayOutputStream(64);
+		id1.copyTo(os);
+		assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0));
+	}
+
+	@Test
+	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();
+		assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0));
+	}
+
+	@Test
+	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();
+		assertEquals(id1, LongObjectId.fromString(os.toByteArray(), 0));
+	}
+
+	@Test
+	public void testCopyToStringBuilder() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		StringBuilder sb = new StringBuilder();
+		char[] buf = new char[64];
+		id1.copyTo(buf, sb);
+		assertEquals(id1, LongObjectId.fromString(sb.toString()));
+	}
+
+	@Test
+	public void testCopy() {
+		AnyLongObjectId id1 = LongObjectIdTestUtils.hash("test");
+		assertEquals(id1.copy(), id1);
+		MutableLongObjectId id2 = new MutableLongObjectId();
+		id2.fromObjectId(id1);
+		assertEquals(id1, id2.copy());
+	}
+}
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/MutableLongObjectIdTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/MutableLongObjectIdTest.java
new file mode 100644
index 0000000..9bdc3ab
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/MutableLongObjectIdTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.lib;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+/*
+ * Ported to SHA-256 from org.eclipse.jgit.lib.MutableObjectIdTest
+ */
+public class MutableLongObjectIdTest {
+
+	@Test
+	public void testFromRawLong() {
+		MutableLongObjectId m = new MutableLongObjectId();
+		m.fromRaw(new long[] { 1L, 2L, 3L, 4L });
+		assertEquals(new LongObjectId(1L, 2L, 3L, 4L), m);
+	}
+
+	@Test
+	public void testFromString() {
+		AnyLongObjectId id = new LongObjectId(1L, 2L, 3L, 4L);
+		MutableLongObjectId m = new MutableLongObjectId();
+		m.fromString(id.name());
+		assertEquals(id, m);
+	}
+
+	@Test
+	public void testFromStringByte() {
+		AnyLongObjectId id = new LongObjectId(1L, 2L, 3L, 4L);
+		MutableLongObjectId m = new MutableLongObjectId();
+		byte[] buf = new byte[64];
+		id.copyTo(buf, 0);
+		m.fromString(buf, 0);
+		assertEquals(id, m);
+	}
+
+	@Test
+	public void testCopy() {
+		MutableLongObjectId m = new MutableLongObjectId();
+		m.fromRaw(new long[] { 1L, 2L, 3L, 4L });
+		assertEquals(m, new MutableLongObjectId(m));
+	}
+
+	@Test
+	public void testToObjectId() {
+		MutableLongObjectId m = new MutableLongObjectId();
+		m.fromRaw(new long[] { 1L, 2L, 3L, 4L });
+		assertEquals(m, m.toObjectId());
+	}
+}
diff --git a/org.eclipse.jgit.lfs/.classpath b/org.eclipse.jgit.lfs/.classpath
new file mode 100644
index 0000000..04a2be7
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jgit.lfs/.fbprefs b/org.eclipse.jgit.lfs/.fbprefs
new file mode 100644
index 0000000..81a0767
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.fbprefs
@@ -0,0 +1,125 @@
+#FindBugs User Preferences
+#Mon May 04 16:24:13 PDT 2009
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|false
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUseObjectEquals=UseObjectEquals|false
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=default
+excludefilter0=findBugs/FindBugsExcludeFilter.xml
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
+run_at_full_build=true
diff --git a/org.eclipse.jgit.lfs/.gitignore b/org.eclipse.jgit.lfs/.gitignore
new file mode 100644
index 0000000..934e0e0
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
diff --git a/org.eclipse.jgit.lfs/.project b/org.eclipse.jgit.lfs/.project
new file mode 100644
index 0000000..d9beec1
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.lfs</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..99f26c0
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..5a0ad22
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..ff39d16
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,398 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..c336cce
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000..3dec4d9
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000..ce7a0f0
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,2 @@
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..3294d4f
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,98 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..923c37f
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.lfs/BUCK b/org.eclipse.jgit.lfs/BUCK
new file mode 100644
index 0000000..ddb3a10
--- /dev/null
+++ b/org.eclipse.jgit.lfs/BUCK
@@ -0,0 +1,17 @@
+SRCS = glob(['src/**'])
+RESOURCES = glob(['resources/**'])
+
+java_library(
+  name = 'jgit-lfs',
+  srcs = SRCS,
+  resources = RESOURCES,
+  deps = [
+    '//org.eclipse.jgit:jgit'
+  ],
+  visibility = ['PUBLIC'],
+)
+
+java_sources(
+  name = 'jgit-lfs_src',
+  srcs = SRCS + RESOURCES,
+)
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..30baf05
--- /dev/null
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.lfs
+Bundle-Version: 4.3.2.qualifier
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Export-Package: org.eclipse.jgit.lfs.errors;version="4.3.2",
+ org.eclipse.jgit.lfs.internal;version="4.3.2";x-friends:="org.eclipse.jgit.lfs.test",
+ org.eclipse.jgit.lfs.lib;version="4.3.2"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)"
diff --git a/org.eclipse.jgit.lfs/about.html b/org.eclipse.jgit.lfs/about.html
new file mode 100644
index 0000000..721e62d
--- /dev/null
+++ b/org.eclipse.jgit.lfs/about.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+  body {
+    size: 8.5in 11.0in;
+    margin: 0.25in 0.5in 0.25in 0.5in;
+    tab-interval: 0.5in;
+    }
+  p {
+    margin-left: auto;
+    margin-top:  0.5em;
+    margin-bottom: 0.5em;
+    }
+  p.list {
+  	margin-left: 0.5in;
+    margin-top:  0.05em;
+    margin-bottom: 0.05em;
+    }
+  </style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification,
+	are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+	this list of conditions and the following disclaimer. </li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+	this list of conditions and the following disclaimer in the documentation
+	and/or other materials provided with the distribution. </li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+	contributors may be used to endorse or promote products derived from
+	this software without specific prior written permission. </li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.lfs/build.properties b/org.eclipse.jgit.lfs/build.properties
new file mode 100644
index 0000000..8148271
--- /dev/null
+++ b/org.eclipse.jgit.lfs/build.properties
@@ -0,0 +1,7 @@
+source.. = src/,\
+           resources/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties,\
+               about.html
diff --git a/org.eclipse.jgit.lfs/plugin.properties b/org.eclipse.jgit.lfs/plugin.properties
new file mode 100644
index 0000000..7c22ddb
--- /dev/null
+++ b/org.eclipse.jgit.lfs/plugin.properties
@@ -0,0 +1,2 @@
+plugin_name=JGit Large File Storage
+provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
new file mode 100644
index 0000000..b0f543d
--- /dev/null
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>org.eclipse.jgit-parent</artifactId>
+    <version>4.3.2-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.eclipse.jgit.lfs</artifactId>
+  <name>JGit - Large File Storage</name>
+
+  <description>
+    JGit Large File Storage (LFS) implementation.
+  </description>
+
+  <properties>
+    <translate-qualifier/>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <sourceDirectory>src/</sourceDirectory>
+
+    <resources>
+      <resource>
+        <directory>.</directory>
+        <includes>
+          <include>plugin.properties</include>
+          <include>about.html</include>
+        </includes>
+      </resource>
+      <resource>
+        <directory>resources/</directory>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifestFile>${source-bundle-manifest}</manifestFile>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${bundle-manifest}</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <version>${clirr-version}</version>
+        <configuration>
+          <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
+          <minSeverity>info</minSeverity>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
new file mode 100644
index 0000000..700e2d5
--- /dev/null
+++ b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
@@ -0,0 +1,4 @@
+incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH.
+invalidLongId=Invalid id: {0}
+invalidLongIdLength=Invalid id length {0}; should be {1}
+requiredHashFunctionNotAvailable=Required hash function {0} not available.
\ No newline at end of file
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptLongObjectException.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptLongObjectException.java
new file mode 100644
index 0000000..fea148b
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptLongObjectException.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.errors;
+
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+
+/**
+ * Thrown when an object id is given that doesn't match the hash of the object's
+ * content
+ *
+ * @since 4.3
+ */
+public class CorruptLongObjectException extends IllegalArgumentException {
+
+	private static final long serialVersionUID = 1L;
+
+	private final AnyLongObjectId id;
+
+	private final AnyLongObjectId contentHash;
+
+	/**
+	 * Corrupt long object detected.
+	 *
+	 * @param id
+	 *            id of the long object
+	 * @param contentHash
+	 *            hash of the long object's content
+	 *
+	 * @param message
+	 */
+	public CorruptLongObjectException(AnyLongObjectId id,
+			AnyLongObjectId contentHash,
+			String message) {
+		super(message);
+		this.id = id;
+		this.contentHash = contentHash;
+	}
+
+	/**
+	 * @return the id of the object, i.e. the expected hash of the object's
+	 *         content
+	 */
+	public AnyLongObjectId getId() {
+		return id;
+	}
+
+	/**
+	 * @return the actual hash of the object's content which doesn't match the
+	 *         object's id when this exception is thrown which signals that the
+	 *         object has been corrupted
+	 */
+	public AnyLongObjectId getContentHash() {
+		return contentHash;
+	}
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java
new file mode 100644
index 0000000..1f6e2d1
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/InvalidLongObjectIdException.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009, Jonas Fonseca <fonseca@diku.dk>
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * 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.UnsupportedEncodingException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.lfs.internal.LfsText;
+
+/**
+ * Thrown when an invalid long object id is passed in as an argument.
+ *
+ * @since 4.3
+ */
+public class InvalidLongObjectIdException extends IllegalArgumentException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Create exception with bytes of the invalid object id.
+	 *
+	 * @param bytes containing the invalid id.
+	 * @param offset in the byte array where the error occurred.
+	 * @param length of the sequence of invalid bytes.
+	 */
+	public InvalidLongObjectIdException(byte[] bytes, int offset, int length) {
+		super(MessageFormat.format(LfsText.get().invalidLongId,
+				asAscii(bytes, offset, length)));
+	}
+
+	/**
+	 * @param idString
+	 *            String containing the invalid id
+	 */
+	public InvalidLongObjectIdException(String idString) {
+		super(MessageFormat.format(LfsText.get().invalidLongId, idString));
+	}
+
+	private static String asAscii(byte[] bytes, int offset, int length) {
+		try {
+			return ": " + new String(bytes, offset, length, "US-ASCII"); //$NON-NLS-1$ //$NON-NLS-2$
+		} catch (UnsupportedEncodingException e2) {
+			return ""; //$NON-NLS-1$
+		} catch (StringIndexOutOfBoundsException e2) {
+			return ""; //$NON-NLS-1$
+		}
+	}
+}
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
new file mode 100644
index 0000000..eaffcc9
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.internal;
+
+import org.eclipse.jgit.nls.NLS;
+import org.eclipse.jgit.nls.TranslationBundle;
+
+/**
+ * Translation bundle for JGit LFS server
+ */
+public class LfsText extends TranslationBundle {
+
+	/**
+	 * @return an instance of this translation bundle
+	 */
+	public static LfsText get() {
+		return NLS.getBundleFor(LfsText.class);
+	}
+
+	// @formatter:off
+	/***/ public String incorrectLONG_OBJECT_ID_LENGTH;
+	/***/ public String invalidLongId;
+	/***/ public String invalidLongIdLength;
+	/***/ public String requiredHashFunctionNotAvailable;
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java
new file mode 100644
index 0000000..e9eb0e3
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AbbreviatedLongObjectId.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.lib;
+
+import java.io.Serializable;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException;
+import org.eclipse.jgit.lfs.internal.LfsText;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * A prefix abbreviation of an {@link LongObjectId}.
+ * <p>
+ * Enable abbreviating SHA-256 strings used by Git LFS, using sufficient leading
+ * digits from the LongObjectId name to still be unique within the repository
+ * the string was generated from. These ids are likely to be unique for a useful
+ * period of time, especially if they contain at least 6-10 hex digits.
+ * <p>
+ * This class converts the hex string into a binary form, to make it more
+ * efficient for matching against an object.
+ *
+ * Ported to SHA-256 from {@link AbbreviatedObjectId}
+ *
+ * @since 4.3
+ */
+public final class AbbreviatedLongObjectId implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Test a string of characters to verify it is a hex format.
+	 * <p>
+	 * If true the string can be parsed with {@link #fromString(String)}.
+	 *
+	 * @param id
+	 *            the string to test.
+	 * @return true if the string can converted into an AbbreviatedObjectId.
+	 */
+	public static final boolean isId(final String id) {
+		if (id.length() < 2
+				|| Constants.LONG_OBJECT_ID_STRING_LENGTH < id.length())
+			return false;
+		try {
+			for (int i = 0; i < id.length(); i++)
+				RawParseUtils.parseHexInt4((byte) id.charAt(i));
+			return true;
+		} catch (ArrayIndexOutOfBoundsException e) {
+			return false;
+		}
+	}
+
+	/**
+	 * Convert an AbbreviatedObjectId from hex characters (US-ASCII).
+	 *
+	 * @param buf
+	 *            the US-ASCII buffer to read from.
+	 * @param offset
+	 *            position to read the first character from.
+	 * @param end
+	 *            one past the last position to read (<code>end-offset</code> is
+	 *            the length of the string).
+	 * @return the converted object id.
+	 */
+	public static final AbbreviatedLongObjectId fromString(final byte[] buf,
+			final int offset, final int end) {
+		if (end - offset > Constants.LONG_OBJECT_ID_STRING_LENGTH)
+			throw new IllegalArgumentException(MessageFormat.format(
+							LfsText.get().invalidLongIdLength,
+					Integer.valueOf(end - offset),
+					Integer.valueOf(Constants.LONG_OBJECT_ID_STRING_LENGTH)));
+		return fromHexString(buf, offset, end);
+	}
+
+	/**
+	 * Convert an AbbreviatedObjectId from an {@link AnyObjectId}.
+	 * <p>
+	 * This method copies over all bits of the Id, and is therefore complete
+	 * (see {@link #isComplete()}).
+	 *
+	 * @param id
+	 *            the {@link ObjectId} to convert from.
+	 * @return the converted object id.
+	 */
+	public static final AbbreviatedLongObjectId fromLongObjectId(
+			AnyLongObjectId id) {
+		return new AbbreviatedLongObjectId(
+				Constants.LONG_OBJECT_ID_STRING_LENGTH, id.w1, id.w2, id.w3,
+				id.w4);
+	}
+
+	/**
+	 * Convert an AbbreviatedLongObjectId from hex characters.
+	 *
+	 * @param str
+	 *            the string to read from. Must be &lt;= 64 characters.
+	 * @return the converted object id.
+	 */
+	public static final AbbreviatedLongObjectId fromString(final String str) {
+		if (str.length() > Constants.LONG_OBJECT_ID_STRING_LENGTH)
+			throw new IllegalArgumentException(
+					MessageFormat.format(LfsText.get().invalidLongId, str));
+		final byte[] b = org.eclipse.jgit.lib.Constants.encodeASCII(str);
+		return fromHexString(b, 0, b.length);
+	}
+
+	private static final AbbreviatedLongObjectId fromHexString(final byte[] bs,
+			int ptr, final int end) {
+		try {
+			final long a = hexUInt64(bs, ptr, end);
+			final long b = hexUInt64(bs, ptr + 16, end);
+			final long c = hexUInt64(bs, ptr + 32, end);
+			final long d = hexUInt64(bs, ptr + 48, end);
+			return new AbbreviatedLongObjectId(end - ptr, a, b, c, d);
+		} catch (ArrayIndexOutOfBoundsException e1) {
+			throw new InvalidLongObjectIdException(bs, ptr, end - ptr);
+		}
+	}
+
+	private static final long hexUInt64(final byte[] bs, int p, final int end) {
+		if (16 <= end - p)
+			return RawParseUtils.parseHexInt64(bs, p);
+
+		long r = 0;
+		int n = 0;
+		while (n < 16 && p < end) {
+			r <<= 4;
+			r |= RawParseUtils.parseHexInt4(bs[p++]);
+			n++;
+		}
+		return r << (16 - n) * 4;
+	}
+
+	static long mask(final int nibbles, final long word, final long v) {
+		final long b = (word - 1) * 16;
+		if (b + 16 <= nibbles) {
+			// We have all of the bits required for this word.
+			//
+			return v;
+		}
+
+		if (nibbles <= b) {
+			// We have none of the bits required for this word.
+			//
+			return 0;
+		}
+
+		final long s = 64 - (nibbles - b) * 4;
+		return (v >>> s) << s;
+	}
+
+	/** Number of half-bytes used by this id. */
+	final int nibbles;
+
+	final long w1;
+
+	final long w2;
+
+	final long w3;
+
+	final long w4;
+
+	AbbreviatedLongObjectId(final int n, final long new_1, final long new_2,
+			final long new_3, final long new_4) {
+		nibbles = n;
+		w1 = new_1;
+		w2 = new_2;
+		w3 = new_3;
+		w4 = new_4;
+	}
+
+	/** @return number of hex digits appearing in this id */
+	public int length() {
+		return nibbles;
+	}
+
+	/** @return true if this ObjectId is actually a complete id. */
+	public boolean isComplete() {
+		return length() == Constants.LONG_OBJECT_ID_STRING_LENGTH;
+	}
+
+	/** @return a complete ObjectId; null if {@link #isComplete()} is false */
+	public LongObjectId toLongObjectId() {
+		return isComplete() ? new LongObjectId(w1, w2, w3, w4) : null;
+	}
+
+	/**
+	 * Compares this abbreviation to a full object id.
+	 *
+	 * @param other
+	 *            the other object id.
+	 * @return &lt;0 if this abbreviation names an object that is less than
+	 *         <code>other</code>; 0 if this abbreviation exactly matches the
+	 *         first {@link #length()} digits of <code>other.name()</code>;
+	 *         &gt;0 if this abbreviation names an object that is after
+	 *         <code>other</code>.
+	 */
+	public final int prefixCompare(final AnyLongObjectId other) {
+		int cmp;
+
+		cmp = NB.compareUInt64(w1, mask(1, other.w1));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w2, mask(2, other.w2));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w3, mask(3, other.w3));
+		if (cmp != 0)
+			return cmp;
+
+		return NB.compareUInt64(w4, mask(4, other.w4));
+	}
+
+	/**
+	 * Compare this abbreviation to a network-byte-order LongObjectId.
+	 *
+	 * @param bs
+	 *            array containing the other LongObjectId in network byte order.
+	 * @param p
+	 *            position within {@code bs} to start the compare at. At least
+	 *            32 bytes, starting at this position are required.
+	 * @return &lt;0 if this abbreviation names an object that is less than
+	 *         <code>other</code>; 0 if this abbreviation exactly matches the
+	 *         first {@link #length()} digits of <code>other.name()</code>;
+	 *         &gt;0 if this abbreviation names an object that is after
+	 *         <code>other</code>.
+	 */
+	public final int prefixCompare(final byte[] bs, final int p) {
+		int cmp;
+
+		cmp = NB.compareUInt64(w1, mask(1, NB.decodeInt64(bs, p)));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w2, mask(2, NB.decodeInt64(bs, p + 8)));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w3, mask(3, NB.decodeInt64(bs, p + 16)));
+		if (cmp != 0)
+			return cmp;
+
+		return NB.compareUInt64(w4, mask(4, NB.decodeInt64(bs, p + 24)));
+	}
+
+	/**
+	 * Compare this abbreviation to a network-byte-order LongObjectId.
+	 *
+	 * @param bs
+	 *            array containing the other LongObjectId in network byte order.
+	 * @param p
+	 *            position within {@code bs} to start the compare at. At least 4
+	 *            longs, starting at this position are required.
+	 * @return &lt;0 if this abbreviation names an object that is less than
+	 *         <code>other</code>; 0 if this abbreviation exactly matches the
+	 *         first {@link #length()} digits of <code>other.name()</code>;
+	 *         &gt;0 if this abbreviation names an object that is after
+	 *         <code>other</code>.
+	 */
+	public final int prefixCompare(final long[] bs, final int p) {
+		int cmp;
+
+		cmp = NB.compareUInt64(w1, mask(1, bs[p]));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w2, mask(2, bs[p + 1]));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w3, mask(3, bs[p + 2]));
+		if (cmp != 0)
+			return cmp;
+
+		return NB.compareUInt64(w4, mask(4, bs[p + 3]));
+	}
+
+	/** @return value for a fan-out style map, only valid of length &gt;= 2. */
+	public final int getFirstByte() {
+		return (int) (w1 >>> 56);
+	}
+
+	private long mask(final long word, final long v) {
+		return mask(nibbles, word, v);
+	}
+
+	@Override
+	public int hashCode() {
+		return (int) (w1 >> 32);
+	}
+
+	@Override
+	public boolean equals(final Object o) {
+		if (o instanceof AbbreviatedLongObjectId) {
+			final AbbreviatedLongObjectId b = (AbbreviatedLongObjectId) o;
+			return nibbles == b.nibbles && w1 == b.w1 && w2 == b.w2
+					&& w3 == b.w3 && w4 == b.w4;
+		}
+		return false;
+	}
+
+	/**
+	 * @return string form of the abbreviation, in lower case hexadecimal.
+	 */
+	public final String name() {
+		final char[] b = new char[Constants.LONG_OBJECT_ID_STRING_LENGTH];
+
+		AnyLongObjectId.formatHexChar(b, 0, w1);
+		if (nibbles <= 16)
+			return new String(b, 0, nibbles);
+
+		AnyLongObjectId.formatHexChar(b, 16, w2);
+		if (nibbles <= 32)
+			return new String(b, 0, nibbles);
+
+		AnyLongObjectId.formatHexChar(b, 32, w3);
+		if (nibbles <= 48)
+			return new String(b, 0, nibbles);
+
+		AnyLongObjectId.formatHexChar(b, 48, w4);
+		return new String(b, 0, nibbles);
+	}
+
+	@SuppressWarnings("nls")
+	@Override
+	public String toString() {
+		return "AbbreviatedLongObjectId[" + name() + "]"; //$NON-NLS-1$
+	}
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
new file mode 100644
index 0000000..1f0df88
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.lib;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * A (possibly mutable) SHA-256 abstraction.
+ * <p>
+ * If this is an instance of {@link MutableLongObjectId} the concept of equality
+ * with this instance can alter at any time, if this instance is modified to
+ * represent a different object name.
+ *
+ * Ported to SHA-256 from {@link AnyObjectId}
+ *
+ * @since 4.3
+ */
+public abstract class AnyLongObjectId implements Comparable<AnyLongObjectId> {
+
+	/**
+	 * Compare two object identifier byte sequences for equality.
+	 *
+	 * @param firstObjectId
+	 *            the first identifier to compare. Must not be null.
+	 * @param secondObjectId
+	 *            the second identifier to compare. Must not be null.
+	 * @return true if the two identifiers are the same.
+	 */
+	public static boolean equals(final AnyLongObjectId firstObjectId,
+			final AnyLongObjectId secondObjectId) {
+		if (firstObjectId == secondObjectId)
+			return true;
+
+		// We test word 2 first as odds are someone already used our
+		// word 1 as a hash code, and applying that came up with these
+		// two instances we are comparing for equality. Therefore the
+		// first two words are very likely to be identical. We want to
+		// break away from collisions as quickly as possible.
+		//
+		return firstObjectId.w2 == secondObjectId.w2
+				&& firstObjectId.w3 == secondObjectId.w3
+				&& firstObjectId.w4 == secondObjectId.w4
+				&& firstObjectId.w1 == secondObjectId.w1;
+	}
+
+	long w1;
+
+	long w2;
+
+	long w3;
+
+	long w4;
+
+	/**
+	 * Get the first 8 bits of the LongObjectId.
+	 *
+	 * This is a faster version of {@code getByte(0)}.
+	 *
+	 * @return a discriminator usable for a fan-out style map. Returned values
+	 *         are unsigned and thus are in the range [0,255] rather than the
+	 *         signed byte range of [-128, 127].
+	 */
+	public final int getFirstByte() {
+		return (int) (w1 >>> 56);
+	}
+
+	/**
+	 * Get the second 8 bits of the LongObjectId.
+	 *
+	 * @return a discriminator usable for a fan-out style map. Returned values
+	 *         are unsigned and thus are in the range [0,255] rather than the
+	 *         signed byte range of [-128, 127].
+	 */
+	public final int getSecondByte() {
+		return (int) ((w1 >>> 48) & 0xff);
+	}
+
+	/**
+	 * Get any byte from the LongObjectId.
+	 *
+	 * Callers hard-coding {@code getByte(0)} should instead use the much faster
+	 * special case variant {@link #getFirstByte()}.
+	 *
+	 * @param index
+	 *            index of the byte to obtain from the raw form of the
+	 *            LongObjectId. Must be in range [0,
+	 *            {@link Constants#LONG_OBJECT_ID_LENGTH}).
+	 * @return the value of the requested byte at {@code index}. Returned values
+	 *         are unsigned and thus are in the range [0,255] rather than the
+	 *         signed byte range of [-128, 127].
+	 * @throws ArrayIndexOutOfBoundsException
+	 *             {@code index} is less than 0, equal to
+	 *             {@link Constants#LONG_OBJECT_ID_LENGTH}, or greater than
+	 *             {@link Constants#LONG_OBJECT_ID_LENGTH}.
+	 */
+	public final int getByte(int index) {
+		long w;
+		switch (index >> 3) {
+		case 0:
+			w = w1;
+			break;
+		case 1:
+			w = w2;
+			break;
+		case 2:
+			w = w3;
+			break;
+		case 3:
+			w = w4;
+			break;
+		default:
+			throw new ArrayIndexOutOfBoundsException(index);
+		}
+
+		return (int) ((w >>> (8 * (15 - (index & 15)))) & 0xff);
+	}
+
+	/**
+	 * Compare this LongObjectId to another and obtain a sort ordering.
+	 *
+	 * @param other
+	 *            the other id to compare to. Must not be null.
+	 * @return &lt; 0 if this id comes before other; 0 if this id is equal to
+	 *         other; &gt; 0 if this id comes after other.
+	 */
+	public final int compareTo(final AnyLongObjectId other) {
+		if (this == other)
+			return 0;
+
+		int cmp;
+
+		cmp = NB.compareUInt64(w1, other.w1);
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w2, other.w2);
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w3, other.w3);
+		if (cmp != 0)
+			return cmp;
+
+		return NB.compareUInt64(w4, other.w4);
+	}
+
+	/**
+	 * Compare this LongObjectId to a network-byte-order LongObjectId.
+	 *
+	 * @param bs
+	 *            array containing the other LongObjectId in network byte order.
+	 * @param p
+	 *            position within {@code bs} to start the compare at. At least
+	 *            32 bytes, starting at this position are required.
+	 * @return a negative integer, zero, or a positive integer as this object is
+	 *         less than, equal to, or greater than the specified object.
+	 */
+	public final int compareTo(final byte[] bs, final int p) {
+		int cmp;
+
+		cmp = NB.compareUInt64(w1, NB.decodeInt64(bs, p));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w2, NB.decodeInt64(bs, p + 8));
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w3, NB.decodeInt64(bs, p + 16));
+		if (cmp != 0)
+			return cmp;
+
+		return NB.compareUInt64(w4, NB.decodeInt64(bs, p + 24));
+	}
+
+	/**
+	 * Compare this LongObjectId to a network-byte-order LongObjectId.
+	 *
+	 * @param bs
+	 *            array containing the other LongObjectId in network byte order.
+	 * @param p
+	 *            position within {@code bs} to start the compare at. At least 4
+	 *            longs, starting at this position are required.
+	 * @return a negative integer, zero, or a positive integer as this object is
+	 *         less than, equal to, or greater than the specified object.
+	 */
+	public final int compareTo(final long[] bs, final int p) {
+		int cmp;
+
+		cmp = NB.compareUInt64(w1, bs[p]);
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w2, bs[p + 1]);
+		if (cmp != 0)
+			return cmp;
+
+		cmp = NB.compareUInt64(w3, bs[p + 2]);
+		if (cmp != 0)
+			return cmp;
+
+		return NB.compareUInt64(w4, bs[p + 3]);
+	}
+
+	/**
+	 * Tests if this LongObjectId starts with the given abbreviation.
+	 *
+	 * @param abbr
+	 *            the abbreviation.
+	 * @return true if this LongObjectId begins with the abbreviation; else
+	 *         false.
+	 */
+	public boolean startsWith(final AbbreviatedLongObjectId abbr) {
+		return abbr.prefixCompare(this) == 0;
+	}
+
+	public final int hashCode() {
+		return (int) (w1 >> 32);
+	}
+
+	/**
+	 * Determine if this LongObjectId has exactly the same value as another.
+	 *
+	 * @param other
+	 *            the other id to compare to. May be null.
+	 * @return true only if both LongObjectIds have identical bits.
+	 */
+	public final boolean equals(final AnyLongObjectId other) {
+		return other != null ? equals(this, other) : false;
+	}
+
+	public final boolean equals(final Object o) {
+		if (o instanceof AnyLongObjectId)
+			return equals((AnyLongObjectId) o);
+		else
+			return false;
+	}
+
+	/**
+	 * Copy this LongObjectId to an output writer in raw binary.
+	 *
+	 * @param w
+	 *            the buffer to copy to. Must be in big endian order.
+	 */
+	public void copyRawTo(final ByteBuffer w) {
+		w.putLong(w1);
+		w.putLong(w2);
+		w.putLong(w3);
+		w.putLong(w4);
+	}
+
+	/**
+	 * Copy this LongObjectId to a byte array.
+	 *
+	 * @param b
+	 *            the buffer to copy to.
+	 * @param o
+	 *            the offset within b to write at.
+	 */
+	public void copyRawTo(final byte[] b, final int o) {
+		NB.encodeInt64(b, o, w1);
+		NB.encodeInt64(b, o + 8, w2);
+		NB.encodeInt64(b, o + 16, w3);
+		NB.encodeInt64(b, o + 24, w4);
+	}
+
+	/**
+	 * Copy this LongObjectId to an long array.
+	 *
+	 * @param b
+	 *            the buffer to copy to.
+	 * @param o
+	 *            the offset within b to write at.
+	 */
+	public void copyRawTo(final long[] b, final int o) {
+		b[o] = w1;
+		b[o + 1] = w2;
+		b[o + 2] = w3;
+		b[o + 3] = w4;
+	}
+
+	/**
+	 * Copy this LongObjectId to an output writer in raw binary.
+	 *
+	 * @param w
+	 *            the stream to write to.
+	 * @throws IOException
+	 *             the stream writing failed.
+	 */
+	public void copyRawTo(final OutputStream w) throws IOException {
+		writeRawLong(w, w1);
+		writeRawLong(w, w2);
+		writeRawLong(w, w3);
+		writeRawLong(w, w4);
+	}
+
+	private static void writeRawLong(final OutputStream w, long v)
+			throws IOException {
+		w.write((int) (v >>> 56));
+		w.write((int) (v >>> 48));
+		w.write((int) (v >>> 40));
+		w.write((int) (v >>> 32));
+		w.write((int) (v >>> 24));
+		w.write((int) (v >>> 16));
+		w.write((int) (v >>> 8));
+		w.write((int) v);
+	}
+
+	/**
+	 * Copy this LongObjectId to an output writer in hex format.
+	 *
+	 * @param w
+	 *            the stream to copy to.
+	 * @throws IOException
+	 *             the stream writing failed.
+	 */
+	public void copyTo(final OutputStream w) throws IOException {
+		w.write(toHexByteArray());
+	}
+
+	/**
+	 * Copy this LongObjectId to a byte array in hex format.
+	 *
+	 * @param b
+	 *            the buffer to copy to.
+	 * @param o
+	 *            the offset within b to write at.
+	 */
+	public void copyTo(byte[] b, int o) {
+		formatHexByte(b, o + 0, w1);
+		formatHexByte(b, o + 16, w2);
+		formatHexByte(b, o + 32, w3);
+		formatHexByte(b, o + 48, w4);
+	}
+
+	/**
+	 * Copy this LongObjectId to a ByteBuffer in hex format.
+	 *
+	 * @param b
+	 *            the buffer to copy to.
+	 */
+	public void copyTo(ByteBuffer b) {
+		b.put(toHexByteArray());
+	}
+
+	private byte[] toHexByteArray() {
+		final byte[] dst = new byte[Constants.LONG_OBJECT_ID_STRING_LENGTH];
+		formatHexByte(dst, 0, w1);
+		formatHexByte(dst, 16, w2);
+		formatHexByte(dst, 32, w3);
+		formatHexByte(dst, 48, w4);
+		return dst;
+	}
+
+	private static final byte[] hexbyte = { '0', '1', '2', '3', '4', '5', '6',
+			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+	private static void formatHexByte(final byte[] dst, final int p, long w) {
+		int o = p + 15;
+		while (o >= p && w != 0) {
+			dst[o--] = hexbyte[(int) (w & 0xf)];
+			w >>>= 4;
+		}
+		while (o >= p)
+			dst[o--] = '0';
+	}
+
+	/**
+	 * Copy this LongObjectId to an output writer in hex format.
+	 *
+	 * @param w
+	 *            the stream to copy to.
+	 * @throws IOException
+	 *             the stream writing failed.
+	 */
+	public void copyTo(final Writer w) throws IOException {
+		w.write(toHexCharArray());
+	}
+
+	/**
+	 * Copy this LongObjectId to an output writer in hex format.
+	 *
+	 * @param tmp
+	 *            temporary char array to buffer construct into before writing.
+	 *            Must be at least large enough to hold 2 digits for each byte
+	 *            of object id (64 characters or larger).
+	 * @param w
+	 *            the stream to copy to.
+	 * @throws IOException
+	 *             the stream writing failed.
+	 */
+	public void copyTo(final char[] tmp, final Writer w) throws IOException {
+		toHexCharArray(tmp);
+		w.write(tmp, 0, Constants.LONG_OBJECT_ID_STRING_LENGTH);
+	}
+
+	/**
+	 * Copy this LongObjectId to a StringBuilder in hex format.
+	 *
+	 * @param tmp
+	 *            temporary char array to buffer construct into before writing.
+	 *            Must be at least large enough to hold 2 digits for each byte
+	 *            of object id (64 characters or larger).
+	 * @param w
+	 *            the string to append onto.
+	 */
+	public void copyTo(final char[] tmp, final StringBuilder w) {
+		toHexCharArray(tmp);
+		w.append(tmp, 0, Constants.LONG_OBJECT_ID_STRING_LENGTH);
+	}
+
+	char[] toHexCharArray() {
+		final char[] dst = new char[Constants.LONG_OBJECT_ID_STRING_LENGTH];
+		toHexCharArray(dst);
+		return dst;
+	}
+
+	private void toHexCharArray(final char[] dst) {
+		formatHexChar(dst, 0, w1);
+		formatHexChar(dst, 16, w2);
+		formatHexChar(dst, 32, w3);
+		formatHexChar(dst, 48, w4);
+	}
+
+	private static final char[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
+			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+	static void formatHexChar(final char[] dst, final int p, long w) {
+		int o = p + 15;
+		while (o >= p && w != 0) {
+			dst[o--] = hexchar[(int) (w & 0xf)];
+			w >>>= 4;
+		}
+		while (o >= p)
+			dst[o--] = '0';
+	}
+
+	@SuppressWarnings("nls")
+	@Override
+	public String toString() {
+		return "AnyLongObjectId[" + name() + "]";
+	}
+
+	/**
+	 * @return string form of the SHA-256, in lower case hexadecimal.
+	 */
+	public final String name() {
+		return new String(toHexCharArray());
+	}
+
+	/**
+	 * @return string form of the SHA-256, in lower case hexadecimal.
+	 */
+	public final String getName() {
+		return name();
+	}
+
+	/**
+	 * Return an abbreviation (prefix) of this object SHA-256.
+	 * <p>
+	 * This implementation does not guarantee uniqueness. Callers should instead
+	 * use {@link ObjectReader#abbreviate(AnyObjectId, int)} to obtain a unique
+	 * abbreviation within the scope of a particular object database.
+	 *
+	 * @param len
+	 *            length of the abbreviated string.
+	 * @return SHA-256 abbreviation.
+	 */
+	public AbbreviatedLongObjectId abbreviate(final int len) {
+		final long a = AbbreviatedLongObjectId.mask(len, 1, w1);
+		final long b = AbbreviatedLongObjectId.mask(len, 2, w2);
+		final long c = AbbreviatedLongObjectId.mask(len, 3, w3);
+		final long d = AbbreviatedLongObjectId.mask(len, 4, w4);
+		return new AbbreviatedLongObjectId(len, a, b, c, d);
+	}
+
+	/**
+	 * Obtain an immutable copy of this current object.
+	 * <p>
+	 * Only returns <code>this</code> if this instance is an unsubclassed
+	 * instance of {@link LongObjectId}; otherwise a new instance is returned
+	 * holding the same value.
+	 * <p>
+	 * This method is useful to shed any additional memory that may be tied to
+	 * the subclass, yet retain the unique identity of the object id for future
+	 * lookups within maps and repositories.
+	 *
+	 * @return an immutable copy, using the smallest memory footprint possible.
+	 */
+	public final LongObjectId copy() {
+		if (getClass() == LongObjectId.class)
+			return (LongObjectId) this;
+		return new LongObjectId(this);
+	}
+
+	/**
+	 * Obtain an immutable copy of this current object.
+	 * <p>
+	 * See {@link #copy()} if <code>this</code> is a possibly subclassed (but
+	 * immutable) identity and the application needs a lightweight identity
+	 * <i>only</i> reference.
+	 *
+	 * @return an immutable copy. May be <code>this</code> if this is already an
+	 *         immutable instance.
+	 */
+	public abstract LongObjectId toObjectId();
+}
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
new file mode 100644
index 0000000..3cadbf7
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.lfs.lib;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.lfs.internal.LfsText;
+
+/**
+ * Misc. constants used throughout JGit LFS extension.
+ *
+ * @since 4.3
+ **/
+@SuppressWarnings("nls")
+public final class Constants {
+	/** Hash function used natively by Git LFS extension for large objects. */
+	private static final String LONG_HASH_FUNCTION = "SHA-256";
+
+	/**
+	 * A Git LFS large object hash is 256 bits, i.e. 32 bytes.
+	 * <p>
+	 * Changing this assumption is not going to be as easy as changing this
+	 * declaration.
+	 */
+	public static final int LONG_OBJECT_ID_LENGTH = 32;
+
+	/**
+	 * A Git LFS large object can be expressed as a 64 character string of
+	 * hexadecimal digits.
+	 *
+	 * @see #LONG_OBJECT_ID_LENGTH
+	 */
+	public static final int LONG_OBJECT_ID_STRING_LENGTH = LONG_OBJECT_ID_LENGTH
+			* 2;
+
+	/**
+	 * Create a new digest function for objects.
+	 *
+	 * @return a new digest object.
+	 * @throws RuntimeException
+	 *             this Java virtual machine does not support the required hash
+	 *             function. Very unlikely given that JGit uses a hash function
+	 *             that is in the Java reference specification.
+	 */
+	public static MessageDigest newMessageDigest() {
+		try {
+			return MessageDigest.getInstance(LONG_HASH_FUNCTION);
+		} catch (NoSuchAlgorithmException nsae) {
+			throw new RuntimeException(MessageFormat.format(
+					LfsText.get().requiredHashFunctionNotAvailable,
+					LONG_HASH_FUNCTION), nsae);
+		}
+	}
+
+	static {
+		if (LONG_OBJECT_ID_LENGTH != newMessageDigest().getDigestLength())
+			throw new LinkageError(
+					LfsText.get().incorrectLONG_OBJECT_ID_LENGTH);
+	}
+
+	/**
+	 * Content type used by LFS REST API as defined in
+	 * {@link "https://github.com/github/git-lfs/blob/master/docs/api/http-v1-batch.md"}
+	 */
+	public static String CONTENT_TYPE_GIT_LFS_JSON = "application/vnd.git-lfs+json";
+
+	/**
+	 * "arbitrary binary data" as defined in RFC 2046
+	 * {@link "https://www.ietf.org/rfc/rfc2046.txt"}
+	 */
+	public static String HDR_APPLICATION_OCTET_STREAM = "application/octet-stream";
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java
new file mode 100644
index 0000000..c4a4e43
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LongObjectId.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.lib;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * A SHA-256 abstraction.
+ *
+ * Ported to SHA-256 from {@link ObjectId}
+ *
+ * @since 4.3
+ */
+public class LongObjectId extends AnyLongObjectId implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	private static final LongObjectId ZEROID;
+
+	private static final String ZEROID_STR;
+
+	static {
+		ZEROID = new LongObjectId(0L, 0L, 0L, 0L);
+		ZEROID_STR = ZEROID.name();
+	}
+
+	/**
+	 * Get the special all-zero LongObjectId.
+	 *
+	 * @return the all-zero LongObjectId, often used to stand-in for no object.
+	 */
+	public static final LongObjectId zeroId() {
+		return ZEROID;
+	}
+
+	/**
+	 * Test a string of characters to verify that it can be interpreted as
+	 * LongObjectId.
+	 * <p>
+	 * If true the string can be parsed with {@link #fromString(String)}.
+	 *
+	 * @param id
+	 *            the string to test.
+	 * @return true if the string can converted into an LongObjectId.
+	 */
+	public static final boolean isId(final String id) {
+		if (id.length() != Constants.LONG_OBJECT_ID_STRING_LENGTH)
+			return false;
+		try {
+			for (int i = 0; i < Constants.LONG_OBJECT_ID_STRING_LENGTH; i++) {
+				RawParseUtils.parseHexInt4((byte) id.charAt(i));
+			}
+			return true;
+		} catch (ArrayIndexOutOfBoundsException e) {
+			return false;
+		}
+	}
+
+	/**
+	 * Convert a LongObjectId into a hex string representation.
+	 *
+	 * @param i
+	 *            the id to convert. May be null.
+	 * @return the hex string conversion of this id's content.
+	 */
+	public static final String toString(final LongObjectId i) {
+		return i != null ? i.name() : ZEROID_STR;
+	}
+
+	/**
+	 * Compare two object identifier byte sequences for equality.
+	 *
+	 * @param firstBuffer
+	 *            the first buffer to compare against. Must have at least 32
+	 *            bytes from position fi through the end of the buffer.
+	 * @param fi
+	 *            first offset within firstBuffer to begin testing.
+	 * @param secondBuffer
+	 *            the second buffer to compare against. Must have at least 32
+	 *            bytes from position si through the end of the buffer.
+	 * @param si
+	 *            first offset within secondBuffer to begin testing.
+	 * @return true if the two identifiers are the same.
+	 */
+	public static boolean equals(final byte[] firstBuffer, final int fi,
+			final byte[] secondBuffer, final int si) {
+		return firstBuffer[fi] == secondBuffer[si]
+				&& firstBuffer[fi + 1] == secondBuffer[si + 1]
+				&& firstBuffer[fi + 2] == secondBuffer[si + 2]
+				&& firstBuffer[fi + 3] == secondBuffer[si + 3]
+				&& firstBuffer[fi + 4] == secondBuffer[si + 4]
+				&& firstBuffer[fi + 5] == secondBuffer[si + 5]
+				&& firstBuffer[fi + 6] == secondBuffer[si + 6]
+				&& firstBuffer[fi + 7] == secondBuffer[si + 7]
+				&& firstBuffer[fi + 8] == secondBuffer[si + 8]
+				&& firstBuffer[fi + 9] == secondBuffer[si + 9]
+				&& firstBuffer[fi + 10] == secondBuffer[si + 10]
+				&& firstBuffer[fi + 11] == secondBuffer[si + 11]
+				&& firstBuffer[fi + 12] == secondBuffer[si + 12]
+				&& firstBuffer[fi + 13] == secondBuffer[si + 13]
+				&& firstBuffer[fi + 14] == secondBuffer[si + 14]
+				&& firstBuffer[fi + 15] == secondBuffer[si + 15]
+				&& firstBuffer[fi + 16] == secondBuffer[si + 16]
+				&& firstBuffer[fi + 17] == secondBuffer[si + 17]
+				&& firstBuffer[fi + 18] == secondBuffer[si + 18]
+				&& firstBuffer[fi + 19] == secondBuffer[si + 19]
+				&& firstBuffer[fi + 20] == secondBuffer[si + 20]
+				&& firstBuffer[fi + 21] == secondBuffer[si + 21]
+				&& firstBuffer[fi + 22] == secondBuffer[si + 22]
+				&& firstBuffer[fi + 23] == secondBuffer[si + 23]
+				&& firstBuffer[fi + 24] == secondBuffer[si + 24]
+				&& firstBuffer[fi + 25] == secondBuffer[si + 25]
+				&& firstBuffer[fi + 26] == secondBuffer[si + 26]
+				&& firstBuffer[fi + 27] == secondBuffer[si + 27]
+				&& firstBuffer[fi + 28] == secondBuffer[si + 28]
+				&& firstBuffer[fi + 29] == secondBuffer[si + 29]
+				&& firstBuffer[fi + 30] == secondBuffer[si + 30]
+				&& firstBuffer[fi + 31] == secondBuffer[si + 31];
+	}
+
+	/**
+	 * Convert a LongObjectId from raw binary representation.
+	 *
+	 * @param bs
+	 *            the raw byte buffer to read from. At least 32 bytes must be
+	 *            available within this byte array.
+	 * @return the converted object id.
+	 */
+	public static final LongObjectId fromRaw(final byte[] bs) {
+		return fromRaw(bs, 0);
+	}
+
+	/**
+	 * Convert a LongObjectId from raw binary representation.
+	 *
+	 * @param bs
+	 *            the raw byte buffer to read from. At least 32 bytes after p
+	 *            must be available within this byte array.
+	 * @param p
+	 *            position to read the first byte of data from.
+	 * @return the converted object id.
+	 */
+	public static final LongObjectId fromRaw(final byte[] bs, final int p) {
+		final long a = NB.decodeInt64(bs, p);
+		final long b = NB.decodeInt64(bs, p + 8);
+		final long c = NB.decodeInt64(bs, p + 16);
+		final long d = NB.decodeInt64(bs, p + 24);
+		return new LongObjectId(a, b, c, d);
+	}
+
+	/**
+	 * Convert a LongObjectId from raw binary representation.
+	 *
+	 * @param is
+	 *            the raw long buffer to read from. At least 4 longs must be
+	 *            available within this long array.
+	 * @return the converted object id.
+	 */
+	public static final LongObjectId fromRaw(final long[] is) {
+		return fromRaw(is, 0);
+	}
+
+	/**
+	 * Convert a LongObjectId from raw binary representation.
+	 *
+	 * @param is
+	 *            the raw long buffer to read from. At least 4 longs after p
+	 *            must be available within this long array.
+	 * @param p
+	 *            position to read the first long of data from.
+	 * @return the converted object id.
+	 */
+	public static final LongObjectId fromRaw(final long[] is, final int p) {
+		return new LongObjectId(is[p], is[p + 1], is[p + 2], is[p + 3]);
+	}
+
+	/**
+	 * Convert a LongObjectId from hex characters (US-ASCII).
+	 *
+	 * @param buf
+	 *            the US-ASCII buffer to read from. At least 64 bytes after
+	 *            offset must be available within this byte array.
+	 * @param offset
+	 *            position to read the first character from.
+	 * @return the converted object id.
+	 */
+	public static final LongObjectId fromString(final byte[] buf, final int offset) {
+		return fromHexString(buf, offset);
+	}
+
+	/**
+	 * Convert a LongObjectId from hex characters.
+	 *
+	 * @param str
+	 *            the string to read from. Must be 64 characters long.
+	 * @return the converted object id.
+	 */
+	public static LongObjectId fromString(final String str) {
+		if (str.length() != Constants.LONG_OBJECT_ID_STRING_LENGTH)
+			throw new InvalidLongObjectIdException(str);
+		return fromHexString(org.eclipse.jgit.lib.Constants.encodeASCII(str),
+				0);
+	}
+
+	private static final LongObjectId fromHexString(final byte[] bs, int p) {
+		try {
+			final long a = RawParseUtils.parseHexInt64(bs, p);
+			final long b = RawParseUtils.parseHexInt64(bs, p + 16);
+			final long c = RawParseUtils.parseHexInt64(bs, p + 32);
+			final long d = RawParseUtils.parseHexInt64(bs, p + 48);
+			return new LongObjectId(a, b, c, d);
+		} catch (ArrayIndexOutOfBoundsException e1) {
+			throw new InvalidLongObjectIdException(bs, p,
+					Constants.LONG_OBJECT_ID_STRING_LENGTH);
+		}
+	}
+
+	LongObjectId(final long new_1, final long new_2, final long new_3,
+			final long new_4) {
+		w1 = new_1;
+		w2 = new_2;
+		w3 = new_3;
+		w4 = new_4;
+	}
+
+	/**
+	 * Initialize this instance by copying another existing LongObjectId.
+	 * <p>
+	 * This constructor is mostly useful for subclasses which want to extend a
+	 * LongObjectId with more properties, but initialize from an existing
+	 * LongObjectId instance acquired by other means.
+	 *
+	 * @param src
+	 *            another already parsed LongObjectId to copy the value out of.
+	 */
+	protected LongObjectId(final AnyLongObjectId src) {
+		w1 = src.w1;
+		w2 = src.w2;
+		w3 = src.w3;
+		w4 = src.w4;
+	}
+
+	@Override
+	public LongObjectId toObjectId() {
+		return this;
+	}
+
+	private void writeObject(ObjectOutputStream os) throws IOException {
+		os.writeLong(w1);
+		os.writeLong(w2);
+		os.writeLong(w3);
+		os.writeLong(w4);
+	}
+
+	private void readObject(ObjectInputStream ois) throws IOException {
+		w1 = ois.readLong();
+		w2 = ois.readLong();
+		w3 = ois.readLong();
+		w4 = ois.readLong();
+	}
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java
new file mode 100644
index 0000000..130e94e
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/MutableLongObjectId.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.lfs.lib;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException;
+import org.eclipse.jgit.lfs.internal.LfsText;
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * A mutable SHA-256 abstraction.
+ *
+ * Ported to SHA-256 from {@link MutableObjectId}
+ *
+ * @since 4.3
+ */
+public class MutableLongObjectId extends AnyLongObjectId {
+	/**
+	 * Empty constructor. Initialize object with default (zeros) value.
+	 */
+	public MutableLongObjectId() {
+		super();
+	}
+
+	/**
+	 * Copying constructor.
+	 *
+	 * @param src
+	 *            original entry, to copy id from
+	 */
+	MutableLongObjectId(MutableLongObjectId src) {
+		fromObjectId(src);
+	}
+
+	/**
+	 * Set any byte in the id.
+	 *
+	 * @param index
+	 *            index of the byte to set in the raw form of the ObjectId. Must
+	 *            be in range [0, {@link Constants#LONG_OBJECT_ID_LENGTH}).
+	 * @param value
+	 *            the value of the specified byte at {@code index}. Values are
+	 *            unsigned and thus are in the range [0,255] rather than the
+	 *            signed byte range of [-128, 127].
+	 * @throws ArrayIndexOutOfBoundsException
+	 *             {@code index} is less than 0, equal to
+	 *             {@link Constants#LONG_OBJECT_ID_LENGTH}, or greater than
+	 *             {@link Constants#LONG_OBJECT_ID_LENGTH}.
+	 */
+	public void setByte(int index, int value) {
+		switch (index >> 3) {
+		case 0:
+			w1 = set(w1, index & 7, value);
+			break;
+		case 1:
+			w2 = set(w2, index & 7, value);
+			break;
+		case 2:
+			w3 = set(w3, index & 7, value);
+			break;
+		case 3:
+			w4 = set(w4, index & 7, value);
+			break;
+		default:
+			throw new ArrayIndexOutOfBoundsException(index);
+		}
+	}
+
+	private static long set(long w, int index, long value) {
+		value &= 0xff;
+
+		switch (index) {
+		case 0:
+			return (w & 0x00ffffffffffffffL) | (value << 56);
+		case 1:
+			return (w & 0xff00ffffffffffffL) | (value << 48);
+		case 2:
+			return (w & 0xffff00ffffffffffL) | (value << 40);
+		case 3:
+			return (w & 0xffffff00ffffffffL) | (value << 32);
+		case 4:
+			return (w & 0xffffffff00ffffffL) | (value << 24);
+		case 5:
+			return (w & 0xffffffffff00ffffL) | (value << 16);
+		case 6:
+			return (w & 0xffffffffffff00ffL) | (value << 8);
+		case 7:
+			return (w & 0xffffffffffffff00L) | value;
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
+	/** Make this id match {@link LongObjectId#zeroId()}. */
+	public void clear() {
+		w1 = 0;
+		w2 = 0;
+		w3 = 0;
+		w4 = 0;
+	}
+
+	/**
+	 * Copy an LongObjectId into this mutable buffer.
+	 *
+	 * @param src
+	 *            the source id to copy from.
+	 */
+	public void fromObjectId(AnyLongObjectId src) {
+		this.w1 = src.w1;
+		this.w2 = src.w2;
+		this.w3 = src.w3;
+		this.w4 = src.w4;
+	}
+
+	/**
+	 * Convert an LongObjectId from raw binary representation.
+	 *
+	 * @param bs
+	 *            the raw byte buffer to read from. At least 32 bytes must be
+	 *            available within this byte array.
+	 */
+	public void fromRaw(final byte[] bs) {
+		fromRaw(bs, 0);
+	}
+
+	/**
+	 * Convert an LongObjectId from raw binary representation.
+	 *
+	 * @param bs
+	 *            the raw byte buffer to read from. At least 32 bytes after p
+	 *            must be available within this byte array.
+	 * @param p
+	 *            position to read the first byte of data from.
+	 */
+	public void fromRaw(final byte[] bs, final int p) {
+		w1 = NB.decodeInt64(bs, p);
+		w2 = NB.decodeInt64(bs, p + 8);
+		w3 = NB.decodeInt64(bs, p + 16);
+		w4 = NB.decodeInt64(bs, p + 24);
+	}
+
+	/**
+	 * Convert an LongObjectId from binary representation expressed in integers.
+	 *
+	 * @param longs
+	 *            the raw long buffer to read from. At least 4 longs must be
+	 *            available within this longs array.
+	 */
+	public void fromRaw(final long[] longs) {
+		fromRaw(longs, 0);
+	}
+
+	/**
+	 * Convert an LongObjectId from binary representation expressed in longs.
+	 *
+	 * @param longs
+	 *            the raw int buffer to read from. At least 4 longs after p must
+	 *            be available within this longs array.
+	 * @param p
+	 *            position to read the first integer of data from.
+	 *
+	 */
+	public void fromRaw(final long[] longs, final int p) {
+		w1 = longs[p];
+		w2 = longs[p + 1];
+		w3 = longs[p + 2];
+		w4 = longs[p + 3];
+	}
+
+	/**
+	 * Convert an LongObjectId from hex characters (US-ASCII).
+	 *
+	 * @param buf
+	 *            the US-ASCII buffer to read from. At least 32 bytes after
+	 *            offset must be available within this byte array.
+	 * @param offset
+	 *            position to read the first character from.
+	 */
+	public void fromString(final byte[] buf, final int offset) {
+		fromHexString(buf, offset);
+	}
+
+	/**
+	 * Convert an LongObjectId from hex characters.
+	 *
+	 * @param str
+	 *            the string to read from. Must be 64 characters long.
+	 */
+	public void fromString(final String str) {
+		if (str.length() != Constants.LONG_OBJECT_ID_STRING_LENGTH)
+			throw new IllegalArgumentException(
+					MessageFormat.format(LfsText.get().invalidLongId, str));
+		fromHexString(org.eclipse.jgit.lib.Constants.encodeASCII(str), 0);
+	}
+
+	private void fromHexString(final byte[] bs, int p) {
+		try {
+			w1 = RawParseUtils.parseHexInt64(bs, p);
+			w2 = RawParseUtils.parseHexInt64(bs, p + 16);
+			w3 = RawParseUtils.parseHexInt64(bs, p + 32);
+			w4 = RawParseUtils.parseHexInt64(bs, p + 48);
+		} catch (ArrayIndexOutOfBoundsException e1) {
+			throw new InvalidLongObjectIdException(bs, p,
+					Constants.LONG_OBJECT_ID_STRING_LENGTH);
+		}
+	}
+
+	@Override
+	public LongObjectId toObjectId() {
+		return new LongObjectId(this);
+	}
+}
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 c22b3bc..3d13c71 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.2.1.qualifier"
+      version="4.3.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index cbfa6b6..81a8c3b 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-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 73b6eb6..f2597a4 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.2.1.qualifier"
+      version="4.3.2.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 82dfb1f..ca0fdca 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-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 186b263..829ef0a 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.2.1.qualifier"
+      version="4.3.2.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 4cee17e..6b900d4 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.gitignore
new file mode 100644
index 0000000..2f7896d
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.project
new file mode 100644
index 0000000..c2061b9
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jgit.lfs.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..14bdc2c
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..898252b
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 0000000..823c0f5
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 0000000..0cba949
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/build.properties
new file mode 100644
index 0000000..b4a8dde
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/build.properties
@@ -0,0 +1,4 @@
+bin.includes = feature.xml,\
+               edl-v10.html,\
+               feature.properties,\
+               license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/edl-v10.html
new file mode 100644
index 0000000..1826b47
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/edl-v10.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Distribution License - Version 1.0</title>
+<style type="text/css">
+  body {
+    size: 8.5in 11.0in;
+    margin: 0.25in 0.5in 0.25in 0.5in;
+    tab-interval: 0.5in;
+    }
+  p {
+    margin-left: auto;
+    margin-top:  0.5em;
+    margin-bottom: 0.5em;
+    }
+  p.list {
+    margin-left: 0.5in;
+    margin-top:  0.05em;
+    margin-bottom: 0.05em;
+    }
+  </style>
+
+</head>
+
+<body lang="EN-US">
+
+<p><b>Eclipse Distribution License - v 1.0</b></p>
+
+<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
+
+<p>All rights reserved.</p>
+<p>Redistribution and use in source and binary forms, with or without modification,
+    are permitted provided that the following conditions are met:
+<ul><li>Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.</li>
+<li>Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.</li>
+<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.</li></ul>
+</p>
+<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.</p>
+
+</body>
+
+</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties
new file mode 100644
index 0000000..c5bffa8
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.properties
@@ -0,0 +1,159 @@
+###############################################################################
+# Copyright (c) 20015 Matthias Sohn <matthias.sohn@sap.com> and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+###############################################################################
+
+featureName=Java implementation of Git - optional LFS support
+providerName=Eclipse JGit
+
+updateSiteName=Eclipse JGit Update Site
+
+# description property - text of the "Feature Description"
+description=\
+Optional LFS support.\n
+################ end of description property ##################################
+
+# "copyright" property - text of the "Feature Update Copyright"
+copyright=\
+Copyright (c) 2015, Matthias Sohn et.al.\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Distribution License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/org/documents/edl-v10.html\n
+################ end of copyright property ####################################
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=license.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+Eclipse Foundation Software User Agreement\n\
+April 9, 2014\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\
+OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\
+USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\
+AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\
+NOTICES INDICATED OR REFERENCED BELOW.  BY USING THE CONTENT, YOU\n\
+AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\
+AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\
+OR NOTICES INDICATED OR REFERENCED BELOW.  IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\
+OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the\n\
+Eclipse Foundation is provided to you under the terms and conditions of\n\
+the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is\n\
+provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\
+For purposes of the EPL, "Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code,\n\
+documentation and other files maintained in the Eclipse Foundation source code\n\
+repository ("Repository") in software modules ("Modules") and made available\n\
+as downloadable archives ("Downloads").\n\
+\n\
+       - Content may be structured and packaged into modules to facilitate delivering,\n\
+         extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\
+         plug-in fragments ("Fragments"), and features ("Features").\n\
+       - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\
+         in a directory named "plugins".\n\
+       - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\
+         Each Feature may be packaged as a sub-directory in a directory named "features".\n\
+         Within a Feature, files named "feature.xml" may contain a list of the names and version\n\
+         numbers of the Plug-ins and/or Fragments associated with that Feature.\n\
+       - Features may also include other Features ("Included Features"). Within a Feature, files\n\
+         named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be\n\
+contained in files named "about.html" ("Abouts"). The terms and\n\
+conditions governing Features and Included Features should be contained\n\
+in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\
+Licenses may be located in any directory of a Download or Module\n\
+including, but not limited to the following locations:\n\
+\n\
+       - The top-level (root) directory\n\
+       - Plug-in and Fragment directories\n\
+       - Inside Plug-ins and Fragments packaged as JARs\n\
+       - Sub-directories of the directory named "src" of certain Plug-ins\n\
+       - Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using the\n\
+Provisioning Technology (as defined below), you must agree to a license ("Feature \n\
+Update License") during the installation process. If the Feature contains\n\
+Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform\n\
+you where you can locate them. Feature Update Licenses may be found in\n\
+the "license" property of files named "feature.properties" found within a Feature.\n\
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the\n\
+terms and conditions (or references to such terms and conditions) that\n\
+govern your use of the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER\n\
+TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\
+SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+       - Eclipse Distribution License Version 1.0 (available at http://www.eclipse.org/licenses/edl-v1.0.html)\n\
+       - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\
+       - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\
+       - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\
+       - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\
+TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License\n\
+is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\
+govern that particular Content.\n\
+\n\
+\n\Use of Provisioning Technology\n\
+\n\
+The Eclipse Foundation makes available provisioning software, examples of which include,\n\
+but are not limited to, p2 and the Eclipse Update Manager ("Provisioning Technology") for\n\
+the purpose of allowing users to install software, documentation, information and/or\n\
+other materials (collectively "Installable Software"). This capability is provided with\n\
+the intent of allowing such users to install, extend and update Eclipse-based products.\n\
+Information about packaging Installable Software is available at\n\
+http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
+\n\
+You may use Provisioning Technology to allow other parties to install Installable Software.\n\
+You shall be responsible for enabling the applicable license agreements relating to the\n\
+Installable Software to be presented to, and accepted by, the users of the Provisioning Technology\n\
+in accordance with the Specification. By using Provisioning Technology in such a manner and\n\
+making it available in accordance with the Specification, you further acknowledge your\n\
+agreement to, and the acquisition of all necessary rights to permit the following:\n\
+\n\
+       1. A series of actions may occur ("Provisioning Process") in which a user may execute\n\
+          the Provisioning Technology on a machine ("Target Machine") with the intent of installing,\n\
+          extending or updating the functionality of an Eclipse-based product.\n\
+       2. During the Provisioning Process, the Provisioning Technology may cause third party\n\
+          Installable Software or a portion thereof to be accessed and copied to the Target Machine.\n\
+       3. Pursuant to the Specification, you will provide to the user the terms and conditions that\n\
+          govern the use of the Installable Software ("Installable Software Agreement") and such\n\
+          Installable Software Agreement shall be accessed from the Target Machine in accordance\n\
+          with the Specification. Such Installable Software Agreement must inform the user of the\n\
+          terms and conditions that govern the Installable Software and must solicit acceptance by\n\
+          the end user in the manner prescribed in such Installable Software Agreement. Upon such\n\
+          indication of agreement by the user, the provisioning Technology will complete installation\n\
+          of the Installable Software.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are\n\
+currently may have restrictions on the import, possession, and use,\n\
+and/or re-export to another country, of encryption software. BEFORE\n\
+using any encryption software, please check the country's laws,\n\
+regulations and policies concerning the import, possession, or use, and\n\
+re-export of encryption software, to see if this is permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.\n
+########### end of license property ##########################################
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
new file mode 100644
index 0000000..829c056
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eclipse.jgit.lfs"
+      label="%featureName"
+      version="4.3.2.qualifier"
+      provider-name="%providerName">
+
+   <description url="http://www.eclipse.org/jgit/">
+      %description
+   </description>
+
+   <copyright>
+      %copyright
+   </copyright>
+
+   <license url="%licenseURL">
+      %license
+   </license>
+
+   <url>
+      <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+      <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
+   </url>
+
+   <requires>
+      <import plugin="org.eclipse.jgit"/>
+   </requires>
+
+   <plugin
+         id="org.eclipse.jgit.lfs"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jgit.lfs.server"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="com.google.gson"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html
new file mode 100644
index 0000000..95ad95e
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!-- saved from url=(0044)http://www.eclipse.org/legal/epl/notice.html -->
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+
+<title>Eclipse Foundation Software User Agreement</title>
+</head>
+
+<body lang="EN-US">
+<h2>Eclipse Foundation Software User Agreement</h2>
+<p>April 9, 2014</p>
+
+<h3>Usage Of Content</h3>
+
+<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+   (COLLECTIVELY "CONTENT").  USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+   CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW.  BY USING THE CONTENT, YOU AGREE THAT YOUR USE
+   OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
+   NOTICES INDICATED OR REFERENCED BELOW.  IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
+   CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
+
+<h3>Applicable Licenses</h3>
+
+<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
+   ("EPL").  A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+   For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
+   repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").</p>
+
+<ul>
+       <li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content.  Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
+       <li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".</li>
+       <li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.  Each Feature may be packaged as a sub-directory in a directory named "features".  Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
+      and/or Fragments associated with that Feature.</li>
+       <li>Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.</li>
+</ul>
+
+<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
+Included Features should be contained in files named "license.html" ("Feature Licenses").  Abouts and Feature Licenses may be located in any directory of a Download or Module
+including, but not limited to the following locations:</p>
+
+<ul>
+       <li>The top-level (root) directory</li>
+       <li>Plug-in and Fragment directories</li>
+       <li>Inside Plug-ins and Fragments packaged as JARs</li>
+       <li>Sub-directories of the directory named "src" of certain Plug-ins</li>
+       <li>Feature directories</li>
+</ul>
+
+<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
+installation process.  If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
+inform you where you can locate them.  Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
+that directory.</p>
+
+<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.  SOME OF THESE
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
+
+<ul>
+       <li>Eclipse Distribution License Version 1.0 (available at <a href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)</li>
+       <li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
+       <li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
+       <li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
+       <li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
+</ul>
+
+<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT.  If no About, Feature License, or Feature Update License is provided, please
+contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
+
+
+<h3>Use of Provisioning Technology</h3>
+
+<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
+   Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
+   other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
+   install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
+   ("Specification").</p>
+
+<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
+   applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
+   in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
+   Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
+
+<ol>
+       <li>A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
+       on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
+       product.</li>
+       <li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
+       accessed and copied to the Target Machine.</li>
+       <li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
+       Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
+       Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
+       the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
+       indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
+</ol>
+
+<h3>Cryptography</h3>
+
+<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
+   another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
+   possession, or use, and re-export of encryption software, to see if this is permitted.</p>
+
+<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
+
+
+</body></html>
\ No newline at end of file
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
new file mode 100644
index 0000000..bf96616
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com>
+   and other copyright owners as documented in the project's IP log.
+
+   This program and the accompanying materials are made available
+   under the terms of the Eclipse Distribution License v1.0 which
+   accompanies this distribution, is reproduced below, and is
+   available at http://www.eclipse.org/org/documents/edl-v10.php
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or
+   without modification, are permitted provided that the following
+   conditions are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above
+     copyright notice, this list of conditions and the following
+     disclaimer in the documentation and/or other materials provided
+     with the distribution.
+
+   - Neither the name of the Eclipse Foundation, Inc. nor the
+     names of its contributors may be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.eclipse.jgit</groupId>
+    <artifactId>jgit.tycho.parent</artifactId>
+    <version>4.3.2-SNAPSHOT</version>
+  </parent>
+
+  <groupId>org.eclipse.jgit.feature</groupId>
+  <artifactId>org.eclipse.jgit.lfs</artifactId>
+  <packaging>eclipse-feature</packaging>
+
+  <name>JGit - Optional LFS support</name>
+  <dependencies>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
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 67ff3cf..73a9a13 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.2.1.qualifier"
+      version="4.3.2.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -26,8 +26,13 @@
          id="org.eclipse.jgit"
          version="0.0.0"/>
 
+   <includes
+         id="org.eclipse.jgit.lfs"
+         version="0.0.0"/>
+
    <requires>
-      <import feature="org.eclipse.jgit" version="4.2.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="4.3.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="4.3.0" 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 8cbbdc5..2628721 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-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 d53df31..4cb5cb7 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.2.1.qualifier"
+      version="4.3.2.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 8dd726d..c5015c6 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index 2186ad9..c2bfc92 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -21,6 +21,12 @@
    <feature url="features/org.eclipse.jgit.http.apache_0.0.0.qualifier.jar" id="org.eclipse.jgit.http.apache" version="0.0.0" patch="true">
       <category name="JGit"/>
    </feature>
+   <feature url="features/org.eclipse.jgit.lfs_0.0.0.qualifier.jar" id="org.eclipse.jgit.lfs" version="0.0.0">
+      <category name="JGit"/>
+   </feature>
+   <feature url="features/org.eclipse.jgit.lfs_0.0.0.qualifier.jar" id="org.eclipse.jgit.lfs" version="0.0.0">
+      <category name="JGit"/>
+   </feature>
    <category-def name="JGit" label="JGit">
       <description>
          JGit
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 bf8c7cb..1410d68 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
@@ -70,6 +70,16 @@
 	  <version>${project.version}</version>
 	</dependency>
     <dependency>
+	  <groupId>org.eclipse.jgit</groupId>
+	  <artifactId>org.eclipse.jgit.lfs</artifactId>
+	  <version>${project.version}</version>
+	</dependency>
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jgit</groupId>
       <artifactId>org.eclipse.jgit.pgm</artifactId>
       <version>${project.version}</version>
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 38db4f6..4d49aae 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.2.1.qualifier"
+      version="4.3.2.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 ef6de2b..204026b 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-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 09e7c94..4e1f66f 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.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
index e8670e7..19edf51 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
@@ -45,6 +45,7 @@
       <unit id="org.objenesis.source" version="1.0.0.v201105211943"/>
       <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.jcraft.jsch" version="0.1.51.v201410302000"/>
       <unit id="com.jcraft.jsch.source" version="0.1.51.v201410302000"/>
       <unit id="org.junit" version="4.11.0.v201303080030"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
index 0b85d75..3451b42 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.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.4" sequenceNumber="1440024079">
+<target name="jgit-4.4" sequenceNumber="1444167820">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/>
@@ -45,6 +45,7 @@
       <unit id="org.objenesis.source" version="1.0.0.v201105211943"/>
       <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.jcraft.jsch" version="0.1.51.v201410302000"/>
       <unit id="com.jcraft.jsch.source" version="0.1.51.v201410302000"/>
       <unit id="org.junit" version="4.11.0.v201303080030"/>
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 2067482..08116eb 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="1440022750">
+<target name="jgit-4.5" sequenceNumber="1456522731">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/>
@@ -31,8 +31,8 @@
       <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
       <unit id="org.apache.httpcomponents.httpcore" version="4.3.3.v201411290715"/>
       <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/>
-      <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715"/>
-      <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715B"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715B"/>
       <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.0.21.v201301150030"/>
@@ -45,6 +45,7 @@
       <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.jcraft.jsch" version="0.1.53.v201508180515"/>
       <unit id="com.jcraft.jsch.source" version="0.1.53.v201508180515"/>
       <unit id="org.junit" version="4.11.0.v201303080030"/>
@@ -57,7 +58,7 @@
       <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
       <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
       <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
-      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/"/>
+      <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20160221192158/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 cdf24b5..5cd1037 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.2.13.tpd"
-include "orbit/R20150821153341-Mars.tpd"
+include "orbit/R20160221192158-Mars.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
new file mode 100644
index 0000000..e506aec
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -0,0 +1,68 @@
+<?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="1455835295">
+  <locations>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.http" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.io" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.security" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.server" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.util" version="9.2.13.v20150730"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.2.13.v20150730"/>
+      <repository id="jetty-9.2.13" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.2.13.v20150730/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.apache.ant" version="1.9.4.v201504302020"/>
+      <unit id="org.apache.ant.source" version="1.9.4.v201504302020"/>
+      <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.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.3.3.v201411290715"/>
+      <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/>
+      <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201511171540"/>
+      <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201511171540"/>
+      <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.0.21.v201301150030"/>
+      <unit id="org.kohsuke.args4j.source" version="2.0.21.v201301150030"/>
+      <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/>
+      <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/>
+      <unit id="javaewah" version="0.7.9.v201401101600"/>
+      <unit id="javaewah.source" version="0.7.9.v201401101600"/>
+      <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.jcraft.jsch" version="0.1.53.v201508180515"/>
+      <unit id="com.jcraft.jsch.source" version="0.1.53.v201508180515"/>
+      <unit id="org.junit" version="4.11.0.v201303080030"/>
+      <unit id="org.junit.source" version="4.11.0.v201303080030"/>
+      <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.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/S20151204220443/repository/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="org.eclipse.osgi" version="0.0.0"/>
+      <repository location="http://download.eclipse.org/releases/mars/"/>
+    </location>
+  </locations>
+</target>
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
new file mode 100644
index 0000000..3963361
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.5" with source configurePhase
+
+include "projects/jetty-9.2.13.tpd"
+include "orbit/S20151204220443-Neon.tpd"
+
+location "http://download.eclipse.org/releases/mars/" {
+	org.eclipse.osgi lazy
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd
index 38c9297..d9e072f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd
@@ -24,6 +24,7 @@
 	org.objenesis.source [1.0.0.v201105211943,1.0.0.v201105211943]
 	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.jcraft.jsch [0.1.51.v201410302000,0.1.51.v201410302000]
 	com.jcraft.jsch.source [0.1.51.v201410302000,0.1.51.v201410302000]
 	org.junit [4.11.0.v201303080030,4.11.0.v201303080030]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20160221192158-Mars.tpd
similarity index 89%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20160221192158-Mars.tpd
index cb63807..5e47f68 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20160221192158-Mars.tpd
@@ -1,7 +1,7 @@
-target "R20150821153341-Mars" with source configurePhase
+target "R20160221192158-Mars" with source configurePhase
 // see http://download.eclipse.org/tools/orbit/downloads/
 
-location "http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/" {
+location "http://download.eclipse.org/tools/orbit/downloads/drops/R20160221192158/repository/" {
 	org.apache.ant [1.9.4.v201504302020,1.9.4.v201504302020]
 	org.apache.ant.source [1.9.4.v201504302020,1.9.4.v201504302020]
 	org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400]
@@ -10,8 +10,8 @@
 	org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721]
 	org.apache.httpcomponents.httpcore [4.3.3.v201411290715,4.3.3.v201411290715]
 	org.apache.httpcomponents.httpcore.source [4.3.3.v201411290715,4.3.3.v201411290715]
-	org.apache.httpcomponents.httpclient [4.3.6.v201411290715,4.3.6.v201411290715]
-	org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715,4.3.6.v201411290715]
+	org.apache.httpcomponents.httpclient [4.3.6.v201411290715B,4.3.6.v201411290715B]
+	org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715B,4.3.6.v201411290715B]
 	org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
 	org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
 	org.kohsuke.args4j [2.0.21.v201301150030,2.0.21.v201301150030]
@@ -24,6 +24,7 @@
 	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.jcraft.jsch [0.1.53.v201508180515,0.1.53.v201508180515]
 	com.jcraft.jsch.source [0.1.53.v201508180515,0.1.53.v201508180515]
 	org.junit [4.11.0.v201303080030,4.11.0.v201303080030]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20151204220443-Neon.tpd
similarity index 87%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20151204220443-Neon.tpd
index cb63807..3afdbaa 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20151204220443-Neon.tpd
@@ -1,7 +1,7 @@
-target "R20150821153341-Mars" with source configurePhase
+target "S20151204220443-Neon" with source configurePhase
 // see http://download.eclipse.org/tools/orbit/downloads/
 
-location "http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/" {
+location "http://download.eclipse.org/tools/orbit/downloads/drops/S20151204220443/repository/" {
 	org.apache.ant [1.9.4.v201504302020,1.9.4.v201504302020]
 	org.apache.ant.source [1.9.4.v201504302020,1.9.4.v201504302020]
 	org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400]
@@ -10,8 +10,8 @@
 	org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721]
 	org.apache.httpcomponents.httpcore [4.3.3.v201411290715,4.3.3.v201411290715]
 	org.apache.httpcomponents.httpcore.source [4.3.3.v201411290715,4.3.3.v201411290715]
-	org.apache.httpcomponents.httpclient [4.3.6.v201411290715,4.3.6.v201411290715]
-	org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715,4.3.6.v201411290715]
+	org.apache.httpcomponents.httpclient [4.3.6.v201511171540,4.3.6.v201511171540]
+	org.apache.httpcomponents.httpclient.source [4.3.6.v201511171540,4.3.6.v201511171540]
 	org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
 	org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
 	org.kohsuke.args4j [2.0.21.v201301150030,2.0.21.v201301150030]
@@ -24,6 +24,7 @@
 	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.jcraft.jsch [0.1.53.v201508180515,0.1.53.v201508180515]
 	com.jcraft.jsch.source [0.1.53.v201508180515,0.1.53.v201508180515]
 	org.junit [4.11.0.v201303080030,4.11.0.v201303080030]
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 a4fcefa..4696b82 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-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 7231c9c..39b4be9 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright (C) 2009, 2013, Matthias Sohn <matthias.sohn@sap.com>
+   Copyright (C) 2009, 2015, Matthias Sohn <matthias.sohn@sap.com>
    and other copyright owners as documented in the project's IP log.
 
    This program and the accompanying materials are made available
@@ -53,7 +53,7 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>4.2.1-SNAPSHOT</version>
+  <version>4.3.2-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
@@ -61,6 +61,7 @@
   <properties>
     <tycho-version>0.23.0</tycho-version>
     <tycho-extras-version>${tycho-version}</tycho-extras-version>
+    <target-platform>jgit-4.5</target-platform>
   </properties>
 
   <pluginRepositories>
@@ -78,6 +79,7 @@
     <module>org.eclipse.jgit.target</module>
     <module>org.eclipse.jgit.feature</module>
     <module>org.eclipse.jgit.http.apache.feature</module>
+    <module>org.eclipse.jgit.lfs.feature</module>
     <module>org.eclipse.jgit.pgm.feature</module>
     <module>org.eclipse.jgit.source.feature</module>
     <module>org.eclipse.jgit.pgm.source.feature</module>
@@ -227,43 +229,6 @@
 
   <profiles>
     <profile>
-      <id>platform-kepler</id>
-      <activation>
-        <property>
-          <name>platform-version-name</name>
-          <value>kepler</value>
-        </property>
-      </activation>
-      <properties>
-        <target-platform>jgit-4.3</target-platform>
-      </properties>
-    </profile>
-    <profile>
-      <id>platform-luna</id>
-      <activation>
-        <property>
-          <name>platform-version-name</name>
-          <value>luna</value>
-        </property>
-      </activation>
-      <properties>
-        <target-platform>jgit-4.4</target-platform>
-      </properties>
-    </profile>
-    <profile>
-      <id>platform-mars</id>
-      <activation>
-        <activeByDefault>true</activeByDefault>
-        <property>
-          <name>platform-version-name</name>
-          <value>mars</value>
-        </property>
-      </activation>
-      <properties>
-        <target-platform>jgit-4.5</target-platform>
-      </properties>
-    </profile>
-    <profile>
       <id>eclipse-sign</id>
       <build>
         <plugins>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index b64f5f0..097319d 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -2,28 +2,28 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Import-Package: org.eclipse.jgit.api;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.diff;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="4.2.1",
- org.eclipse.jgit.junit;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.merge;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.pgm;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.pgm.internal;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.pgm.opt;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.diff;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.3.2",
+ org.eclipse.jgit.junit;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.pgm;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.4.0,5.0.0)",
  org.kohsuke.args4j;version="[2.0.12,2.1.0)"
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 135bf65..4225323 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm/BUCK b/org.eclipse.jgit.pgm/BUCK
index edcf2fc..47711b5 100644
--- a/org.eclipse.jgit.pgm/BUCK
+++ b/org.eclipse.jgit.pgm/BUCK
@@ -9,8 +9,18 @@
     '//org.eclipse.jgit:jgit',
     '//org.eclipse.jgit.archive:jgit-archive',
     '//org.eclipse.jgit.http.apache:http-apache',
+    '//org.eclipse.jgit.lfs.server:jgit-lfs-server',
     '//org.eclipse.jgit.ui:ui',
     '//lib:args4j',
+    '//lib:httpcomponents',
+    '//lib:httpcore',
+    '//lib/jetty:http',
+    '//lib/jetty:io',
+    '//lib/jetty:server',
+    '//lib/jetty:servlet',
+    '//lib/jetty:security',
+    '//lib/jetty:util',
+    '//lib:servlet-api'
   ],
   visibility = ['PUBLIC'],
 )
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 5c6a16b..ab0c11b 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -2,48 +2,69 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Import-Package: org.apache.commons.compress.archivers;version="[1.3,2.0)",
+Import-Package: javax.servlet;version="[3.1.0,4.0.0)",
+ javax.servlet.http;version="[3.1.0,4.0.0)",
+ org.apache.commons.compress.archivers;version="[1.3,2.0)",
  org.apache.commons.compress.archivers.tar;version="[1.3,2.0)",
  org.apache.commons.compress.archivers.zip;version="[1.3,2.0)",
- org.eclipse.jgit.api;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.archive;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.awtui;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.blame;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.diff;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.gitrepo;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.merge;version="4.2.1",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.notes;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revplot;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.pack;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)",
+ org.eclipse.jetty.continuation;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.http;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.io;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
+ org.eclipse.jgit.api;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.archive;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.awtui;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.blame;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.diff;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.gitrepo;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.ketch;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.server;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.notes;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revplot;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.pack;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)",
  org.kohsuke.args4j;version="[2.0.12,2.1.0)",
  org.kohsuke.args4j.spi;version="[2.0.15,2.1.0)"
-Export-Package: org.eclipse.jgit.console;version="4.2.1";
+Export-Package: org.eclipse.jgit.console;version="4.3.2";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="4.2.1";
+ org.eclipse.jgit.pgm;version="4.3.2";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.pgm.opt,
@@ -54,11 +75,11 @@
    org.eclipse.jgit.treewalk,
    javax.swing,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="4.2.1";
+ org.eclipse.jgit.pgm.debug;version="4.3.2";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="4.2.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="4.2.1";
+ org.eclipse.jgit.pgm.internal;version="4.3.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.3.2";
   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 5df8f53..3bbff12 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.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.2.1.qualifier";roots="."
+Bundle-Version: 4.3.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.3.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 6aa2004..c159f7d 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -38,6 +38,7 @@
 org.eclipse.jgit.pgm.Version
 
 org.eclipse.jgit.pgm.debug.DiffAlgorithms
+org.eclipse.jgit.pgm.debug.LfsStore
 org.eclipse.jgit.pgm.debug.MakeCacheTree
 org.eclipse.jgit.pgm.debug.ReadDirCache
 org.eclipse.jgit.pgm.debug.RebuildCommitGraph
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index b9ad706..8edb94a 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
@@ -122,6 +122,34 @@
       <artifactId>log4j</artifactId>
       <version>${log4j-version}</version>
     </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.junit.http</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jgit</groupId>
+      <artifactId>org.eclipse.jgit.lfs.server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index b4b1261..fc412ba 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -74,6 +74,12 @@
 jgitVersion=jgit version {0}
 lineFormat={0}
 listeningOn=Listening on {0}
+lfsNoAccessKey=No accessKey in {0}
+lfsNoSecretKey=No secretKey in {0}
+lfsProtocolUrl=LFS protocol URL: {0}
+lfsStoreDirectory=LFS objects stored in: {0}
+lfsStoreUrl=LFS store URL: {0}
+lfsUnknownStoreType="Unknown LFS store type: {0}"
 mergeConflict=CONFLICT(content): Merge conflict in {0}
 mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge:
 mergeFailed=Automatic merge failed; fix conflicts and then commit the result
@@ -107,6 +113,7 @@
 metaVar_filepattern=filepattern
 metaVar_gitDir=GIT_DIR
 metaVar_hostName=HOSTNAME
+metaVar_lfsStorage=STORAGE
 metaVar_linesOfContext=lines
 metaVar_message=message
 metaVar_n=n
@@ -123,6 +130,9 @@
 metaVar_refspec=refspec
 metaVar_remoteName=name
 metaVar_revision=REVISION
+metaVar_s3Bucket=BUCKET
+metaVar_s3Region=REGION
+metaVar_s3StorageClass=STORAGE-CLASS
 metaVar_seconds=SECONDS
 metaVar_service=SERVICE
 metaVar_treeish=tree-ish
@@ -168,6 +178,7 @@
 remoteRefObjectChangedIsNotExpectedOne=remote ref object changed - is not expected one {0}
 remoteSideDoesNotSupportDeletingRefs=remote side does not support deleting refs
 repaint=Repaint
+s3InvalidBucket=Invalid S3 bucket ''{0}''
 serviceNotSupported=Service ''{0}'' not supported
 skippingObject=skipping {0} {1}
 statusFileListFormat=\t%1$s
@@ -218,6 +229,10 @@
 usage_Gc=Cleanup unnecessary files and optimize the local repository
 usage_Glog=View commit history as a graph
 usage_IndexPack=Build pack index file for an existing packed archive
+usage_LFSDirectory=Directory to store large objects
+usage_LFSPort=Server http port
+usage_LFSRunStore=Store (fs | s3), store lfs objects in file system or Amazon S3
+usage_LFSStoreUrl=URL of the LFS store
 usage_LongFormat=Always output the long format
 usage_LsRemote=List references in a remote repository
 usage_lsRemoteHeads=Show only refs starting with refs/heads
@@ -229,12 +244,19 @@
 usage_ReadDirCache= Read the DirCache 100 times
 usage_RebuildCommitGraph=Recreate a repository from another one's commit graph
 usage_RebuildRefTree=Copy references into a RefTree
+usage_RebuildRefTreeEnable=set extensions.refsStorage = reftree
 usage_Remote=Manage set of tracked repositories
 usage_RepositoryToReadFrom=Repository to read from
 usage_RepositoryToReceiveInto=Repository to receive into
 usage_RevList=List commit objects in reverse chronological order
 usage_RevParse=Pick out and massage parameters
 usage_RevParseAll=Show all refs found in refs/
+usage_S3Bucket=S3 bucket name
+usage_S3Expiration=Authorization validity in seconds, default 60 sec
+usage_S3Region=S3 region (us-east-1 | us-west-1 | us-west-2 | eu-west-1 |\
+ eu-central-1 | ap-southeast-1 | ap- southeast-2 | ap-northeast-1 |\
+ ap-northeast-2 | sa-east-1), see http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
+usage_S3StorageClass=S3 storage class (REDUCED_REDUNDANCY | STANDARD), default REDUCED_REDUNDANCY
 usage_ServerSideBackendForJgitFetch=Server side backend for 'jgit fetch'
 usage_ServerSideBackendForJgitPush=Server side backend for 'jgit push'
 usage_ShowCacheTree=Show cache tree
@@ -347,6 +369,8 @@
 usage_resetHard=Resets the index and working tree
 usage_resetSoft=Resets without touching the index file nor the working tree
 usage_resetMixed=Resets the index but not the working tree
+usage_runLfsStore=Run LFS Store in a given directory
+usage_S3NoSslVerify=Skip verification of Amazon server certificate and hostname
 usage_setTheGitRepositoryToOperateOn=set the git repository to operate on
 usage_show=display one commit
 usage_showRefNamesMatchingCommits=Show ref names matching commits
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
index 04182d6..03f3fac 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
@@ -45,18 +45,29 @@
 
 import java.io.File;
 import java.net.InetSocketAddress;
+import java.net.URISyntaxException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executors;
 
+import org.eclipse.jgit.internal.ketch.KetchLeader;
+import org.eclipse.jgit.internal.ketch.KetchLeaderCache;
+import org.eclipse.jgit.internal.ketch.KetchPreReceive;
+import org.eclipse.jgit.internal.ketch.KetchSystem;
+import org.eclipse.jgit.internal.ketch.KetchText;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.pgm.internal.CLIText;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.storage.file.WindowCacheConfig;
 import org.eclipse.jgit.storage.pack.PackConfig;
 import org.eclipse.jgit.transport.DaemonClient;
 import org.eclipse.jgit.transport.DaemonService;
+import org.eclipse.jgit.transport.ReceivePack;
 import org.eclipse.jgit.transport.resolver.FileResolver;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
 import org.eclipse.jgit.util.FS;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
@@ -90,6 +101,13 @@
 	@Option(name = "--export-all", usage = "usage_exportWithoutGitDaemonExportOk")
 	boolean exportAll;
 
+	@Option(name = "--ketch")
+	KetchServerType ketchServerType;
+
+	enum KetchServerType {
+		LEADER;
+	}
+
 	@Argument(required = true, metaVar = "metaVar_directory", usage = "usage_directoriesToExport")
 	final List<File> directory = new ArrayList<File>();
 
@@ -146,7 +164,9 @@
 			service(d, n).setOverridable(true);
 		for (final String n : forbidOverride)
 			service(d, n).setOverridable(false);
-
+		if (ketchServerType == KetchServerType.LEADER) {
+			startKetchLeader(d);
+		}
 		d.start();
 		outw.println(MessageFormat.format(CLIText.get().listeningOn, d.getAddress()));
 	}
@@ -159,4 +179,29 @@
 			throw die(MessageFormat.format(CLIText.get().serviceNotSupported, n));
 		return svc;
 	}
+
+	private void startKetchLeader(org.eclipse.jgit.transport.Daemon daemon) {
+		KetchSystem system = new KetchSystem();
+		final KetchLeaderCache leaders = new KetchLeaderCache(system);
+		final ReceivePackFactory<DaemonClient> factory;
+
+		factory = daemon.getReceivePackFactory();
+		daemon.setReceivePackFactory(new ReceivePackFactory<DaemonClient>() {
+			@Override
+			public ReceivePack create(DaemonClient req, Repository repo)
+					throws ServiceNotEnabledException,
+					ServiceNotAuthorizedException {
+				ReceivePack rp = factory.create(req, repo);
+				KetchLeader leader;
+				try {
+					leader = leaders.get(repo);
+				} catch (URISyntaxException err) {
+					throw new ServiceNotEnabledException(
+							KetchText.get().invalidFollowerUri, err);
+				}
+				rp.setPreReceiveHook(new KetchPreReceive(leader));
+				return rp;
+			}
+		});
+	}
 }
\ No newline at end of file
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
new file mode 100644
index 0000000..00bea86
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
@@ -0,0 +1,317 @@
+/*
+ * 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.pgm.debug;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lfs.server.LargeFileRepository;
+import org.eclipse.jgit.lfs.server.LfsProtocolServlet;
+import org.eclipse.jgit.lfs.server.fs.FileLfsServlet;
+import org.eclipse.jgit.lfs.server.fs.FileLfsRepository;
+import org.eclipse.jgit.lfs.server.s3.S3Config;
+import org.eclipse.jgit.lfs.server.s3.S3Repository;
+import org.eclipse.jgit.pgm.Command;
+import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+@Command(common = true, usage = "usage_runLfsStore")
+class LfsStore extends TextBuiltin {
+
+	/**
+	 * Tiny web application server for testing
+	 */
+	class AppServer {
+
+		private final Server server;
+
+		private final ServerConnector connector;
+
+		private final ContextHandlerCollection contexts;
+
+		private URI uri;
+
+		AppServer(int port) {
+			server = new Server();
+
+			HttpConfiguration http_config = new HttpConfiguration();
+			http_config.setOutputBufferSize(32768);
+
+			connector = new ServerConnector(server,
+					new HttpConnectionFactory(http_config));
+			connector.setPort(port);
+			try {
+				String host = InetAddress.getByName("localhost") //$NON-NLS-1$
+						.getHostAddress();
+				connector.setHost(host);
+				if (host.contains(":") && !host.startsWith("[")) //$NON-NLS-1$ //$NON-NLS-2$
+					host = "[" + host + "]"; //$NON-NLS-1$//$NON-NLS-2$
+				uri = new URI("http://" + host + ":" + port); //$NON-NLS-1$ //$NON-NLS-2$
+			} catch (UnknownHostException e) {
+				throw new RuntimeException("Cannot find localhost", e); //$NON-NLS-1$
+			} catch (URISyntaxException e) {
+				throw new RuntimeException("Unexpected URI error on " + uri, e); //$NON-NLS-1$
+			}
+
+			contexts = new ContextHandlerCollection();
+			server.setHandler(contexts);
+			server.setConnectors(new Connector[] { connector });
+		}
+
+		/**
+		 * Create a new servlet context within the server.
+		 * <p>
+		 * This method should be invoked before the server is started, once for
+		 * each context the caller wants to register.
+		 *
+		 * @param path
+		 *            path of the context; use "/" for the root context if
+		 *            binding to the root is desired.
+		 * @return the context to add servlets into.
+		 */
+		ServletContextHandler addContext(String path) {
+			assertNotRunning();
+			if ("".equals(path)) //$NON-NLS-1$
+				path = "/"; //$NON-NLS-1$
+
+			ServletContextHandler ctx = new ServletContextHandler();
+			ctx.setContextPath(path);
+			contexts.addHandler(ctx);
+
+			return ctx;
+		}
+
+		void start() throws Exception {
+			server.start();
+		}
+
+		void stop() throws Exception {
+			server.stop();
+		}
+
+		URI getURI() {
+			return uri;
+		}
+
+		private void assertNotRunning() {
+			if (server.isRunning()) {
+				throw new IllegalStateException("server is running"); //$NON-NLS-1$
+			}
+		}
+	}
+
+	private static enum StoreType {
+		FS, S3;
+	}
+
+	private static enum StorageClass {
+		REDUCED_REDUNDANCY, STANDARD
+	}
+
+	private static final String OBJECTS = "objects/"; //$NON-NLS-1$
+
+	private static final String STORE_PATH = "/" + OBJECTS + "*"; //$NON-NLS-1$//$NON-NLS-2$
+
+	private static final String PROTOCOL_PATH = "/lfs/objects/batch"; //$NON-NLS-1$
+
+	@Option(name = "--port", aliases = {"-p" },
+			metaVar = "metaVar_port", usage = "usage_LFSPort")
+	int port;
+
+	@Option(name = "--store", metaVar = "metaVar_lfsStorage", usage = "usage_LFSRunStore")
+	StoreType storeType;
+
+	@Option(name = "--store-url", aliases = {"-u" }, metaVar = "metaVar_url",
+			usage = "usage_LFSStoreUrl")
+	String storeUrl;
+
+	@Option(name = "--region", aliases = {"-r" },
+			metaVar = "metaVar_s3Region", usage = "usage_S3Region")
+	String region; // $NON-NLS-1$
+
+	@Option(name = "--bucket", aliases = {"-b" },
+			metaVar = "metaVar_s3Bucket", usage = "usage_S3Bucket")
+	String bucket; // $NON-NLS-1$
+
+	@Option(name = "--storage-class", aliases = {"-c" },
+			metaVar = "metaVar_s3StorageClass", usage = "usage_S3StorageClass")
+	StorageClass storageClass = StorageClass.REDUCED_REDUNDANCY;
+
+	@Option(name = "--expire", aliases = {"-e" },
+			metaVar = "metaVar_seconds", usage = "usage_S3Expiration")
+	int expirationSeconds = 600;
+
+	@Option(name = "--no-ssl-verify", usage = "usage_S3NoSslVerify")
+	boolean disableSslVerify = false;
+
+	@Argument(required = false, metaVar = "metaVar_directory", usage = "usage_LFSDirectory")
+	String directory;
+
+	String protocolUrl;
+
+	String accessKey;
+
+	String secretKey;
+
+	@Override
+	protected boolean requiresRepository() {
+		return false;
+	}
+
+	protected void run() throws Exception {
+		AppServer server = new AppServer(port);
+		URI baseURI = server.getURI();
+		ServletContextHandler app = server.addContext("/"); //$NON-NLS-1$
+
+		final LargeFileRepository repository;
+		switch (storeType) {
+		case FS:
+			Path dir = Paths.get(directory);
+			FileLfsRepository fsRepo = new FileLfsRepository(
+					getStoreUrl(baseURI), dir);
+			FileLfsServlet content = new FileLfsServlet(fsRepo, 30000);
+			app.addServlet(new ServletHolder(content), STORE_PATH);
+			repository = fsRepo;
+			break;
+
+		case S3:
+			readAWSKeys();
+			checkOptions();
+			S3Config config = new S3Config(region.toString(), bucket,
+					storageClass.toString(), accessKey, secretKey,
+					expirationSeconds, disableSslVerify);
+			repository = new S3Repository(config);
+			break;
+		default:
+			throw new IllegalArgumentException(MessageFormat
+					.format(CLIText.get().lfsUnknownStoreType, storeType));
+		}
+
+		LfsProtocolServlet protocol = new LfsProtocolServlet() {
+
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected LargeFileRepository getLargeFileRepository() {
+				return repository;
+			}
+
+		};
+		app.addServlet(new ServletHolder(protocol), PROTOCOL_PATH);
+
+		server.start();
+
+		outw.println(MessageFormat.format(CLIText.get().lfsProtocolUrl,
+				getProtocolUrl(baseURI)));
+		if (storeType == StoreType.FS) {
+			outw.println(MessageFormat.format(CLIText.get().lfsStoreDirectory,
+					directory));
+			outw.println(MessageFormat.format(CLIText.get().lfsStoreUrl,
+					getStoreUrl(baseURI)));
+		}
+	}
+
+	private void checkOptions() {
+		if (bucket == null || bucket.length() == 0) {
+			throw die(MessageFormat.format(CLIText.get().s3InvalidBucket,
+					bucket));
+		}
+	}
+
+	private void readAWSKeys() throws IOException, ConfigInvalidException {
+		String credentialsPath = System.getProperty("user.home") //$NON-NLS-1$
+				+ "/.aws/credentials"; //$NON-NLS-1$
+		FileBasedConfig c = new FileBasedConfig(new File(credentialsPath),
+				FS.DETECTED);
+		c.load();
+		accessKey = c.getString("default", null, "accessKey"); //$NON-NLS-1$//$NON-NLS-2$
+		secretKey = c.getString("default", null, "secretKey"); //$NON-NLS-1$ //$NON-NLS-2$
+		if (accessKey == null || accessKey.isEmpty()) {
+			throw die(MessageFormat.format(CLIText.get().lfsNoAccessKey,
+					credentialsPath));
+		}
+		if (secretKey == null || secretKey.isEmpty()) {
+			throw die(MessageFormat.format(CLIText.get().lfsNoSecretKey,
+					credentialsPath));
+		}
+	}
+
+	private String getStoreUrl(URI baseURI) {
+		if (storeUrl == null) {
+			if (storeType == StoreType.FS) {
+				storeUrl = baseURI + "/" + OBJECTS; //$NON-NLS-1$
+			} else {
+				die("Local store not running and no --store-url specified"); //$NON-NLS-1$
+			}
+		}
+		return storeUrl;
+	}
+
+	private String getProtocolUrl(URI baseURI) {
+		if (protocolUrl == null) {
+			protocolUrl = baseURI + PROTOCOL_PATH;
+		}
+		return protocolUrl;
+	}
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
index 78ca1a7..57345e2 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
@@ -43,9 +43,11 @@
 
 package org.eclipse.jgit.pgm.debug;
 
+import static org.eclipse.jgit.lib.Constants.HEAD;
+
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 import org.eclipse.jgit.internal.storage.reftree.RefTree;
 import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
@@ -57,12 +59,17 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.pgm.Command;
 import org.eclipse.jgit.pgm.TextBuiltin;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.kohsuke.args4j.Option;
 
 @Command(usage = "usage_RebuildRefTree")
 class RebuildRefTree extends TextBuiltin {
+	@Option(name = "--enable", usage = "usage_RebuildRefTreeEnable")
+	boolean enable;
+
 	private String txnNamespace;
 	private String txnCommitted;
 
@@ -103,7 +110,7 @@
 				oldTreeId = ObjectId.zeroId();
 			}
 
-			RefTree tree = rebuild(refDb.getRefs(RefDatabase.ALL));
+			RefTree tree = rebuild(refDb);
 			b.setTreeId(tree.writeTree(inserter));
 			b.setAuthor(new PersonIdent(db));
 			b.setCommitter(b.getAuthor());
@@ -122,16 +129,32 @@
 			default:
 				throw die(String.format("%s: %s", update.getName(), result)); //$NON-NLS-1$
 			}
+
+			if (enable && !(db.getRefDatabase() instanceof RefTreeDatabase)) {
+				StoredConfig cfg = db.getConfig();
+				cfg.setInt("core", null, "repositoryformatversion", 1); //$NON-NLS-1$ //$NON-NLS-2$
+				cfg.setString("extensions", null, "refsStorage", "reftree"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+				cfg.save();
+				errw.println("Enabled reftree."); //$NON-NLS-1$
+				errw.flush();
+			}
 		}
 	}
 
-	private RefTree rebuild(Map<String, Ref> refMap) {
+	private RefTree rebuild(RefDatabase refdb) throws IOException {
 		RefTree tree = RefTree.newEmptyTree();
 		List<org.eclipse.jgit.internal.storage.reftree.Command> cmds
 			= new ArrayList<>();
 
-		for (Ref r : refMap.values()) {
-			if (r.getName().equals(txnCommitted)
+		Ref head = refdb.exactRef(HEAD);
+		if (head != null) {
+			cmds.add(new org.eclipse.jgit.internal.storage.reftree.Command(
+					null,
+					head));
+		}
+
+		for (Ref r : refdb.getRefs(RefDatabase.ALL).values()) {
+			if (r.getName().equals(txnCommitted) || r.getName().equals(HEAD)
 					|| r.getName().startsWith(txnNamespace)) {
 				continue;
 			}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 2812137..e99fe00 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -150,6 +150,12 @@
 	/***/ public String initializedEmptyGitRepositoryIn;
 	/***/ public String invalidHttpProxyOnlyHttpSupported;
 	/***/ public String jgitVersion;
+	/***/ public String lfsNoAccessKey;
+	/***/ public String lfsNoSecretKey;
+	/***/ public String lfsProtocolUrl;
+	/***/ public String lfsStoreDirectory;
+	/***/ public String lfsStoreUrl;
+	/***/ public String lfsUnknownStoreType;
 	/***/ public String lineFormat;
 	/***/ public String listeningOn;
 	/***/ public String mergeCheckoutConflict;
@@ -178,6 +184,7 @@
 	/***/ public String metaVar_filepattern;
 	/***/ public String metaVar_gitDir;
 	/***/ public String metaVar_hostName;
+	/***/ public String metaVar_lfsStorage;
 	/***/ public String metaVar_linesOfContext;
 	/***/ public String metaVar_message;
 	/***/ public String metaVar_n;
@@ -192,6 +199,9 @@
 	/***/ public String metaVar_refs;
 	/***/ public String metaVar_refspec;
 	/***/ public String metaVar_remoteName;
+	/***/ public String metaVar_s3Bucket;
+	/***/ public String metaVar_s3Region;
+	/***/ public String metaVar_s3StorageClass;
 	/***/ public String metaVar_seconds;
 	/***/ public String metaVar_service;
 	/***/ public String metaVar_treeish;
@@ -237,6 +247,7 @@
 	/***/ public String remoteRefObjectChangedIsNotExpectedOne;
 	/***/ public String remoteSideDoesNotSupportDeletingRefs;
 	/***/ public String repaint;
+	/***/ public String s3InvalidBucket;
 	/***/ public String serviceNotSupported;
 	/***/ public String skippingObject;
 	/***/ public String statusFileListFormat;
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index abb86e8..4c47ce5 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -2,52 +2,52 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
- org.eclipse.jgit.api;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.attributes;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.awtui;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.blame;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.diff;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.events;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.fnmatch;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.gitrepo;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.hooks;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.ignore;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.ignore.internal;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.junit;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.merge;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.notes;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.patch;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.pgm;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.pgm.internal;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revplot;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.storage.pack;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.submodule;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.http;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)",
+ org.eclipse.jgit.api;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.attributes;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.awtui;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.blame;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.diff;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.events;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.fnmatch;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.gitrepo;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.hooks;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.ignore;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.notes;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.patch;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.pgm;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revplot;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.storage.pack;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.submodule;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)",
  org.hamcrest;version="[1.1.0,2.0.0)",
  org.junit;version="[4.4.0,5.0.0)",
  org.junit.experimental.theories;version="[4.4.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 0985c3d..a52ac4c 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII.patch
new file mode 100644
index 0000000..d6408c9
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII.patch
@@ -0,0 +1,8 @@
+diff --git a/NonASCII b/NonASCII
+index 2e65efe..7898192 100644
+--- a/NonASCII
++++ b/NonASCII
+@@ -1 +1 @@
+-あ
+\ No newline at end of file
++あ
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2.patch
new file mode 100644
index 0000000..cea6cfd
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2.patch
@@ -0,0 +1,9 @@
+diff --git a/NonASCII2 b/NonASCII2
+index 2e65efe..7898192 100644
+--- a/NonASCII2
++++ b/NonASCII2
+@@ -1 +1 @@
+-你好, 世界!
+\ No newline at end of file
++再见
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PostImage
new file mode 100644
index 0000000..5c8e34d
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PostImage
@@ -0,0 +1 @@
+再见
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PreImage
new file mode 100644
index 0000000..4369661
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PreImage
@@ -0,0 +1 @@
+你好, 世界!
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd.patch
new file mode 100644
index 0000000..de31a9e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd.patch
@@ -0,0 +1,7 @@
+diff --git a/NonASCIIAdd b/NonASCIIAdd
+index 2e65efe..7898192 100644
+--- a/NonASCIIAdd
++++ b/NonASCIIAdd
+@@ -0,0 +1 @@
++あ
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2.patch
new file mode 100644
index 0000000..5b41e9c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2.patch
@@ -0,0 +1,8 @@
+diff --git a/NonASCIIAdd2 b/NonASCIIAdd2
+new file mode 100644
+index 2e65efe..7898192 100644
+--- /dev/null
++++ b/NonASCIIAdd2
+@@ -0,0 +1 @@
++あ
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2_PostImage
new file mode 100644
index 0000000..0575c79
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2_PostImage
@@ -0,0 +1 @@
+あ
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PostImage
new file mode 100644
index 0000000..0575c79
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PostImage
@@ -0,0 +1 @@
+あ
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PreImage
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PreImage
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel.patch
new file mode 100644
index 0000000..80e8f82
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel.patch
@@ -0,0 +1,9 @@
+diff --git a/NonASCIIDel b/NonASCIIDel
+deleted file mode 100644
+new file mode 100644
+index 2e65efe..7898192 100644
+--- a/NonASCIIDel
++++ /dev/null
+@@ -1 +0,0 @@
+-あ
+\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel_PreImage
new file mode 100644
index 0000000..0575c79
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel_PreImage
@@ -0,0 +1 @@
+あ
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PostImage
new file mode 100644
index 0000000..f2435a2
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PostImage
@@ -0,0 +1 @@
+あ
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PreImage
new file mode 100644
index 0000000..0575c79
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PreImage
@@ -0,0 +1 @@
+あ
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W.patch
new file mode 100644
index 0000000..cfecb8c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W.patch
@@ -0,0 +1,7 @@
+diff --git a/W b/W
+index a3648a1..2d44096 100644
+--- a/W
++++ b/W
+@@ -1 +0,0 @@
+-a
+\ No newline at end of file
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PostImage
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PostImage
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PreImage
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PreImage
@@ -0,0 +1 @@
+a
\ No newline at end of file
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 4fefdfd..126ca5c 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
@@ -53,6 +53,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Set;
 
 import org.eclipse.jgit.api.errors.FilterFailedException;
 import org.eclipse.jgit.api.errors.GitAPIException;
@@ -62,14 +63,11 @@
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lib.ConfigConstants;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.lib.*;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeOptions;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Test;
@@ -1002,6 +1000,91 @@
 		assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
 	}
 
+	@Test
+	public void testAddGitlink() throws Exception {
+		createNestedRepo("git-link-dir");
+		try (Git git = new Git(db)) {
+			git.add().addFilepattern("git-link-dir").call();
+
+			assertEquals(
+					"[git-link-dir, mode:160000]",
+					indexState(0));
+			Set<String> untrackedFiles = git.status().call().getUntracked();
+			assert (untrackedFiles.isEmpty());
+		}
+
+	}
+
+	@Test
+	public void testAddSubrepoWithDirNoGitlinks() throws Exception {
+		createNestedRepo("nested-repo");
+
+		// Set DIR_NO_GITLINKS
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
+		config.save();
+
+		assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
+
+		try (Git git = new Git(db)) {
+			git.add().addFilepattern("nested-repo").call();
+
+			assertEquals(
+					"[nested-repo/README1.md, mode:100644]" +
+							"[nested-repo/README2.md, mode:100644]",
+					indexState(0));
+		}
+
+		// Turn off DIR_NO_GITLINKS, ensure nested-repo is still treated as
+		// a normal directory
+		// Set DIR_NO_GITLINKS
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, false);
+		config.save();
+
+		writeTrashFile("nested-repo", "README3.md", "content");
+
+		try (Git git = new Git(db)) {
+			git.add().addFilepattern("nested-repo").call();
+
+			assertEquals(
+					"[nested-repo/README1.md, mode:100644]" +
+							"[nested-repo/README2.md, mode:100644]" +
+							"[nested-repo/README3.md, mode:100644]",
+					indexState(0));
+		}
+	}
+
+	@Test
+	public void testAddGitlinkDoesNotChange() throws Exception {
+		createNestedRepo("nested-repo");
+
+		try (Git git = new Git(db)) {
+			git.add().addFilepattern("nested-repo").call();
+
+			assertEquals(
+					"[nested-repo, mode:160000]",
+					indexState(0));
+		}
+
+		// Set DIR_NO_GITLINKS
+		StoredConfig config = db.getConfig();
+		config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
+		config.save();
+
+		assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
+
+		try (Git git = new Git(db)) {
+			git.add().addFilepattern("nested-repo").call();
+
+			assertEquals(
+					"[nested-repo, mode:160000]",
+					indexState(0));
+		}
+	}
+
 	private static DirCacheEntry addEntryToBuilder(String path, File file,
 			ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage)
 			throws IOException {
@@ -1029,4 +1112,25 @@
 			throw new IOException("could not commit");
 	}
 
+	private void createNestedRepo(String path) throws IOException {
+		File gitLinkDir = new File(db.getWorkTree(), path);
+		FileUtils.mkdir(gitLinkDir);
+
+		FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
+		nestedBuilder.setWorkTree(gitLinkDir);
+
+		Repository nestedRepo = nestedBuilder.build();
+		nestedRepo.create();
+
+		writeTrashFile(path, "README1.md", "content");
+		writeTrashFile(path, "README2.md", "content");
+
+		// Commit these changes in the subrepo
+		try (Git git = new Git(nestedRepo)) {
+			git.add().addFilepattern(".").call();
+			git.commit().setMessage("subrepo commit").call();
+		} catch (GitAPIException e) {
+			throw new RuntimeException(e);
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
index d842046..f2b5b3b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
@@ -147,6 +147,16 @@
 	}
 
 	@Test
+	public void testModifyW() throws Exception {
+		ApplyResult result = init("W");
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "W"),
+				result.getUpdatedFiles().get(0));
+		checkFile(new File(db.getWorkTree(), "W"),
+				b.getString(0, b.size(), false));
+	}
+
+	@Test
 	public void testModifyX() throws Exception {
 		ApplyResult result = init("X");
 		assertEquals(1, result.getUpdatedFiles().size());
@@ -186,6 +196,55 @@
 				b.getString(0, b.size(), false));
 	}
 
+	@Test
+	public void testNonASCII() throws Exception {
+		ApplyResult result = init("NonASCII");
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "NonASCII"),
+				result.getUpdatedFiles().get(0));
+		checkFile(new File(db.getWorkTree(), "NonASCII"),
+				b.getString(0, b.size(), false));
+	}
+
+	@Test
+	public void testNonASCII2() throws Exception {
+		ApplyResult result = init("NonASCII2");
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "NonASCII2"),
+				result.getUpdatedFiles().get(0));
+		checkFile(new File(db.getWorkTree(), "NonASCII2"),
+				b.getString(0, b.size(), false));
+	}
+
+	@Test
+	public void testNonASCIIAdd() throws Exception {
+		ApplyResult result = init("NonASCIIAdd");
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "NonASCIIAdd"),
+				result.getUpdatedFiles().get(0));
+		checkFile(new File(db.getWorkTree(), "NonASCIIAdd"),
+				b.getString(0, b.size(), false));
+	}
+
+	@Test
+	public void testNonASCIIAdd2() throws Exception {
+		ApplyResult result = init("NonASCIIAdd2", false, true);
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "NonASCIIAdd2"),
+				result.getUpdatedFiles().get(0));
+		checkFile(new File(db.getWorkTree(), "NonASCIIAdd2"),
+				b.getString(0, b.size(), false));
+	}
+
+	@Test
+	public void testNonASCIIDel() throws Exception {
+		ApplyResult result = init("NonASCIIDel", true, false);
+		assertEquals(1, result.getUpdatedFiles().size());
+		assertEquals(new File(db.getWorkTree(), "NonASCIIDel"),
+				result.getUpdatedFiles().get(0));
+		assertFalse(new File(db.getWorkTree(), "NonASCIIDel").exists());
+	}
+
 	private static byte[] readFile(final String patchFile) throws IOException {
 		final InputStream in = getTestResource(patchFile);
 		if (in == null) {
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 362d7ac..0bb6610 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
@@ -88,7 +88,6 @@
 import org.eclipse.jgit.transport.URIish;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class CheckoutCommandTest extends RepositoryTestCase {
@@ -633,11 +632,116 @@
 	}
 
 	@Test
-	@Ignore
-	public void testSmudgeAndClean() throws IOException, GitAPIException {
-		// @TODO: fix this test
-		File clean_filter = writeTempFile("sed s/V1/@version/g -");
-		File smudge_filter = writeTempFile("sed s/@version/V1/g -");
+	public void testSmudgeFilter_deleteFileAndRestoreFromCommit()
+			throws IOException, GitAPIException {
+		File script = writeTempFile("sed s/o/e/g");
+		StoredConfig config = git.getRepository().getConfig();
+		config.setString("filter", "tstFilter", "smudge",
+				"sh " + slashify(script.getPath()));
+		config.save();
+
+		writeTrashFile("foo", "foo");
+		git.add().addFilepattern("foo").call();
+		git.commit().setMessage("initial").call();
+
+		writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+		git.add().addFilepattern(".gitattributes").call();
+		git.commit().setMessage("add filter").call();
+
+		writeTrashFile("src/a.tmp", "foo");
+		// Caution: we need a trailing '\n' since sed on mac always appends
+		// linefeeds if missing
+		writeTrashFile("src/a.txt", "foo\n");
+		git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+				.call();
+		RevCommit content = git.commit().setMessage("added content").call();
+
+		deleteTrashFile("src/a.txt");
+		git.checkout().setStartPoint(content.getName()).addPath("src/a.txt")
+				.call();
+
+		assertEquals(
+				"[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+				indexState(CONTENT));
+		assertEquals("foo", read("src/a.tmp"));
+		assertEquals("fee\n", read("src/a.txt"));
+	}
+
+	@Test
+	public void testSmudgeFilter_deleteFileAndRestoreFromIndex()
+			throws IOException, GitAPIException {
+		File script = writeTempFile("sed s/o/e/g");
+		StoredConfig config = git.getRepository().getConfig();
+		config.setString("filter", "tstFilter", "smudge",
+				"sh " + slashify(script.getPath()));
+		config.save();
+
+		writeTrashFile("foo", "foo");
+		git.add().addFilepattern("foo").call();
+		git.commit().setMessage("initial").call();
+
+		writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+		git.add().addFilepattern(".gitattributes").call();
+		git.commit().setMessage("add filter").call();
+
+		writeTrashFile("src/a.tmp", "foo");
+		// Caution: we need a trailing '\n' since sed on mac always appends
+		// linefeeds if missing
+		writeTrashFile("src/a.txt", "foo\n");
+		git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+				.call();
+		git.commit().setMessage("added content").call();
+
+		deleteTrashFile("src/a.txt");
+		git.checkout().addPath("src/a.txt").call();
+
+		assertEquals(
+				"[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+				indexState(CONTENT));
+		assertEquals("foo", read("src/a.tmp"));
+		assertEquals("fee\n", read("src/a.txt"));
+	}
+
+	@Test
+	public void testSmudgeFilter_deleteFileAndCreateBranchAndRestoreFromCommit()
+			throws IOException, GitAPIException {
+		File script = writeTempFile("sed s/o/e/g");
+		StoredConfig config = git.getRepository().getConfig();
+		config.setString("filter", "tstFilter", "smudge",
+				"sh " + slashify(script.getPath()));
+		config.save();
+
+		writeTrashFile("foo", "foo");
+		git.add().addFilepattern("foo").call();
+		git.commit().setMessage("initial").call();
+
+		writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+		git.add().addFilepattern(".gitattributes").call();
+		git.commit().setMessage("add filter").call();
+
+		writeTrashFile("src/a.tmp", "foo");
+		// Caution: we need a trailing '\n' since sed on mac always appends
+		// linefeeds if missing
+		writeTrashFile("src/a.txt", "foo\n");
+		git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+				.call();
+		RevCommit content = git.commit().setMessage("added content").call();
+
+		deleteTrashFile("src/a.txt");
+		git.checkout().setName("newBranch").setCreateBranch(true)
+				.setStartPoint(content).addPath("src/a.txt").call();
+
+		assertEquals(
+				"[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+				indexState(CONTENT));
+		assertEquals("foo", read("src/a.tmp"));
+		assertEquals("fee\n", read("src/a.txt"));
+	}
+
+	@Test
+	public void testSmudgeAndClean() throws Exception {
+		File clean_filter = writeTempFile("sed s/V1/@version/g");
+		File smudge_filter = writeTempFile("sed s/@version/V1/g");
 
 		try (Git git2 = new Git(db)) {
 			StoredConfig config = git.getRepository().getConfig();
@@ -646,33 +750,39 @@
 			config.setString("filter", "tstFilter", "clean",
 					"sh " + slashify(clean_filter.getPath()));
 			config.save();
-			writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+			writeTrashFile(".gitattributes", "filterTest.txt filter=tstFilter");
 			git2.add().addFilepattern(".gitattributes").call();
 			git2.commit().setMessage("add attributes").call();
 
-			writeTrashFile("filterTest.txt", "hello world, V1");
+			fsTick(writeTrashFile("filterTest.txt", "hello world, V1\n"));
 			git2.add().addFilepattern("filterTest.txt").call();
-			git2.commit().setMessage("add filterText.txt").call();
+			RevCommit one = git2.commit().setMessage("add filterText.txt").call();
 			assertEquals(
-					"[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]",
+					"[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:hello world, @version\n]",
 					indexState(CONTENT));
 
-			git2.checkout().setCreateBranch(true).setName("test2").call();
-			writeTrashFile("filterTest.txt", "bon giorno world, V1");
+			fsTick(writeTrashFile("filterTest.txt", "bon giorno world, V1\n"));
 			git2.add().addFilepattern("filterTest.txt").call();
-			git2.commit().setMessage("modified filterText.txt").call();
+			RevCommit two = git2.commit().setMessage("modified filterTest.txt").call();
 
 			assertTrue(git2.status().call().isClean());
 			assertEquals(
-					"[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:bon giorno world, @version]",
+					"[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:bon giorno world, @version\n]",
 					indexState(CONTENT));
 
-			git2.checkout().setName("refs/heads/test").call();
+			git2.checkout().setName(one.getName()).call();
 			assertTrue(git2.status().call().isClean());
 			assertEquals(
-					"[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]",
+					"[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:hello world, @version\n]",
 					indexState(CONTENT));
-			assertEquals("hello world, V1", read("filterTest.txt"));
+			assertEquals("hello world, V1\n", read("filterTest.txt"));
+
+			git2.checkout().setName(two.getName()).call();
+			assertTrue(git2.status().call().isClean());
+			assertEquals(
+					"[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:bon giorno world, @version\n]",
+					indexState(CONTENT));
+			assertEquals("bon giorno world, V1\n", read("filterTest.txt"));
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
new file mode 100644
index 0000000..5f10131
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.api.errors.CheckoutConflictException;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
+import org.eclipse.jgit.attributes.Attribute;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.errors.RevisionSyntaxException;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+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.EOL;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.IO;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoint;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for end-of-line conversion and settings using core.autocrlf, *
+ * core.eol and the .gitattributes eol, text, binary (macro for -diff -merge
+ * -text)
+ */
+@RunWith(Theories.class)
+public class EolRepositoryTest extends RepositoryTestCase {
+	private static final FileMode D = FileMode.TREE;
+
+	private static final FileMode F = FileMode.REGULAR_FILE;
+
+	@DataPoint
+	public static boolean doSmudgeEntries = true;
+
+	@DataPoint
+	public static boolean dontSmudgeEntries = false;
+
+	private boolean smudge;
+
+	@DataPoint
+	public static String smallContents[] = {
+			generateTestData(3, 1, true, false),
+			generateTestData(3, 1, false, true),
+			generateTestData(3, 1, true, true) };
+
+	@DataPoint
+	public static String hugeContents[] = {
+			generateTestData(1000000, 17, true, false),
+			generateTestData(1000000, 17, false, true),
+			generateTestData(1000000, 17, true, true) };
+
+	static String generateTestData(int size, int lineSize, boolean withCRLF,
+			boolean withLF) {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < size; i++) {
+			if (i > 0 && i % lineSize == 0) {
+				// newline
+				if (withCRLF && withLF) {
+					// mixed
+					if (i % 2 == 0)
+						sb.append("\r\n");
+					else
+						sb.append("\n");
+				} else if (withCRLF) {
+					sb.append("\r\n");
+				} else if (withLF) {
+					sb.append("\n");
+				}
+			}
+			sb.append("A");
+		}
+		return sb.toString();
+	}
+
+	public EolRepositoryTest(String[] testContent, boolean smudgeEntries) {
+		CONTENT_CRLF = testContent[0];
+		CONTENT_LF = testContent[1];
+		CONTENT_MIXED = testContent[2];
+		this.smudge = smudgeEntries;
+	}
+
+	protected String CONTENT_CRLF;
+
+	protected String CONTENT_LF;
+
+	protected String CONTENT_MIXED;
+
+	private TreeWalk walk;
+
+	/** work tree root .gitattributes */
+	private File dotGitattributes;
+
+	/** file containing CRLF */
+	private File fileCRLF;
+
+	/** file containing LF */
+	private File fileLF;
+
+	/** file containing mixed CRLF and LF */
+	private File fileMixed;
+
+	/** this values are set in {@link #collectRepositoryState()} */
+	private static class ActualEntry {
+		private String attrs;
+
+		private String file;
+
+		private String index;
+
+		private int indexContentLength;
+	}
+
+	private ActualEntry entryCRLF = new ActualEntry();
+
+	private ActualEntry entryLF = new ActualEntry();
+
+	private ActualEntry entryMixed = new ActualEntry();
+
+	private DirCache dirCache;
+
+	@Test
+	public void testDefaultSetup() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(null, null, null, null, "* text=auto");
+		collectRepositoryState();
+		assertEquals("text=auto", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	public void checkEntryContent(ActualEntry entry, String fileContent,
+			String indexContent) {
+		assertEquals(fileContent, entry.file);
+		assertEquals(indexContent, entry.index);
+		if (entry.indexContentLength != 0) {
+			assertEquals(fileContent.length(), entry.indexContentLength);
+		}
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_false() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.FALSE, null, null, null, "* text=auto");
+		collectRepositoryState();
+		assertEquals("text=auto", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_true() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.TRUE, null, null, null, "* text=auto");
+		collectRepositoryState();
+		assertEquals("text=auto", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_input() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.INPUT, null, null, null, "* text=auto");
+		collectRepositoryState();
+		assertEquals("text=auto", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigEOL_lf() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(null, EOL.LF, "*.txt text", null, null);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigEOL_crlf() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(null, EOL.CRLF, "*.txt text", null, null);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigEOL_native_windows() throws Exception {
+		String origLineSeparator = System.getProperty("line.separator", "\n");
+		System.setProperty("line.separator", "\r\n");
+		try {
+			// for EOL to work, the text attribute must be set
+			setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null);
+			collectRepositoryState();
+			assertEquals("text", entryCRLF.attrs);
+			checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+			checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		} finally {
+			System.setProperty("line.separator", origLineSeparator);
+		}
+	}
+
+	@Test
+	public void test_ConfigEOL_native_xnix() throws Exception {
+		String origLineSeparator = System.getProperty("line.separator", "\n");
+		System.setProperty("line.separator", "\n");
+		try {
+			// for EOL to work, the text attribute must be set
+			setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null);
+			collectRepositoryState();
+			assertEquals("text", entryCRLF.attrs);
+			checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+			checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		} finally {
+			System.setProperty("line.separator", origLineSeparator);
+		}
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_false_ConfigEOL_lf() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt text", null, null);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_false_ConfigEOL_native() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.NATIVE, "*.txt text", null, null);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_true_ConfigEOL_lf() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt text", null, null);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_switchToBranchWithTextAttributes()
+			throws Exception {
+		Git git = Git.wrap(db);
+
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.CRLF, null, null,
+				"file1.txt text\nfile2.txt text\nfile3.txt text");
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+
+		// switch to binary for file1
+		dotGitattributes = createAndAddFile(git, Constants.DOT_GIT_ATTRIBUTES,
+				"file1.txt binary\nfile2.txt text\nfile3.txt text");
+		gitCommit(git, "switchedToBinaryFor1");
+		recreateWorktree(git);
+		collectRepositoryState();
+		assertEquals("binary -diff -merge -text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		assertEquals("text", entryLF.attrs);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		assertEquals("text", entryMixed.attrs);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+
+		// checkout the commit which has text for file1
+		gitCheckout(git, "HEAD^");
+		recreateWorktree(git);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_switchToBranchWithBinaryAttributes() throws Exception {
+		Git git = Git.wrap(db);
+
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, null, null,
+				"file1.txt binary\nfile2.txt binary\nfile3.txt binary");
+		collectRepositoryState();
+		assertEquals("binary -diff -merge -text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
+
+		// switch to text for file1
+		dotGitattributes = createAndAddFile(git, Constants.DOT_GIT_ATTRIBUTES,
+				"file1.txt text\nfile2.txt binary\nfile3.txt binary");
+		gitCommit(git, "switchedToTextFor1");
+		recreateWorktree(git);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		assertEquals("binary -diff -merge -text", entryLF.attrs);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		assertEquals("binary -diff -merge -text", entryMixed.attrs);
+		checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
+
+		// checkout the commit which has text for file1
+		gitCheckout(git, "HEAD^");
+		recreateWorktree(git);
+		collectRepositoryState();
+		assertEquals("binary -diff -merge -text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_input_ConfigEOL_lf() throws Exception {
+		// for EOL to work, the text attribute must be set
+		setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt text", null, null);
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_true_GlobalEOL_lf() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt eol=lf", null, null);
+		collectRepositoryState();
+		assertEquals("eol=lf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_false_GlobalEOL_lf() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt eol=lf", null, null);
+		collectRepositoryState();
+		assertEquals("eol=lf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_input_GlobalEOL_lf() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt eol=lf", null, null);
+		collectRepositoryState();
+		assertEquals("eol=lf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_true_GlobalEOL_crlf() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt eol=crlf", null, null);
+		collectRepositoryState();
+		assertEquals("eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_false_GlobalEOL_crlf() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt eol=crlf", null, null);
+		collectRepositoryState();
+		assertEquals("eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_input_GlobalEOL_crlf() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt eol=crlf", null, null);
+		collectRepositoryState();
+		assertEquals("eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_true_GlobalEOL_lf_InfoEOL_crlf()
+			throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.TRUE, null, "*.txt eol=lf", "*.txt eol=crlf", null);
+		// info decides
+		collectRepositoryState();
+		assertEquals("eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_ConfigAutoCRLF_false_GlobalEOL_crlf_InfoEOL_lf()
+			throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.FALSE, null, "*.txt eol=crlf", "*.txt eol=lf", null);
+		// info decides
+		collectRepositoryState();
+		assertEquals("eol=lf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_GlobalEOL_lf_RootEOL_crlf() throws Exception {
+		setupGitAndDoHardReset(null, null, "*.txt eol=lf", null, "*.txt eol=crlf");
+		// root over global
+		collectRepositoryState();
+		assertEquals("eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_GlobalEOL_lf_InfoEOL_crlf_RootEOL_lf() throws Exception {
+		setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt eol=crlf", "*.txt eol=lf");
+		// info overrides all
+		collectRepositoryState();
+		assertEquals("eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_GlobalEOL_lf_InfoEOL_crlf_RootEOL_unspec()
+			throws Exception {
+		setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt eol=crlf",
+				"*.txt text !eol");
+		// info overrides all
+		collectRepositoryState();
+		assertEquals("eol=crlf text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
+	}
+
+	@Test
+	public void test_GlobalEOL_lf_InfoEOL_unspec_RootEOL_crlf()
+			throws Exception {
+		setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt !eol",
+				"*.txt text eol=crlf");
+		// info overrides all
+		collectRepositoryState();
+		assertEquals("text", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
+	}
+
+	@Test
+	public void testBinary1() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.CRLF, "*.txt text", "*.txt binary",
+				"*.txt eol=crlf");
+		// info overrides all
+		collectRepositoryState();
+		assertEquals("binary -diff -merge -text eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
+	}
+
+	@Test
+	public void testBinary2() throws Exception {
+		setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.CRLF, "*.txt text eol=crlf", null,
+				"*.txt binary");
+		// root over global
+		collectRepositoryState();
+		assertEquals("binary -diff -merge -text eol=crlf", entryCRLF.attrs);
+		checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
+		checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
+		checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
+	}
+
+	// create new repo with
+	// global .gitattributes
+	// info .git/config/info/.gitattributes
+	// workdir root .gitattributes
+	// text file lf.txt CONTENT_LF
+	// text file crlf.txt CONTENT_CRLF
+	//
+	// commit files (checkin)
+	// delete working dir files
+	// reset hard (checkout)
+	private void setupGitAndDoHardReset(AutoCRLF autoCRLF, EOL eol,
+			String globalAttributesContent, String infoAttributesContent,
+			String workDirRootAttributesContent) throws Exception {
+		Git git = new Git(db);
+		FileBasedConfig config = db.getConfig();
+		if (autoCRLF != null) {
+			config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_AUTOCRLF, autoCRLF);
+		}
+		if (eol != null) {
+			config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_EOL, eol);
+		}
+		if (globalAttributesContent != null) {
+			File f = new File(db.getDirectory(), "global/attrs");
+			write(f, globalAttributesContent);
+			config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE,
+					f.getAbsolutePath());
+
+		}
+		if (infoAttributesContent != null) {
+			File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
+			write(f, infoAttributesContent);
+		}
+		config.save();
+
+		if (workDirRootAttributesContent != null) {
+			dotGitattributes = createAndAddFile(git,
+					Constants.DOT_GIT_ATTRIBUTES, workDirRootAttributesContent);
+		} else {
+			dotGitattributes = null;
+		}
+
+		fileCRLF = createAndAddFile(git, "file1.txt", "a");
+
+		fileLF = createAndAddFile(git, "file2.txt", "a");
+
+		fileMixed = createAndAddFile(git, "file3.txt", "a");
+
+		RevCommit c = gitCommit(git, "create files");
+
+		fileCRLF = createAndAddFile(git, "file1.txt", CONTENT_CRLF);
+
+		fileLF = createAndAddFile(git, "file2.txt", CONTENT_LF);
+
+		fileMixed = createAndAddFile(git, "file3.txt", CONTENT_MIXED);
+
+		gitCommit(git, "addFiles");
+
+		recreateWorktree(git);
+
+		if (smudge) {
+			DirCache dc = DirCache.lock(git.getRepository().getIndexFile(),
+					FS.detect());
+			DirCacheEditor editor = dc.editor();
+			for (int i = 0; i < dc.getEntryCount(); i++) {
+				editor.add(new DirCacheEditor.PathEdit(
+						dc.getEntry(i).getPathString()) {
+					public void apply(DirCacheEntry ent) {
+						ent.smudgeRacilyClean();
+					}
+				});
+			}
+			editor.commit();
+		}
+
+		// @TODO: find out why the following assertion would break the tests
+		// assertTrue(git.status().call().isClean());
+		git.checkout().setName(c.getName()).call();
+		git.checkout().setName("master").call();
+	}
+
+	private void recreateWorktree(Git git)
+			throws GitAPIException, CheckoutConflictException,
+			InterruptedException, IOException, NoFilepatternException {
+		// re-create file from the repo
+		for (File f : new File[] { dotGitattributes, fileCRLF, fileLF, fileMixed }) {
+			if (f == null)
+				continue;
+			f.delete();
+			Assert.assertFalse(f.exists());
+		}
+		gitResetHard(git);
+		fsTick(db.getIndexFile());
+		gitAdd(git, ".");
+	}
+
+	protected RevCommit gitCommit(Git git, String msg) throws GitAPIException {
+		return git.commit().setMessage(msg).call();
+	}
+
+	protected void gitAdd(Git git, String path) throws GitAPIException {
+		git.add().addFilepattern(path).call();
+	}
+
+	protected void gitResetHard(Git git) throws GitAPIException {
+		git.reset().setMode(ResetType.HARD).call();
+	}
+
+	protected void gitCheckout(Git git, String revstr)
+			throws GitAPIException, RevisionSyntaxException, IOException {
+		git.checkout().setName(db.resolve(revstr).getName()).call();
+	}
+
+	// create a file and add it to the repo
+	private File createAndAddFile(Git git, String path, String content)
+			throws Exception {
+		File f;
+		int pos = path.lastIndexOf('/');
+		if (pos < 0) {
+			f = writeTrashFile(path, content);
+		} else {
+			f = writeTrashFile(path.substring(0, pos), path.substring(pos + 1),
+					content);
+		}
+		gitAdd(git, path);
+		Assert.assertTrue(f.exists());
+		return f;
+	}
+
+	private void collectRepositoryState() throws Exception {
+		dirCache = db.readDirCache();
+		walk = beginWalk();
+		if (dotGitattributes != null)
+			collectEntryContentAndAttributes(F, ".gitattributes", null);
+		collectEntryContentAndAttributes(F, fileCRLF.getName(), entryCRLF);
+		collectEntryContentAndAttributes(F, fileLF.getName(), entryLF);
+		collectEntryContentAndAttributes(F, fileMixed.getName(), entryMixed);
+		endWalk();
+	}
+
+	private TreeWalk beginWalk() throws Exception {
+		TreeWalk newWalk = new TreeWalk(db);
+		newWalk.addTree(new FileTreeIterator(db));
+		newWalk.addTree(new DirCacheIterator(db.readDirCache()));
+		return newWalk;
+	}
+
+	private void endWalk() throws IOException {
+		assertFalse("Not all files tested", walk.next());
+	}
+
+	private void collectEntryContentAndAttributes(FileMode type, String pathName,
+			ActualEntry e) throws IOException {
+		assertTrue("walk has entry", walk.next());
+
+		assertEquals(pathName, walk.getPathString());
+		assertEquals(type, walk.getFileMode(0));
+
+		if (e != null) {
+			e.attrs = "";
+			for (Attribute a : walk.getAttributes().getAll()) {
+				e.attrs += " " + a.toString();
+			}
+			e.attrs = e.attrs.trim();
+			e.file = new String(
+					IO.readFully(new File(db.getWorkTree(), pathName)));
+			DirCacheEntry dce = dirCache.getEntry(pathName);
+			ObjectLoader open = walk.getObjectReader().open(dce.getObjectId());
+			e.index = new String(open.getBytes());
+			e.indexContentLength = dce.getLength();
+		}
+
+		if (D.equals(type))
+			walk.enterSubtree();
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java
new file mode 100644
index 0000000..8ca1b31
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.eclipse.jgit.lib.CoreConfig.EolStreamType.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.io.EolStreamTypeUtil;
+import org.junit.Test;
+
+/**
+ * Unit tests for end-of-line conversion streams
+ */
+public class EolStreamTypeUtilTest {
+
+	@Test
+	public void testCheckoutDirect() throws Exception {
+		testCheckout(DIRECT, DIRECT, "", "");
+		testCheckout(DIRECT, DIRECT, "\r", "\r");
+		testCheckout(DIRECT, DIRECT, "\n", "\n");
+
+		testCheckout(DIRECT, DIRECT, "\r\n", "\r\n");
+		testCheckout(DIRECT, DIRECT, "\n\r", "\n\r");
+
+		testCheckout(DIRECT, DIRECT, "\n\r\n", "\n\r\n");
+		testCheckout(DIRECT, DIRECT, "\r\n\r", "\r\n\r");
+
+		testCheckout(DIRECT, DIRECT, "a\nb\n", "a\nb\n");
+		testCheckout(DIRECT, DIRECT, "a\rb\r", "a\rb\r");
+		testCheckout(DIRECT, DIRECT, "a\n\rb\n\r", "a\n\rb\n\r");
+		testCheckout(DIRECT, DIRECT, "a\r\nb\r\n", "a\r\nb\r\n");
+	}
+
+	@Test
+	public void testCheckoutLF() throws Exception {
+		testCheckout(TEXT_LF, AUTO_LF, "", "");
+		testCheckout(TEXT_LF, AUTO_LF, "\r", "\r");
+		testCheckout(TEXT_LF, AUTO_LF, "\n", "\n");
+
+		testCheckout(TEXT_LF, AUTO_LF, "\r\n", "\n");
+		testCheckout(TEXT_LF, AUTO_LF, "\n\r", "\n\r");
+
+		testCheckout(TEXT_LF, AUTO_LF, "\n\r\n", "\n\n");
+		testCheckout(TEXT_LF, AUTO_LF, "\r\n\r", "\n\r");
+
+		testCheckout(TEXT_LF, AUTO_LF, "a\nb\n", "a\nb\n");
+		testCheckout(TEXT_LF, AUTO_LF, "a\rb\r", "a\rb\r");
+		testCheckout(TEXT_LF, AUTO_LF, "a\n\rb\n\r", "a\n\rb\n\r");
+		testCheckout(TEXT_LF, AUTO_LF, "a\r\nb\r\n", "a\nb\n");
+	}
+
+	@Test
+	public void testCheckoutCRLF() throws Exception {
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "", "");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "\r", "\r");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "\n", "\r\n");
+
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "\r\n", "\r\n");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "\n\r", "\r\n\r");
+
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "\n\r\n", "\r\n\r\n");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "\r\n\r", "\r\n\r");
+
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "a\nb\n", "a\r\nb\r\n");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "a\rb\r", "a\rb\r");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "a\n\rb\n\r", "a\r\n\rb\r\n\r");
+		testCheckout(TEXT_CRLF, AUTO_CRLF, "a\r\nb\r\n", "a\r\nb\r\n");
+	}
+
+	/**
+	 * Test stream type detection based on stream content.
+	 * <p>
+	 * Tests three things with the output text:
+	 * <p>
+	 * 1) conversion if output was declared as text
+	 * <p>
+	 * 2) conversion if output was declared as potentially text (AUTO_...) and
+	 * is in fact text
+	 * <p>
+	 * 3) conversion if modified output (now with binary characters) was
+	 * declared as potentially text but now contains binary characters
+	 * <p>
+	 *
+	 * @param streamTypeText
+	 *            is the enum meaning that the output is definitely text (no
+	 *            binary check at all)
+	 * @param streamTypeWithBinaryCheck
+	 *            is the enum meaning that the output may be text (binary check
+	 *            is done)
+	 * @param output
+	 *            is a text output without binary characters
+	 * @param expectedConversion
+	 *            is the expected converted output without binary characters
+	 * @throws Exception
+	 */
+	private void testCheckout(EolStreamType streamTypeText,
+			EolStreamType streamTypeWithBinaryCheck, String output,
+			String expectedConversion) throws Exception {
+		ByteArrayOutputStream b;
+		byte[] outputBytes = output.getBytes(StandardCharsets.UTF_8);
+		byte[] expectedConversionBytes = expectedConversion
+				.getBytes(StandardCharsets.UTF_8);
+
+		// test using output text and assuming it was declared TEXT
+		b = new ByteArrayOutputStream();
+		try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
+				streamTypeText)) {
+			out.write(outputBytes);
+		}
+		assertArrayEquals(expectedConversionBytes, b.toByteArray());
+
+		// test using ouput text and assuming it was declared AUTO, using binary
+		// detection
+		b = new ByteArrayOutputStream();
+		try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
+				streamTypeWithBinaryCheck)) {
+			out.write(outputBytes);
+		}
+		assertArrayEquals(expectedConversionBytes, b.toByteArray());
+
+		// now pollute output text with some binary bytes
+		outputBytes = extendWithBinaryData(outputBytes);
+		expectedConversionBytes = extendWithBinaryData(expectedConversionBytes);
+
+		// again, test using output text and assuming it was declared TEXT
+		b = new ByteArrayOutputStream();
+		try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
+				streamTypeText)) {
+			out.write(outputBytes);
+		}
+		assertArrayEquals(expectedConversionBytes, b.toByteArray());
+
+		// again, test using ouput text and assuming it was declared AUTO, using
+		// binary
+		// detection
+		b = new ByteArrayOutputStream();
+		try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
+				streamTypeWithBinaryCheck)) {
+			out.write(outputBytes);
+		}
+		// expect no conversion
+		assertArrayEquals(outputBytes, b.toByteArray());
+	}
+
+	@Test
+	public void testCheckinDirect() throws Exception {
+		testCheckin(DIRECT, DIRECT, "", "");
+		testCheckin(DIRECT, DIRECT, "\r", "\r");
+		testCheckin(DIRECT, DIRECT, "\n", "\n");
+
+		testCheckin(DIRECT, DIRECT, "\r\n", "\r\n");
+		testCheckin(DIRECT, DIRECT, "\n\r", "\n\r");
+
+		testCheckin(DIRECT, DIRECT, "\n\r\n", "\n\r\n");
+		testCheckin(DIRECT, DIRECT, "\r\n\r", "\r\n\r");
+
+		testCheckin(DIRECT, DIRECT, "a\nb\n", "a\nb\n");
+		testCheckin(DIRECT, DIRECT, "a\rb\r", "a\rb\r");
+		testCheckin(DIRECT, DIRECT, "a\n\rb\n\r", "a\n\rb\n\r");
+		testCheckin(DIRECT, DIRECT, "a\r\nb\r\n", "a\r\nb\r\n");
+	}
+
+	@Test
+	public void testCheckinLF() throws Exception {
+		testCheckin(TEXT_LF, AUTO_LF, "", "");
+		testCheckin(TEXT_LF, AUTO_LF, "\r", "\r");
+		testCheckin(TEXT_LF, AUTO_LF, "\n", "\n");
+
+		testCheckin(TEXT_LF, AUTO_LF, "\r\n", "\n");
+		testCheckin(TEXT_LF, AUTO_LF, "\n\r", "\n\r");
+
+		testCheckin(TEXT_LF, AUTO_LF, "\n\r\n", "\n\n");
+		testCheckin(TEXT_LF, AUTO_LF, "\r\n\r", "\n\r");
+
+		testCheckin(TEXT_LF, AUTO_LF, "a\nb\n", "a\nb\n");
+		testCheckin(TEXT_LF, AUTO_LF, "a\rb\r", "a\rb\r");
+		testCheckin(TEXT_LF, AUTO_LF, "a\n\rb\n\r", "a\n\rb\n\r");
+		testCheckin(TEXT_LF, AUTO_LF, "a\r\nb\r\n", "a\nb\n");
+	}
+
+	@Test
+	public void testCheckinCRLF() throws Exception {
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "", "");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "\r", "\r");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "\n", "\r\n");
+
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "\r\n", "\r\n");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "\n\r", "\r\n\r");
+
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "\n\r\n", "\r\n\r\n");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "\r\n\r", "\r\n\r");
+
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "a\nb\n", "a\r\nb\r\n");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "a\rb\r", "a\rb\r");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "a\n\rb\n\r", "a\r\n\rb\r\n\r");
+		testCheckin(TEXT_CRLF, AUTO_CRLF, "a\r\nb\r\n", "a\r\nb\r\n");
+	}
+
+	/**
+	 * Test stream type detection based on stream content.
+	 * <p>
+	 * Tests three things with the input text:
+	 * <p>
+	 * 1) conversion if input was declared as text
+	 * <p>
+	 * 2) conversion if input was declared as potentially text (AUTO_...) and is
+	 * in fact text
+	 * <p>
+	 * 3) conversion if modified input (now with binary characters) was declared
+	 * as potentially text but now contains binary characters
+	 * <p>
+	 *
+	 * @param streamTypeText
+	 *            is the enum meaning that the input is definitely text (no
+	 *            binary check at all)
+	 * @param streamTypeWithBinaryCheck
+	 *            is the enum meaning that the input may be text (binary check
+	 *            is done)
+	 * @param input
+	 *            is a text input without binary characters
+	 * @param expectedConversion
+	 *            is the expected converted input without binary characters
+	 * @throws Exception
+	 */
+	private void testCheckin(EolStreamType streamTypeText,
+			EolStreamType streamTypeWithBinaryCheck, String input,
+			String expectedConversion) throws Exception {
+		byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);
+		byte[] expectedConversionBytes = expectedConversion
+				.getBytes(StandardCharsets.UTF_8);
+
+		// test using input text and assuming it was declared TEXT
+		try (InputStream in = EolStreamTypeUtil.wrapInputStream(
+				new ByteArrayInputStream(inputBytes),
+				streamTypeText)) {
+			byte[] b = new byte[1024];
+			int len = IO.readFully(in, b, 0);
+			assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len));
+		}
+
+		// test using input text and assuming it was declared AUTO, using binary
+		// detection
+		try (InputStream in = EolStreamTypeUtil.wrapInputStream(
+				new ByteArrayInputStream(inputBytes),
+				streamTypeWithBinaryCheck)) {
+			byte[] b = new byte[1024];
+			int len = IO.readFully(in, b, 0);
+			assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len));
+		}
+
+		// now pollute input text with some binary bytes
+		inputBytes = extendWithBinaryData(inputBytes);
+		expectedConversionBytes = extendWithBinaryData(expectedConversionBytes);
+
+		// again, test using input text and assuming it was declared TEXT
+		try (InputStream in = EolStreamTypeUtil.wrapInputStream(
+				new ByteArrayInputStream(inputBytes), streamTypeText)) {
+			byte[] b = new byte[1024];
+			int len = IO.readFully(in, b, 0);
+			assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len));
+		}
+
+		// again, test using input text and assuming it was declared AUTO, using
+		// binary
+		// detection
+		try (InputStream in = EolStreamTypeUtil.wrapInputStream(
+				new ByteArrayInputStream(inputBytes),
+				streamTypeWithBinaryCheck)) {
+			byte[] b = new byte[1024];
+			int len = IO.readFully(in, b, 0);
+			// expect no conversion
+			assertArrayEquals(inputBytes, Arrays.copyOf(b, len));
+		}
+	}
+
+	private byte[] extendWithBinaryData(byte[] data) throws Exception {
+		int n = 3;
+		byte[] dataEx = new byte[data.length + n];
+		System.arraycopy(data, 0, dataEx, 0, data.length);
+		for (int i = 0; i < n; i++) {
+			dataEx[data.length + i] = (byte) i;
+		}
+		return dataEx;
+	}
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
new file mode 100644
index 0000000..ca456b3
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
@@ -0,0 +1,339 @@
+/*
+ * 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.attributes;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Test;
+
+/**
+ * Tests {@link AttributesHandler}
+ */
+public class AttributesHandlerTest extends RepositoryTestCase {
+	private static final FileMode D = FileMode.TREE;
+
+	private static final FileMode F = FileMode.REGULAR_FILE;
+
+	private TreeWalk walk;
+
+	@Test
+	public void testExpandNonMacro1() throws Exception {
+		setupRepo(null, null, null, "*.txt text");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("text"));
+		endWalk();
+	}
+
+	@Test
+	public void testExpandNonMacro2() throws Exception {
+		setupRepo(null, null, null, "*.txt -text");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("-text"));
+		endWalk();
+	}
+
+	@Test
+	public void testExpandNonMacro3() throws Exception {
+		setupRepo(null, null, null, "*.txt !text");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs(""));
+		endWalk();
+	}
+
+	@Test
+	public void testExpandNonMacro4() throws Exception {
+		setupRepo(null, null, null, "*.txt text=auto");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("text=auto"));
+		endWalk();
+	}
+
+	@Test
+	public void testExpandBuiltInMacro1() throws Exception {
+		setupRepo(null, null, null, "*.txt binary");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("binary -diff -merge -text"));
+		endWalk();
+	}
+
+	@Test
+	public void testExpandBuiltInMacro2() throws Exception {
+		setupRepo(null, null, null, "*.txt -binary");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("-binary diff merge text"));
+		endWalk();
+	}
+
+	@Test
+	public void testExpandBuiltInMacro3() throws Exception {
+		setupRepo(null, null, null, "*.txt !binary");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs(""));
+		endWalk();
+	}
+
+	@Test
+	public void testCustomGlobalMacro1() throws Exception {
+		setupRepo(
+				"[attr]foo a -b !c d=e", null, null, "*.txt foo");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo a -b d=e"));
+		endWalk();
+	}
+
+	@Test
+	public void testCustomGlobalMacro2() throws Exception {
+		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt -foo");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("-foo -a b d=e"));
+		endWalk();
+	}
+
+	@Test
+	public void testCustomGlobalMacro3() throws Exception {
+		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt !foo");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs(""));
+		endWalk();
+	}
+
+	@Test
+	public void testCustomGlobalMacro4() throws Exception {
+		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt foo=bar");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo=bar a -b d=bar"));
+		endWalk();
+	}
+
+	@Test
+	public void testInfoOverridesGlobal() throws Exception {
+		setupRepo("[attr]foo bar1",
+				"[attr]foo bar2", null, "*.txt foo");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo bar2"));
+		endWalk();
+	}
+
+	@Test
+	public void testWorkDirRootOverridesGlobal() throws Exception {
+		setupRepo("[attr]foo bar1",
+				null,
+				"[attr]foo bar3", "*.txt foo");
+
+		walk = beginWalk();
+		assertIteration(F, ".gitattributes");
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo bar3"));
+		endWalk();
+	}
+
+	@Test
+	public void testInfoOverridesWorkDirRoot() throws Exception {
+		setupRepo("[attr]foo bar1",
+				"[attr]foo bar2", "[attr]foo bar3", "*.txt foo");
+
+		walk = beginWalk();
+		assertIteration(F, ".gitattributes");
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo bar2"));
+		endWalk();
+	}
+
+	@Test
+	public void testRecursiveMacro() throws Exception {
+		setupRepo(
+				"[attr]foo x bar -foo",
+				null, null, "*.txt foo");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo x bar"));
+		endWalk();
+	}
+
+	@Test
+	public void testCyclicMacros() throws Exception {
+		setupRepo(
+				"[attr]foo x -bar\n[attr]bar y -foo", null, null, "*.txt foo");
+
+		walk = beginWalk();
+		assertIteration(D, "sub");
+		assertIteration(F, "sub/.gitattributes");
+		assertIteration(F, "sub/a.txt", attrs("foo x -bar -y"));
+		endWalk();
+	}
+
+	private static Collection<Attribute> attrs(String s) {
+		return new AttributesRule("*", s).getAttributes();
+	}
+
+	private void assertIteration(FileMode type, String pathName)
+			throws IOException {
+		assertIteration(type, pathName, Collections.<Attribute> emptyList());
+	}
+
+	private void assertIteration(FileMode type, String pathName,
+			Collection<Attribute> expectedAttrs) throws IOException {
+		assertTrue("walk has entry", walk.next());
+		assertEquals(pathName, walk.getPathString());
+		assertEquals(type, walk.getFileMode(0));
+
+		if (expectedAttrs != null) {
+			assertEquals(new ArrayList<>(expectedAttrs),
+					new ArrayList<>(walk.getAttributes().getAll()));
+		}
+
+		if (D.equals(type))
+			walk.enterSubtree();
+	}
+
+	/**
+	 * @param globalAttributesContent
+	 * @param infoAttributesContent
+	 * @param rootAttributesContent
+	 * @param subDirAttributesContent
+	 * @throws Exception
+	 *             Setup a repo with .gitattributes files and a test file
+	 *             sub/a.txt
+	 */
+	private void setupRepo(
+			String globalAttributesContent,
+			String infoAttributesContent, String rootAttributesContent, String subDirAttributesContent)
+					throws Exception {
+		FileBasedConfig config = db.getConfig();
+		if (globalAttributesContent != null) {
+			File f = new File(db.getDirectory(), "global/attributes");
+			write(f, globalAttributesContent);
+			config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE,
+					f.getAbsolutePath());
+
+		}
+		if (infoAttributesContent != null) {
+			File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
+			write(f, infoAttributesContent);
+		}
+		config.save();
+
+		if (rootAttributesContent != null) {
+			writeAttributesFile(Constants.DOT_GIT_ATTRIBUTES,
+					rootAttributesContent);
+		}
+
+		if (subDirAttributesContent != null) {
+			writeAttributesFile("sub/" + Constants.DOT_GIT_ATTRIBUTES,
+					subDirAttributesContent);
+		}
+
+		writeTrashFile("sub/a.txt", "a");
+	}
+
+	private void writeAttributesFile(String name, String... rules)
+			throws IOException {
+		StringBuilder data = new StringBuilder();
+		for (String line : rules)
+			data.append(line + "\n");
+		writeTrashFile(name, data.toString());
+	}
+
+	private TreeWalk beginWalk() {
+		TreeWalk newWalk = new TreeWalk(db);
+		newWalk.addTree(new FileTreeIterator(db));
+		return newWalk;
+	}
+
+	private void endWalk() throws IOException {
+		assertFalse("Not all files tested", walk.next());
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
index 9f82b8a..e8dd952 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
@@ -293,28 +293,28 @@
 	public void testGetters() {
 		AttributesRule r = new AttributesRule("/pattern/", "");
 		assertFalse(r.isNameOnly());
-		assertTrue(r.dirOnly());
+		assertTrue(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertTrue(r.getAttributes().isEmpty());
 		assertEquals(r.getPattern(), "/pattern");
 
 		r = new AttributesRule("/patter?/", "");
 		assertFalse(r.isNameOnly());
-		assertTrue(r.dirOnly());
+		assertTrue(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertTrue(r.getAttributes().isEmpty());
 		assertEquals(r.getPattern(), "/patter?");
 
 		r = new AttributesRule("patt*", "");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertTrue(r.getAttributes().isEmpty());
 		assertEquals(r.getPattern(), "patt*");
 
 		r = new AttributesRule("pattern", "attribute1");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertFalse(r.getAttributes().isEmpty());
 		assertEquals(r.getAttributes().size(), 1);
@@ -322,28 +322,28 @@
 
 		r = new AttributesRule("pattern", "attribute1 -attribute2");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertEquals(r.getAttributes().size(), 2);
 		assertEquals(r.getPattern(), "pattern");
 
 		r = new AttributesRule("pattern", "attribute1 \t-attribute2 \t");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertEquals(r.getAttributes().size(), 2);
 		assertEquals(r.getPattern(), "pattern");
 
 		r = new AttributesRule("pattern", "attribute1\t-attribute2\t");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertEquals(r.getAttributes().size(), 2);
 		assertEquals(r.getPattern(), "pattern");
 
 		r = new AttributesRule("pattern", "attribute1\t -attribute2\t ");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertEquals(r.getAttributes().size(), 2);
 		assertEquals(r.getPattern(), "pattern");
@@ -351,7 +351,7 @@
 		r = new AttributesRule("pattern",
 				"attribute1 -attribute2  attribute3=value ");
 		assertTrue(r.isNameOnly());
-		assertFalse(r.dirOnly());
+		assertFalse(r.isDirOnly());
 		assertNotNull(r.getAttributes());
 		assertEquals(r.getAttributes().size(), 3);
 		assertEquals(r.getPattern(), "pattern");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
index 0e595e6..7421e90 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -251,14 +251,17 @@
 	}
 
 	private void assertAttributesNode(String pathName,
-			AttributesNode attributesNode, List<Attribute> nodeAttrs) {
+			AttributesNode attributesNode, List<Attribute> nodeAttrs)
+					throws IOException {
 		if (attributesNode == null)
 			assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
 		else {
 
 			Attributes entryAttributes = new Attributes();
-			attributesNode.getAttributes(pathName,
-					false, entryAttributes);
+			new AttributesHandler(walk).mergeAttributes(attributesNode,
+					pathName,
+					false,
+					entryAttributes);
 
 			if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
 				for (Attribute attribute : nodeAttrs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
index d478a7c..ec2370e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
@@ -50,6 +50,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.treewalk.TreeWalk;
 import org.junit.After;
 import org.junit.Test;
 
@@ -57,6 +60,8 @@
  * Test {@link AttributesNode}
  */
 public class AttributesNodeTest {
+	private static final TreeWalk DUMMY_WALK = new TreeWalk(
+			new InMemoryRepository(new DfsRepositoryDescription("FooBar")));
 
 	private static final Attribute A_SET_ATTR = new Attribute("A", SET);
 
@@ -162,9 +167,10 @@
 	}
 
 	private void assertAttribute(String path, AttributesNode node,
-			Attributes attrs) {
+			Attributes attrs) throws IOException {
 		Attributes attributes = new Attributes();
-		node.getAttributes(path, false, attributes);
+		new AttributesHandler(DUMMY_WALK).mergeAttributes(node, path, false,
+				attributes);
 		assertEquals(attrs, attributes);
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
index 4215ba2..b159cca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -219,14 +219,16 @@
 	}
 
 	private void assertAttributesNode(String pathName,
-			AttributesNode attributesNode, List<Attribute> nodeAttrs) {
+			AttributesNode attributesNode, List<Attribute> nodeAttrs)
+					throws IOException {
 		if (attributesNode == null)
 			assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
 		else {
 
 			Attributes entryAttributes = new Attributes();
-			attributesNode.getAttributes(pathName,
-					false, entryAttributes);
+			new AttributesHandler(walk).mergeAttributes(attributesNode,
+					pathName, false,
+					entryAttributes);
 
 			if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
 				for (Attribute attribute : nodeAttrs) {
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 77ef1a6..2cec34b 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
@@ -751,6 +751,59 @@
 		}
 	}
 
+	@Test
+	public void testRemoteRevision() 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=\".\" revision=\"")
+			.append(BRANCH)
+			.append("\" />")
+			.append("<default remote=\"remote1\" revision=\"master\" />")
+			.append("<project path=\"foo\" remote=\"remote2\" name=\"")
+			.append(defaultUri)
+			.append("\" />")
+			.append("</manifest>");
+		writeTrashFile("manifest.xml", xmlContent.toString());
+		RepoCommand command = new RepoCommand(db);
+		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
+			.setURI(rootUri)
+			.call();
+		File hello = new File(db.getWorkTree(), "foo/hello.txt");
+		BufferedReader reader = new BufferedReader(new FileReader(hello));
+		String content = reader.readLine();
+		reader.close();
+		assertEquals("submodule content should be as expected",
+				"branch world", content);
+	}
+
+	@Test
+	public void testDefaultRemoteRevision() throws Exception {
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+			.append("<manifest>")
+			.append("<remote name=\"remote1\" fetch=\".\" revision=\"")
+			.append(BRANCH)
+			.append("\" />")
+			.append("<default remote=\"remote1\" />")
+			.append("<project path=\"foo\" name=\"")
+			.append(defaultUri)
+			.append("\" />")
+			.append("</manifest>");
+		writeTrashFile("manifest.xml", xmlContent.toString());
+		RepoCommand command = new RepoCommand(db);
+		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
+			.setURI(rootUri)
+			.call();
+		File hello = new File(db.getWorkTree(), "foo/hello.txt");
+		BufferedReader reader = new BufferedReader(new FileReader(hello));
+		String content = reader.readLine();
+		reader.close();
+		assertEquals("submodule content should be as expected",
+				"branch world", content);
+	}
+
 	private void resolveRelativeUris() {
 		// Find the longest common prefix ends with "/" as rootUri.
 		defaultUri = defaultDb.getDirectory().toURI().toString();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index f549fb5..41a1a5d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -213,9 +213,20 @@
 		assertEquals(9, stats.numberOfPackedObjects);
 		assertEquals(2, stats.numberOfPackFiles);
 
+		// repack again but now without a grace period for loose objects. Since
+		// we don't have loose objects anymore this shouldn't change anything
+		gc.setExpireAgeMillis(0);
+		gc.gc();
+		stats = gc.getStatistics();
+		assertEquals(0, stats.numberOfLooseObjects);
+		// if objects exist in multiple packFiles then they are counted multiple
+		// times
+		assertEquals(9, stats.numberOfPackedObjects);
+		assertEquals(2, stats.numberOfPackFiles);
+
 		// repack again but now without a grace period for packfiles. We should
 		// end up with one packfile
-		gc.setExpireAgeMillis(0);
+		gc.setPackExpireAgeMillis(0);
 		gc.gc();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index 7adf074..4be1d6d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -104,9 +104,14 @@
 
 	private void delete(final RefUpdate ref, final Result expected,
 			final boolean exists, final boolean removed) throws IOException {
-		assertEquals(exists, db.getAllRefs().containsKey(ref.getName()));
+		delete(db, ref, expected, exists, removed);
+	}
+
+	private void delete(Repository repo, final RefUpdate ref, final Result expected,
+			final boolean exists, final boolean removed) throws IOException {
+		assertEquals(exists, repo.getAllRefs().containsKey(ref.getName()));
 		assertEquals(expected, ref.delete());
-		assertEquals(!removed, db.getAllRefs().containsKey(ref.getName()));
+		assertEquals(!removed, repo.getAllRefs().containsKey(ref.getName()));
 	}
 
 	@Test
@@ -232,6 +237,17 @@
 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
 	}
 
+	@Test
+	public void testDeleteHeadInBareRepo() throws IOException {
+		try (Repository bareRepo = createBareRepository()) {
+			RefUpdate ref = bareRepo.updateRef(Constants.HEAD);
+			ref.setNewObjectId(ObjectId.fromString("0123456789012345678901234567890123456789"));
+			// Create the HEAD ref so we can delete it.
+			assertEquals(Result.NEW, ref.update());
+			ref = bareRepo.updateRef(Constants.HEAD);
+			delete(bareRepo, ref, Result.NO_CHANGE, true, true);
+		}
+	}
 	/**
 	 * Delete a loose ref and make sure the directory in refs is deleted too,
 	 * and the reflog dir too
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
index 5fda070..49e1f6f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
@@ -95,7 +95,8 @@
 			}
 			currentCommits = nextCommitCount;
 
-			gc.setExpireAgeMillis(0); // immediately delete old packs
+			gc.setPackExpireAgeMillis(0); // immediately delete old packs
+			gc.setExpireAgeMillis(0);
 			gc.gc();
 			assertEquals(currentCommits * 3, // commit/tree/object
 					gc.getStatistics().numberOfPackedObjects);
@@ -158,7 +159,8 @@
 			}
 			currentCommits = nextCommitCount;
 
-			gc.setExpireAgeMillis(0); // immediately delete old packs
+			gc.setPackExpireAgeMillis(0); // immediately delete old packs
+			gc.setExpireAgeMillis(0);
 			gc.gc();
 			assertEquals(currentCommits + " commits: ", expectedBitmapCount,
 					gc.getStatistics().numberOfBitmaps);
@@ -198,7 +200,8 @@
 		final int commitsForShallowBranches = 100;
 
 		// Excessive branch history pruning, one old branch.
-		gc.setExpireAgeMillis(0); // immediately delete old packs
+		gc.setPackExpireAgeMillis(0); // immediately delete old packs
+		gc.setExpireAgeMillis(0);
 		gc.gc();
 		assertEquals(
 				commitsForSparseBranch + commitsForFullBranch
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
new file mode 100644
index 0000000..47f70d7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.ORIG_HEAD;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import static org.eclipse.jgit.lib.RefDatabase.ALL;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LocalDiskRefTreeDatabaseTest extends LocalDiskRepositoryTestCase {
+	private FileRepository repo;
+	private RefTreeDatabase refdb;
+	private RefDatabase bootstrap;
+
+	private TestRepository<FileRepository> testRepo;
+	private RevCommit A;
+	private RevCommit B;
+
+	@Before
+	public void setUp() throws Exception {
+		FileRepository init = createWorkRepository();
+		FileBasedConfig cfg = init.getConfig();
+		cfg.setInt("core", null, "repositoryformatversion", 1);
+		cfg.setString("extensions", null, "refsStorage", "reftree");
+		cfg.save();
+
+		repo = (FileRepository) new FileRepositoryBuilder()
+				.setGitDir(init.getDirectory())
+				.build();
+		refdb = (RefTreeDatabase) repo.getRefDatabase();
+		bootstrap = refdb.getBootstrap();
+		addRepoToClose(repo);
+
+		RefUpdate head = refdb.newUpdate(HEAD, true);
+		head.link(R_HEADS + MASTER);
+
+		testRepo = new TestRepository<>(init);
+		A = testRepo.commit().create();
+		B = testRepo.commit(testRepo.getRevWalk().parseCommit(A));
+	}
+
+	@Test
+	public void testHeadOrigHead() throws IOException {
+		RefUpdate master = refdb.newUpdate(HEAD, false);
+		master.setExpectedOldObjectId(ObjectId.zeroId());
+		master.setNewObjectId(A);
+		assertEquals(RefUpdate.Result.NEW, master.update());
+		assertEquals(A, refdb.exactRef(HEAD).getObjectId());
+
+		RefUpdate orig = refdb.newUpdate(ORIG_HEAD, true);
+		orig.setNewObjectId(B);
+		assertEquals(RefUpdate.Result.NEW, orig.update());
+
+		File origFile = new File(repo.getDirectory(), ORIG_HEAD);
+		assertEquals(B.name() + '\n', read(origFile));
+		assertEquals(B, bootstrap.exactRef(ORIG_HEAD).getObjectId());
+		assertEquals(B, refdb.exactRef(ORIG_HEAD).getObjectId());
+		assertFalse(refdb.getRefs(ALL).containsKey(ORIG_HEAD));
+
+		List<Ref> addl = refdb.getAdditionalRefs();
+		assertEquals(2, addl.size());
+		assertEquals(ORIG_HEAD, addl.get(1).getName());
+		assertEquals(B, addl.get(1).getObjectId());
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
index 020d1b1..e4d0f1d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.internal.storage.reftree;
 
 import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.ORIG_HEAD;
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
 import static org.eclipse.jgit.lib.Constants.R_TAGS;
 import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
@@ -124,6 +125,17 @@
 		assertTrue("no references", refdb.getRefs(ALL).isEmpty());
 		assertTrue("no references", refdb.getRefs(R_HEADS).isEmpty());
 		assertTrue("no references", refdb.getRefs(R_TAGS).isEmpty());
+		assertTrue("no references", refdb.getAdditionalRefs().isEmpty());
+	}
+
+	@Test
+	public void testGetAdditionalRefs() throws IOException {
+		update("refs/heads/master", A);
+
+		List<Ref> addl = refdb.getAdditionalRefs();
+		assertEquals(1, addl.size());
+		assertEquals("refs/txn/committed", addl.get(0).getName());
+		assertEquals(getTxnCommitted(), addl.get(0).getObjectId());
 	}
 
 	@Test
@@ -457,6 +469,27 @@
 	}
 
 	@Test
+	public void testUpdate_RefusesOrigHeadOnBare() throws IOException {
+		assertTrue(refdb.getRepository().isBare());
+		ObjectId txnId = getTxnCommitted();
+
+		RefUpdate orig = refdb.newUpdate(ORIG_HEAD, true);
+		orig.setNewObjectId(B);
+		assertEquals(RefUpdate.Result.LOCK_FAILURE, orig.update());
+		assertEquals(txnId, getTxnCommitted());
+
+		ReceiveCommand cmd = command(null, B, ORIG_HEAD);
+		BatchRefUpdate batch = refdb.newBatchUpdate();
+		batch.addCommand(cmd);
+		batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+		assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
+		assertEquals(
+				MessageFormat.format(JGitText.get().invalidRefName, ORIG_HEAD),
+				cmd.getMessage());
+		assertEquals(txnId, getTxnCommitted());
+	}
+
+	@Test
 	public void testBatchRefUpdate_NonFastForwardAborts() throws IOException {
 		update("refs/heads/master", A);
 		update("refs/heads/masters", B);
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 c1b882a..5578c03 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
@@ -65,6 +65,7 @@
 import org.eclipse.jgit.api.errors.NoFilepatternException;
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
 import org.eclipse.jgit.dircache.DirCacheEditor;
 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
 import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -113,7 +114,7 @@
 		return dco.getRemoved();
 	}
 
-	private Map<String, String> getUpdated() {
+	private Map<String, CheckoutMetadata> getUpdated() {
 		return dco.getUpdated();
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
index 6c62925..a1cec2d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
@@ -173,4 +173,39 @@
 		assertEquals(0, RepositoryCache.getRegisteredKeys().size());
 	}
 
+	@Test
+	public void testRepositoryUsageCount() throws Exception {
+		FileKey loc = FileKey.exact(db.getDirectory(), db.getFS());
+		Repository d2 = RepositoryCache.open(loc);
+		assertEquals(1, d2.useCnt.get());
+		RepositoryCache.open(FileKey.exact(loc.getFile(), db.getFS()));
+		assertEquals(2, d2.useCnt.get());
+		d2.close();
+		assertEquals(1, d2.useCnt.get());
+		d2.close();
+		assertEquals(0, d2.useCnt.get());
+	}
+
+	@Test
+	public void testRepositoryUsageCountWithRegisteredRepository() {
+		assertEquals(1, ((Repository) db).useCnt.get());
+		RepositoryCache.register(db);
+		assertEquals(1, ((Repository) db).useCnt.get());
+		db.close();
+		assertEquals(0, ((Repository) db).useCnt.get());
+	}
+
+	public void testRepositoryUnregisteringWhenClosing() throws Exception {
+		FileKey loc = FileKey.exact(db.getDirectory(), db.getFS());
+		Repository d2 = RepositoryCache.open(loc);
+		assertEquals(1, d2.useCnt.get());
+		assertThat(RepositoryCache.getRegisteredKeys(),
+				hasItem(FileKey.exact(db.getDirectory(), db.getFS())));
+		assertEquals(1, RepositoryCache.getRegisteredKeys().size());
+
+		d2.close();
+
+		assertEquals(0, d2.useCnt.get());
+		assertEquals(0, RepositoryCache.getRegisteredKeys().size());
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index df17a3e..bf1d0e6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -62,15 +62,19 @@
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
 import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff;
 import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.RawParseUtils;
 import org.junit.Before;
@@ -281,6 +285,37 @@
 	}
 
 	@Test
+	public void testTreewalkEnterSubtree() throws Exception {
+		try (Git git = new Git(db)) {
+			writeTrashFile("b/c", "b/c");
+			writeTrashFile("z/.git", "gitdir: /tmp/somewhere");
+			git.add().addFilepattern(".").call();
+			git.rm().addFilepattern("a,").addFilepattern("a,b")
+					.addFilepattern("a0b").call();
+			assertEquals("[a/b, mode:100644][b/c, mode:100644][z, mode:160000]",
+					indexState(0));
+			FileUtils.delete(new File(db.getWorkTree(), "b"),
+					FileUtils.RECURSIVE);
+
+			TreeWalk tw = new TreeWalk(db);
+			tw.addTree(new DirCacheIterator(db.readDirCache()));
+			tw.addTree(new FileTreeIterator(db));
+			assertTrue(tw.next());
+			assertEquals("a", tw.getPathString());
+			tw.enterSubtree();
+			tw.next();
+			assertEquals("a/b", tw.getPathString());
+			tw.next();
+			assertEquals("b", tw.getPathString());
+			tw.enterSubtree();
+			tw.next();
+			assertEquals("b/c", tw.getPathString());
+			assertNotNull(tw.getTree(0, AbstractTreeIterator.class));
+			assertNotNull(tw.getTree(EmptyTreeIterator.class));
+		}
+	}
+
+	@Test
 	public void testIsModifiedSymlinkAsFile() throws Exception {
 		writeTrashFile("symlink", "content");
 		try (Git git = new Git(db)) {
@@ -346,7 +381,7 @@
 			DirCache cache = db.lockDirCache();
 			DirCacheEditor editor = cache.editor();
 			editor.add(new PathEdit(path) {
-	
+
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.GITLINK);
 					ent.setObjectId(id);
@@ -363,7 +398,7 @@
 			walk.addTree(indexIter);
 			walk.addTree(workTreeIter);
 			walk.setFilter(PathFilter.create(path));
-	
+
 			assertTrue(walk.next());
 			assertTrue(indexIter.idEqual(workTreeIter));
 		}
@@ -537,6 +572,87 @@
 		}
 	}
 
+	private final FileTreeIterator.FileModeStrategy NO_GITLINKS_STRATEGY =
+			new FileTreeIterator.FileModeStrategy() {
+				@Override
+				public FileMode getMode(File f, FS.Attributes attributes) {
+					if (attributes.isSymbolicLink()) {
+						return FileMode.SYMLINK;
+					} else if (attributes.isDirectory()) {
+						// NOTE: in the production DefaultFileModeStrategy, there is
+						// a check here for a subdirectory called '.git', and if it
+						// exists, we create a GITLINK instead of recursing into the
+						// tree.  In this custom strategy, we ignore nested git dirs
+						// and treat all directories the same.
+						return FileMode.TREE;
+					} else if (attributes.isExecutable()) {
+						return FileMode.EXECUTABLE_FILE;
+					} else {
+						return FileMode.REGULAR_FILE;
+					}
+				}
+			};
+
+	private Repository createNestedRepo() throws IOException {
+		File gitdir = createUniqueTestGitDir(false);
+		FileRepositoryBuilder builder = new FileRepositoryBuilder();
+		builder.setGitDir(gitdir);
+		Repository nestedRepo = builder.build();
+		nestedRepo.create();
+
+		JGitTestUtil.writeTrashFile(nestedRepo, "sub", "a.txt", "content");
+
+		File nestedRepoPath = new File(nestedRepo.getWorkTree(), "sub/nested");
+		FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
+		nestedBuilder.setWorkTree(nestedRepoPath);
+		nestedBuilder.build().create();
+
+		JGitTestUtil.writeTrashFile(nestedRepo, "sub/nested", "b.txt",
+				"content b");
+
+		return nestedRepo;
+	}
+
+	@Test
+	public void testCustomFileModeStrategy() throws Exception {
+		Repository nestedRepo = createNestedRepo();
+
+		Git git = new Git(nestedRepo);
+		// validate that our custom strategy is honored
+		WorkingTreeIterator customIterator =
+				new FileTreeIterator(nestedRepo, NO_GITLINKS_STRATEGY);
+		git.add().setWorkingTreeIterator(customIterator)
+				.addFilepattern(".").call();
+		assertEquals(
+				"[sub/a.txt, mode:100644, content:content]" +
+				"[sub/nested/b.txt, mode:100644, content:content b]",
+				indexState(nestedRepo, CONTENT));
+
+	}
+
+	@Test
+	public void testCustomFileModeStrategyFromParentIterator() throws Exception {
+		Repository nestedRepo = createNestedRepo();
+
+		Git git = new Git(nestedRepo);
+
+		FileTreeIterator customIterator =
+				new FileTreeIterator(nestedRepo, NO_GITLINKS_STRATEGY);
+		File r = new File(nestedRepo.getWorkTree(), "sub");
+
+		// here we want to validate that if we create a new iterator using the
+		// constructor that accepts a parent iterator, that the child iterator
+		// correctly inherits the FileModeStrategy from the parent iterator.
+		FileTreeIterator childIterator =
+				new FileTreeIterator(customIterator, r, nestedRepo.getFS());
+		git.add().setWorkingTreeIterator(childIterator).addFilepattern(".").call();
+		assertEquals(
+				"[sub/a.txt, mode:100644, content:content]" +
+				"[sub/nested/b.txt, mode:100644, content:content b]",
+				indexState(nestedRepo, CONTENT));
+	}
+
+
 	private static void assertEntry(String sha1string, String path, TreeWalk tw)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			CorruptObjectException, IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
index 06cb11b..7e11a61 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Google Inc.
+ * Copyright (C) 2008, 2015 Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -61,6 +61,17 @@
 	}
 
 	@Test
+	public void testCompareUInt64() {
+		assertTrue(NB.compareUInt64(0, 0) == 0);
+		assertTrue(NB.compareUInt64(1, 0) > 0);
+		assertTrue(NB.compareUInt64(0, 1) < 0);
+		assertTrue(NB.compareUInt64(-1, 0) > 0);
+		assertTrue(NB.compareUInt64(0, -1) < 0);
+		assertTrue(NB.compareUInt64(-1, 1) > 0);
+		assertTrue(NB.compareUInt64(1, -1) < 0);
+	}
+
+	@Test
 	public void testDecodeUInt16() {
 		assertEquals(0, NB.decodeUInt16(b(0, 0), 0));
 		assertEquals(0, NB.decodeUInt16(padb(3, 0, 0), 3));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java
similarity index 94%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java
index ed2a4f2..40cac93 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010, Marc Strapetz <marc.strapetz@syntevo.com>
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -52,7 +53,7 @@
 
 import org.junit.Test;
 
-public class EolCanonicalizingInputStreamTest {
+public class AutoLFInputStreamTest {
 
 	@Test
 	public void testLF() throws IOException {
@@ -97,7 +98,7 @@
 	private static void test(byte[] input, byte[] expected,
 			boolean detectBinary) throws IOException {
 		final InputStream bis1 = new ByteArrayInputStream(input);
-		final InputStream cis1 = new EolCanonicalizingInputStream(bis1, detectBinary);
+		final InputStream cis1 = new AutoLFInputStream(bis1, detectBinary);
 		int index1 = 0;
 		for (int b = cis1.read(); b != -1; b = cis1.read()) {
 			assertEquals(expected[index1], (byte) b);
@@ -109,7 +110,7 @@
 		for (int bufferSize = 1; bufferSize < 10; bufferSize++) {
 			final byte[] buffer = new byte[bufferSize];
 			final InputStream bis2 = new ByteArrayInputStream(input);
-			final InputStream cis2 = new EolCanonicalizingInputStream(bis2, detectBinary);
+			final InputStream cis2 = new AutoLFInputStream(bis2, detectBinary);
 
 			int read = 0;
 			for (int readNow = cis2.read(buffer, 0, buffer.length); readNow != -1
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index f9e1a39..db339e0 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Export-Package: org.eclipse.jgit.awtui;version="4.2.1"
-Import-Package: org.eclipse.jgit.errors;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.lib;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.nls;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revplot;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.transport;version="[4.2.1,4.3.0)",
- org.eclipse.jgit.util;version="[4.2.1,4.3.0)"
+Export-Package: org.eclipse.jgit.awtui;version="4.3.2"
+Import-Package: org.eclipse.jgit.errors;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revplot;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.2,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.2,4.4.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index c80f31f..fdd6dad 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-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 36041f8..c0dbc77 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,115 +1,39 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.jgit" version="2">
-    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.FileTreeEntry">
-        <filter id="305324134">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.FileTreeEntry"/>
-                <message_argument value="org.eclipse.jgit_4.2.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.GitlinkTreeEntry">
-        <filter id="305324134">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.GitlinkTreeEntry"/>
-                <message_argument value="org.eclipse.jgit_4.2.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.SymlinkTreeEntry">
-        <filter id="305324134">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.SymlinkTreeEntry"/>
-                <message_argument value="org.eclipse.jgit_4.2.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.Tree">
-        <filter id="305324134">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.Tree"/>
-                <message_argument value="org.eclipse.jgit_4.2.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.TreeEntry">
-        <filter id="305324134">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.TreeEntry"/>
-                <message_argument value="org.eclipse.jgit_4.2.0"/>
-            </message_arguments>
-        </filter>
-    </resource>
     <resource path="src/org/eclipse/jgit/attributes/AttributesNode.java" type="org.eclipse.jgit.attributes.AttributesNode">
-        <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+        <filter comment="moved to new AttributesManager" id="338792546">
             <message_arguments>
                 <message_argument value="org.eclipse.jgit.attributes.AttributesNode"/>
-                <message_argument value="getAttributes(String, boolean, Map&lt;String,Attribute&gt;)"/>
+                <message_argument value="getAttributes(String, boolean, Attributes)"/>
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$BitmapBuilder">
-        <filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
+    <resource path="src/org/eclipse/jgit/attributes/AttributesRule.java" type="org.eclipse.jgit.attributes.AttributesRule">
+        <filter comment="used only in tests: bean naming" id="338792546">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder"/>
-                <message_argument value="addObject(AnyObjectId, int)"/>
-            </message_arguments>
-        </filter>
-        <filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder"/>
-                <message_argument value="getBitmapIndex()"/>
+                <message_argument value="org.eclipse.jgit.attributes.AttributesRule"/>
+                <message_argument value="dirOnly()"/>
             </message_arguments>
         </filter>
     </resource>
-    <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository">
-        <filter comment="Only implementors of Repository are affected. That should be allowed" id="336695337">
+    <resource path="src/org/eclipse/jgit/dircache/DirCacheCheckout.java" type="org.eclipse.jgit.dircache.DirCacheCheckout">
+        <filter comment="add eol stream type conversion" id="338792546">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.lib.Repository"/>
-                <message_argument value="createAttributesNodeProvider()"/>
+                <message_argument value="org.eclipse.jgit.dircache.DirCacheCheckout"/>
+                <message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean)"/>
             </message_arguments>
         </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/transport/PushCertificate.java" type="org.eclipse.jgit.transport.PushCertificate">
-        <filter comment="PushCertificate wasn't really usable in 4.0" id="338722907">
+        <filter comment="add eol stream type conversion" id="338792546">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.transport.PushCertificate"/>
-                <message_argument value="PushCertificate()"/>
+                <message_argument value="org.eclipse.jgit.dircache.DirCacheCheckout"/>
+                <message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, String)"/>
             </message_arguments>
         </filter>
-        <filter comment="PushCertificate wasn't really usable in 4.0" id="338792546">
+        <filter comment="add eol stream type conversion" id="1141899266">
             <message_arguments>
-                <message_argument value="org.eclipse.jgit.transport.PushCertificate"/>
-                <message_argument value="getCommandList()"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/transport/PushCertificateParser.java" type="org.eclipse.jgit.transport.PushCertificateParser">
-        <filter comment="PushCertificates haven't been really usable in 4.0" id="338849923">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.transport.PushCertificateParser"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator">
-        <filter comment="attributes weren't really usable in earlier versions" id="338792546">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
-                <message_argument value="getGlobalAttributesNode()"/>
-            </message_arguments>
-        </filter>
-        <filter comment="attributes weren't really usable in earlier versions" id="338792546">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
-                <message_argument value="getInfoAttributesNode()"/>
-            </message_arguments>
-        </filter>
-    </resource>
-    <resource path="src/org/eclipse/jgit/util/FileUtils.java" type="org.eclipse.jgit.util.FileUtils">
-        <filter id="338792546">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.util.FileUtils"/>
-                <message_argument value="createSymLink(File, String)"/>
+                <message_argument value="4.2"/>
+                <message_argument value="4.3"/>
+                <message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, DirCacheCheckout.CheckoutMetadata)"/>
             </message_arguments>
         </filter>
     </resource>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index d0f03ff..f94c22c 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="4.2.1",
- org.eclipse.jgit.api;version="4.2.1";
+Export-Package: org.eclipse.jgit.annotations;version="4.3.2",
+ org.eclipse.jgit.api;version="4.3.2";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
@@ -21,55 +21,57 @@
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="4.2.1",
- org.eclipse.jgit.blame;version="4.2.1";
+ org.eclipse.jgit.api.errors;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.3.2",
+ org.eclipse.jgit.blame;version="4.3.2";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="4.2.1";
+ org.eclipse.jgit.diff;version="4.3.2";
   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.2.1";
+ org.eclipse.jgit.dircache;version="4.3.2";
   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.2.1";
+ org.eclipse.jgit.errors;version="4.3.2";
   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.2.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="4.2.1",
- org.eclipse.jgit.gitrepo;version="4.2.1";
+ org.eclipse.jgit.events;version="4.3.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.3.2",
+ org.eclipse.jgit.gitrepo;version="4.3.2";
   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.2.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.2.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="4.2.1",
- org.eclipse.jgit.ignore.internal;version="4.2.1";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="4.2.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.storage.dfs;version="4.2.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
- org.eclipse.jgit.internal.storage.file;version="4.2.1";
+ org.eclipse.jgit.gitrepo.internal;version="4.3.2";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.3.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.3.2",
+ org.eclipse.jgit.ignore.internal;version="4.3.2";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.3.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.ketch;version="4.3.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.3.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
+ org.eclipse.jgit.internal.storage.file;version="4.3.2";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
    org.eclipse.jgit.http.server,
-   org.eclipse.jgit.pgm.test,
-   org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="4.2.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="4.2.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="4.2.1";
+   org.eclipse.jgit.lfs.server,
+   org.eclipse.jgit.pgm,
+   org.eclipse.jgit.pgm.test",
+ org.eclipse.jgit.internal.storage.pack;version="4.3.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.3.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.3.2";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
@@ -79,32 +81,32 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.submodule",
- org.eclipse.jgit.merge;version="4.2.1";
+ org.eclipse.jgit.merge;version="4.3.2";
   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.2.1",
- org.eclipse.jgit.notes;version="4.2.1";
+ org.eclipse.jgit.nls;version="4.3.2",
+ org.eclipse.jgit.notes;version="4.3.2";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="4.2.1";
+ org.eclipse.jgit.patch;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.3.2";
   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.2.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.2.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="4.2.1";
+ org.eclipse.jgit.revwalk.filter;version="4.3.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.3.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.3.2";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -116,22 +118,22 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.2.1";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="4.2.1";
+ org.eclipse.jgit.transport.http;version="4.3.2";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.3.2";
   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.2.1";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="4.2.1";
+ org.eclipse.jgit.treewalk.filter;version="4.3.2";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.3.2";
   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.2.1"
+ org.eclipse.jgit.util.io;version="4.3.2"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
 Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 98ede31..3bf02b7 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.2.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="4.2.1.qualifier";roots="."
+Bundle-Version: 4.3.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.3.2.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index e931990..40d15c8 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.2.1-SNAPSHOT</version>
+    <version>4.3.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties
new file mode 100644
index 0000000..1fbb7cb
--- /dev/null
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties
@@ -0,0 +1,13 @@
+accepted=accepted.
+cannotFetchFromLocalReplica=cannot fetch from LocalReplica
+failed=failed!
+invalidFollowerUri=invalid follower URI
+leaderFailedToStore=leader failed to store
+localReplicaRequired=LocalReplica instance is required
+mismatchedTxnNamespace=mismatched txnNamespace; expected {0} found {1}
+outsideTxnNamespace=ref {0} is outside of txnNamespace {1}
+proposingUpdates=Proposing updates
+queuedProposalFailedToApply=queued proposal failed to apply
+starting=starting!
+unsupportedVoterCount=unsupported voter count {0}, expected one of {1}
+waitingForQueue=Waiting for queue
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index 3b94f16..1f37833 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -45,6 +45,7 @@
 
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 import static org.eclipse.jgit.lib.FileMode.GITLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
 import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
 
 import java.io.IOException;
@@ -201,7 +202,10 @@
 					continue;
 				}
 
-				if (f.getEntryRawMode() == TYPE_TREE) {
+				if ((f.getEntryRawMode() == TYPE_TREE
+						&& f.getIndexFileMode(c) != FileMode.GITLINK) ||
+						(f.getEntryRawMode() == TYPE_GITLINK
+								&& f.getIndexFileMode(c) == FileMode.TREE)) {
 					// Index entry exists and is symlink, gitlink or file,
 					// otherwise the tree would have been entered above.
 					// Replace the index entry by diving into tree of files.
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 676ae03..8fbf839 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -201,10 +201,12 @@
 			oldLines.add(rt.getString(i));
 		List<String> newLines = new ArrayList<String>(oldLines);
 		for (HunkHeader hh : fh.getHunks()) {
-			StringBuilder hunk = new StringBuilder();
-			for (int j = hh.getStartOffset(); j < hh.getEndOffset(); j++)
-				hunk.append((char) hh.getBuffer()[j]);
-			RawText hrt = new RawText(hunk.toString().getBytes());
+
+			byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()];
+			System.arraycopy(hh.getBuffer(), hh.getStartOffset(), b, 0,
+					b.length);
+			RawText hrt = new RawText(b);
+
 			List<String> hunkLines = new ArrayList<String>(hrt.size());
 			for (int i = 0; i < hrt.size(); i++)
 				hunkLines.add(hrt.getString(i));
@@ -221,12 +223,16 @@
 					pos++;
 					break;
 				case '-':
-					if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals(
-							hunkLine.substring(1))) {
-						throw new PatchApplyException(MessageFormat.format(
-								JGitText.get().patchApplyException, hh));
+					if (hh.getNewStartLine() == 0) {
+						newLines.clear();
+					} else {
+						if (!newLines.get(hh.getNewStartLine() - 1 + pos)
+								.equals(hunkLine.substring(1))) {
+							throw new PatchApplyException(MessageFormat.format(
+									JGitText.get().patchApplyException, hh));
+						}
+						newLines.remove(hh.getNewStartLine() - 1 + pos);
 					}
-					newLines.remove(hh.getNewStartLine() - 1 + pos);
 					break;
 				case '+':
 					newLines.add(hh.getNewStartLine() - 1 + pos,
@@ -248,7 +254,9 @@
 			// still there!
 			sb.append(l).append('\n');
 		}
-		sb.deleteCharAt(sb.length() - 1);
+		if (sb.length() > 0) {
+			sb.deleteCharAt(sb.length() - 1);
+		}
 		FileWriter fw = new FileWriter(f);
 		fw.write(sb.toString());
 		fw.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
index a83814e..d803efd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
@@ -66,7 +66,7 @@
 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
 import org.eclipse.jgit.util.IO;
-import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
+import org.eclipse.jgit.util.io.AutoLFInputStream;
 
 /**
  * Blame command for building a {@link BlameResult} for a file path.
@@ -248,7 +248,7 @@
 			rawText = new RawText(inTree);
 			break;
 		case TRUE:
-			EolCanonicalizingInputStream in = new EolCanonicalizingInputStream(
+			AutoLFInputStream in = new AutoLFInputStream(
 					new FileInputStream(inTree), true);
 			// Canonicalization should lead to same or shorter length
 			// (CRLF to LF), so the file size on disk is an upper size bound
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 4f918fa..6c80289 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -59,6 +59,7 @@
 import org.eclipse.jgit.api.errors.RefNotFoundException;
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
 import org.eclipse.jgit.dircache.DirCacheEditor;
 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
 import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -68,6 +69,7 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.AnyObjectId;
 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.ObjectReader;
@@ -395,7 +397,8 @@
 			RefNotFoundException {
 		DirCache dc = repo.lockDirCache();
 		try (RevWalk revWalk = new RevWalk(repo);
-				TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader())) {
+				TreeWalk treeWalk = new TreeWalk(repo,
+						revWalk.getObjectReader())) {
 			treeWalk.setRecursive(true);
 			if (!checkoutAllPaths)
 				treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
@@ -426,20 +429,25 @@
 			if (path.equals(previousPath))
 				continue;
 
+			final EolStreamType eolStreamType = treeWalk.getEolStreamType();
+			final String filterCommand = treeWalk
+					.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
 			editor.add(new PathEdit(path) {
 				public void apply(DirCacheEntry ent) {
 					int stage = ent.getStage();
 					if (stage > DirCacheEntry.STAGE_0) {
 						if (checkoutStage != null) {
 							if (stage == checkoutStage.number)
-								checkoutPath(ent, r);
+								checkoutPath(ent, r, new CheckoutMetadata(
+										eolStreamType, filterCommand));
 						} else {
 							UnmergedPathException e = new UnmergedPathException(
 									ent);
 							throw new JGitInternalException(e.getMessage(), e);
 						}
 					} else {
-						checkoutPath(ent, r);
+						checkoutPath(ent, r, new CheckoutMetadata(eolStreamType,
+								filterCommand));
 					}
 				}
 			});
@@ -457,20 +465,26 @@
 		while (treeWalk.next()) {
 			final ObjectId blobId = treeWalk.getObjectId(0);
 			final FileMode mode = treeWalk.getFileMode(0);
+			final EolStreamType eolStreamType = treeWalk.getEolStreamType();
+			final String filterCommand = treeWalk
+					.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
 			editor.add(new PathEdit(treeWalk.getPathString()) {
 				public void apply(DirCacheEntry ent) {
 					ent.setObjectId(blobId);
 					ent.setFileMode(mode);
-					checkoutPath(ent, r);
+					checkoutPath(ent, r,
+							new CheckoutMetadata(eolStreamType, filterCommand));
 				}
 			});
 		}
 		editor.commit();
 	}
 
-	private void checkoutPath(DirCacheEntry entry, ObjectReader reader) {
+	private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
+			CheckoutMetadata checkoutMetadata) {
 		try {
-			DirCacheCheckout.checkoutEntry(repo, entry, reader, true);
+			DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
+					checkoutMetadata);
 		} catch (IOException e) {
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().checkoutConflictWithFile,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index 8ef5508..1699b9f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -54,11 +54,13 @@
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheBuilder;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.dircache.DirCacheIterator;
 import org.eclipse.jgit.errors.CheckoutConflictException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
@@ -336,6 +338,7 @@
 					// Not in commit, don't create untracked
 					continue;
 
+				final EolStreamType eolStreamType = walk.getEolStreamType();
 				final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath());
 				entry.setFileMode(cIter.getEntryFileMode());
 				entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset());
@@ -350,14 +353,17 @@
 					}
 				}
 
-				checkoutPath(entry, reader);
+				checkoutPath(entry, reader,
+						new CheckoutMetadata(eolStreamType, null));
 			}
 		}
 	}
 
-	private void checkoutPath(DirCacheEntry entry, ObjectReader reader) {
+	private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
+			CheckoutMetadata checkoutMetadata) {
 		try {
-			DirCacheCheckout.checkoutEntry(repo, entry, reader, true);
+			DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
+					checkoutMetadata);
 		} catch (IOException e) {
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().checkoutConflictWithFile,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
index 2cdaf24..ef32ac9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -245,12 +245,14 @@
 			DirCache cache = repo.lockDirCache();
 			ObjectId commitId;
 			try (ObjectInserter inserter = repo.newObjectInserter();
-					TreeWalk treeWalk = new TreeWalk(reader)) {
+					TreeWalk treeWalk = new TreeWalk(repo, reader)) {
 
 				treeWalk.setRecursive(true);
 				treeWalk.addTree(headCommit.getTree());
 				treeWalk.addTree(new DirCacheIterator(cache));
 				treeWalk.addTree(new FileTreeIterator(repo));
+				treeWalk.getTree(2, FileTreeIterator.class)
+						.setDirCacheIterator(treeWalk, 1);
 				treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter(
 						1), new IndexDiffFilter(1, 2)));
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java
new file mode 100644
index 0000000..19e4afd
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * 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.attributes;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
+
+/**
+ * The attributes handler knows how to retrieve, parse and merge attributes from
+ * the various gitattributes files. Furthermore it collects and expands macro
+ * expressions. The method {@link #getAttributes()} yields the ready processed
+ * attributes for the current path represented by the {@link TreeWalk}
+ * <p>
+ * The implementation is based on the specifications in
+ * http://git-scm.com/docs/gitattributes
+ *
+ * @since 4.3
+ */
+public class AttributesHandler {
+	private static final String MACRO_PREFIX = "[attr]"; //$NON-NLS-1$
+
+	private static final String BINARY_RULE_KEY = "binary"; //$NON-NLS-1$
+
+	/**
+	 * This is the default <b>binary</b> rule that is present in any git folder
+	 * <code>[attr]binary -diff -merge -text</code>
+	 */
+	private static final List<Attribute> BINARY_RULE_ATTRIBUTES = new AttributesRule(
+			MACRO_PREFIX + BINARY_RULE_KEY, "-diff -merge -text") //$NON-NLS-1$
+					.getAttributes();
+
+	private final TreeWalk treeWalk;
+
+	private final AttributesNode globalNode;
+
+	private final AttributesNode infoNode;
+
+	private final Map<String, List<Attribute>> expansions = new HashMap<>();
+
+	/**
+	 * Create an {@link AttributesHandler} with default rules as well as merged
+	 * rules from global, info and worktree root attributes
+	 *
+	 * @param treeWalk
+	 * @throws IOException
+	 */
+	public AttributesHandler(TreeWalk treeWalk) throws IOException {
+		this.treeWalk = treeWalk;
+		AttributesNodeProvider attributesNodeProvider =treeWalk.getAttributesNodeProvider();
+		this.globalNode = attributesNodeProvider != null
+				? attributesNodeProvider.getGlobalAttributesNode() : null;
+		this.infoNode = attributesNodeProvider != null
+				? attributesNodeProvider.getInfoAttributesNode() : null;
+
+		AttributesNode rootNode = attributesNode(treeWalk,
+				rootOf(
+						treeWalk.getTree(WorkingTreeIterator.class)),
+				rootOf(
+						treeWalk.getTree(DirCacheIterator.class)),
+				rootOf(treeWalk
+						.getTree(CanonicalTreeParser.class)));
+
+		expansions.put(BINARY_RULE_KEY, BINARY_RULE_ATTRIBUTES);
+		for (AttributesNode node : new AttributesNode[] { globalNode, rootNode,
+				infoNode }) {
+			if (node == null) {
+				continue;
+			}
+			for (AttributesRule rule : node.getRules()) {
+				if (rule.getPattern().startsWith(MACRO_PREFIX)) {
+					expansions.put(rule.getPattern()
+							.substring(MACRO_PREFIX.length()).trim(),
+							rule.getAttributes());
+				}
+			}
+		}
+	}
+
+	/**
+	 * see {@link TreeWalk#getAttributes()}
+	 *
+	 * @return the {@link Attributes} for the current path represented by the
+	 *         {@link TreeWalk}
+	 * @throws IOException
+	 */
+	public Attributes getAttributes() throws IOException {
+		String entryPath = treeWalk.getPathString();
+		boolean isDirectory = (treeWalk.getFileMode() == FileMode.TREE);
+		Attributes attributes = new Attributes();
+
+		// Gets the info attributes
+		mergeInfoAttributes(entryPath, isDirectory, attributes);
+
+		// Gets the attributes located on the current entry path
+		mergePerDirectoryEntryAttributes(entryPath, isDirectory,
+				treeWalk.getTree(WorkingTreeIterator.class),
+				treeWalk.getTree(DirCacheIterator.class),
+				treeWalk.getTree(CanonicalTreeParser.class),
+				attributes);
+
+		// Gets the attributes located in the global attribute file
+		mergeGlobalAttributes(entryPath, isDirectory, attributes);
+
+		// now after all attributes are collected - in the correct hierarchy
+		// order - remove all unspecified entries (the ! marker)
+		for (Attribute a : attributes.getAll()) {
+			if (a.getState() == State.UNSPECIFIED)
+				attributes.remove(a.getKey());
+		}
+
+		return attributes;
+	}
+
+	/**
+	 * Merges the matching GLOBAL attributes for an entry path.
+	 *
+	 * @param entryPath
+	 *            the path to test. The path must be relative to this attribute
+	 *            node's own repository path, and in repository path format
+	 *            (uses '/' and not '\').
+	 * @param isDirectory
+	 *            true if the target item is a directory.
+	 * @param result
+	 *            that will hold the attributes matching this entry path. This
+	 *            method will NOT override any existing entry in attributes.
+	 */
+	private void mergeGlobalAttributes(String entryPath, boolean isDirectory,
+			Attributes result) {
+		mergeAttributes(globalNode, entryPath, isDirectory, result);
+	}
+
+	/**
+	 * Merges the matching INFO attributes for an entry path.
+	 *
+	 * @param entryPath
+	 *            the path to test. The path must be relative to this attribute
+	 *            node's own repository path, and in repository path format
+	 *            (uses '/' and not '\').
+	 * @param isDirectory
+	 *            true if the target item is a directory.
+	 * @param result
+	 *            that will hold the attributes matching this entry path. This
+	 *            method will NOT override any existing entry in attributes.
+	 */
+	private void mergeInfoAttributes(String entryPath, boolean isDirectory,
+			Attributes result) {
+		mergeAttributes(infoNode, entryPath, isDirectory, result);
+	}
+
+	/**
+	 * Merges the matching working directory attributes for an entry path.
+	 *
+	 * @param entryPath
+	 *            the path to test. The path must be relative to this attribute
+	 *            node's own repository path, and in repository path format
+	 *            (uses '/' and not '\').
+	 * @param isDirectory
+	 *            true if the target item is a directory.
+	 * @param workingTreeIterator
+	 * @param dirCacheIterator
+	 * @param otherTree
+	 * @param result
+	 *            that will hold the attributes matching this entry path. This
+	 *            method will NOT override any existing entry in attributes.
+	 * @throws IOException
+	 */
+	private void mergePerDirectoryEntryAttributes(String entryPath,
+			boolean isDirectory,
+			@Nullable WorkingTreeIterator workingTreeIterator,
+			@Nullable DirCacheIterator dirCacheIterator,
+			@Nullable CanonicalTreeParser otherTree, Attributes result)
+					throws IOException {
+		// Prevents infinite recurrence
+		if (workingTreeIterator != null || dirCacheIterator != null
+				|| otherTree != null) {
+			AttributesNode attributesNode = attributesNode(
+					treeWalk, workingTreeIterator, dirCacheIterator, otherTree);
+			if (attributesNode != null) {
+				mergeAttributes(attributesNode, entryPath, isDirectory, result);
+			}
+			mergePerDirectoryEntryAttributes(entryPath, isDirectory,
+					parentOf(workingTreeIterator), parentOf(dirCacheIterator),
+					parentOf(otherTree), result);
+		}
+	}
+
+	/**
+	 * Merges the matching node attributes for an entry path.
+	 *
+	 * @param node
+	 *            the node to scan for matches to entryPath
+	 * @param entryPath
+	 *            the path to test. The path must be relative to this attribute
+	 *            node's own repository path, and in repository path format
+	 *            (uses '/' and not '\').
+	 * @param isDirectory
+	 *            true if the target item is a directory.
+	 * @param result
+	 *            that will hold the attributes matching this entry path. This
+	 *            method will NOT override any existing entry in attributes.
+	 */
+	protected void mergeAttributes(@Nullable AttributesNode node,
+			String entryPath,
+			boolean isDirectory, Attributes result) {
+		if (node == null)
+			return;
+		List<AttributesRule> rules = node.getRules();
+		// Parse rules in the reverse order that they were read since the last
+		// entry should be used
+		ListIterator<AttributesRule> ruleIterator = rules
+				.listIterator(rules.size());
+		while (ruleIterator.hasPrevious()) {
+			AttributesRule rule = ruleIterator.previous();
+			if (rule.isMatch(entryPath, isDirectory)) {
+				ListIterator<Attribute> attributeIte = rule.getAttributes()
+						.listIterator(rule.getAttributes().size());
+				// Parses the attributes in the reverse order that they were
+				// read since the last entry should be used
+				while (attributeIte.hasPrevious()) {
+					expandMacro(attributeIte.previous(), result);
+				}
+			}
+		}
+	}
+
+	/**
+	 * @param attr
+	 * @param result
+	 *            contains the (recursive) expanded and merged macro attributes
+	 *            including the attribute iself
+	 */
+	protected void expandMacro(Attribute attr, Attributes result) {
+		// loop detection = exists check
+		if (result.containsKey(attr.getKey()))
+			return;
+
+		// also add macro to result set, same does native git
+		result.put(attr);
+
+		List<Attribute> expansion = expansions.get(attr.getKey());
+		if (expansion == null) {
+			return;
+		}
+		switch (attr.getState()) {
+		case UNSET: {
+			for (Attribute e : expansion) {
+				switch (e.getState()) {
+				case SET:
+					expandMacro(new Attribute(e.getKey(), State.UNSET), result);
+					break;
+				case UNSET:
+					expandMacro(new Attribute(e.getKey(), State.SET), result);
+					break;
+				case UNSPECIFIED:
+					expandMacro(new Attribute(e.getKey(), State.UNSPECIFIED),
+							result);
+					break;
+				case CUSTOM:
+				default:
+					expandMacro(e, result);
+				}
+			}
+			break;
+		}
+		case CUSTOM: {
+			for (Attribute e : expansion) {
+				switch (e.getState()) {
+				case SET:
+				case UNSET:
+				case UNSPECIFIED:
+					expandMacro(e, result);
+					break;
+				case CUSTOM:
+				default:
+					expandMacro(new Attribute(e.getKey(), attr.getValue()),
+							result);
+				}
+			}
+			break;
+		}
+		case UNSPECIFIED: {
+			for (Attribute e : expansion) {
+				expandMacro(new Attribute(e.getKey(), State.UNSPECIFIED),
+						result);
+			}
+			break;
+		}
+		case SET:
+		default:
+			for (Attribute e : expansion) {
+				expandMacro(e, result);
+			}
+			break;
+		}
+	}
+
+	/**
+	 * Get the {@link AttributesNode} for the current entry.
+	 * <p>
+	 * This method implements the fallback mechanism between the index and the
+	 * working tree depending on the operation type
+	 * </p>
+	 *
+	 * @param treeWalk
+	 * @param workingTreeIterator
+	 * @param dirCacheIterator
+	 * @param otherTree
+	 * @return a {@link AttributesNode} of the current entry,
+	 *         {@link NullPointerException} otherwise.
+	 * @throws IOException
+	 *             It raises an {@link IOException} if a problem appears while
+	 *             parsing one on the attributes file.
+	 */
+	private static AttributesNode attributesNode(TreeWalk treeWalk,
+			@Nullable WorkingTreeIterator workingTreeIterator,
+			@Nullable DirCacheIterator dirCacheIterator,
+			@Nullable CanonicalTreeParser otherTree) throws IOException {
+		AttributesNode attributesNode = null;
+		switch (treeWalk.getOperationType()) {
+		case CHECKIN_OP:
+			if (workingTreeIterator != null) {
+				attributesNode = workingTreeIterator.getEntryAttributesNode();
+			}
+			if (attributesNode == null && dirCacheIterator != null) {
+				attributesNode = dirCacheIterator
+						.getEntryAttributesNode(treeWalk.getObjectReader());
+			}
+			if (attributesNode == null && otherTree != null) {
+				attributesNode = otherTree
+						.getEntryAttributesNode(treeWalk.getObjectReader());
+			}
+			break;
+		case CHECKOUT_OP:
+			if (otherTree != null) {
+				attributesNode = otherTree
+						.getEntryAttributesNode(treeWalk.getObjectReader());
+			}
+			if (attributesNode == null && dirCacheIterator != null) {
+				attributesNode = dirCacheIterator
+						.getEntryAttributesNode(treeWalk.getObjectReader());
+			}
+			if (attributesNode == null && workingTreeIterator != null) {
+				attributesNode = workingTreeIterator.getEntryAttributesNode();
+			}
+			break;
+		default:
+			throw new IllegalStateException(
+					"The only supported operation types are:" //$NON-NLS-1$
+							+ OperationType.CHECKIN_OP + "," //$NON-NLS-1$
+							+ OperationType.CHECKOUT_OP);
+		}
+
+		return attributesNode;
+	}
+
+	private static <T extends AbstractTreeIterator> T parentOf(@Nullable T node) {
+		if(node==null) return null;
+		@SuppressWarnings("unchecked")
+		Class<T> type = (Class<T>) node.getClass();
+		AbstractTreeIterator parent = node.parent;
+		if (type.isInstance(parent)) {
+			return type.cast(parent);
+		}
+		return null;
+	}
+
+	private static <T extends AbstractTreeIterator> T rootOf(
+			@Nullable T node) {
+		if(node==null) return null;
+		AbstractTreeIterator t=node;
+		while (t!= null && t.parent != null) {
+			t= t.parent;
+		}
+		@SuppressWarnings("unchecked")
+		Class<T> type = (Class<T>) node.getClass();
+		if (type.isInstance(t)) {
+			return type.cast(t);
+		}
+		return null;
+	}
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
index 5c0aba2..7196502 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
@@ -49,7 +49,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.ListIterator;
 
 import org.eclipse.jgit.lib.Constants;
 
@@ -122,40 +121,4 @@
 		return Collections.unmodifiableList(rules);
 	}
 
-	/**
-	 * Returns the matching attributes for an entry path.
-	 *
-	 * @param entryPath
-	 *            the path to test. The path must be relative to this attribute
-	 *            node's own repository path, and in repository path format
-	 *            (uses '/' and not '\').
-	 * @param isDirectory
-	 *            true if the target item is a directory.
-	 * @param attributes
-	 *            Map that will hold the attributes matching this entry path. If
-	 *            it is not empty, this method will NOT override any existing
-	 *            entry.
-	 * @since 4.2
-	 */
-	public void getAttributes(String entryPath,
-			boolean isDirectory, Attributes attributes) {
-		// Parse rules in the reverse order that they were read since the last
-		// entry should be used
-		ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules
-				.size());
-		while (ruleIterator.hasPrevious()) {
-			AttributesRule rule = ruleIterator.previous();
-			if (rule.isMatch(entryPath, isDirectory)) {
-				ListIterator<Attribute> attributeIte = rule.getAttributes()
-						.listIterator(rule.getAttributes().size());
-				// Parses the attributes in the reverse order that they were
-				// read since the last entry should be used
-				while (attributeIte.hasPrevious()) {
-					Attribute attr = attributeIte.previous();
-					if (!attributes.containsKey(attr.getKey()))
-						attributes.put(attr);
-				}
-			}
-		}
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
index 35d18c4..0532250 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -109,10 +109,11 @@
 	private final String pattern;
 	private final List<Attribute> attributes;
 
-	private boolean nameOnly;
-	private boolean dirOnly;
+	private final boolean nameOnly;
 
-	private IMatcher matcher;
+	private final boolean dirOnly;
+
+	private final IMatcher matcher;
 
 	/**
 	 * Create a new attribute rule with the given pattern. Assumes that the
@@ -128,38 +129,43 @@
 	 */
 	public AttributesRule(String pattern, String attributes) {
 		this.attributes = parseAttributes(attributes);
-		nameOnly = false;
-		dirOnly = false;
 
 		if (pattern.endsWith("/")) { //$NON-NLS-1$
 			pattern = pattern.substring(0, pattern.length() - 1);
 			dirOnly = true;
+		} else {
+			dirOnly = false;
 		}
 
-		boolean hasSlash = pattern.contains("/"); //$NON-NLS-1$
+		int slashIndex = pattern.indexOf('/');
 
-		if (!hasSlash)
+		if (slashIndex < 0) {
 			nameOnly = true;
-		else if (!pattern.startsWith("/")) { //$NON-NLS-1$
+		} else if (slashIndex == 0) {
+			nameOnly = false;
+		} else {
+			nameOnly = false;
 			// Contains "/" but does not start with one
 			// Adding / to the start should not interfere with matching
 			pattern = "/" + pattern; //$NON-NLS-1$
 		}
 
+		IMatcher candidateMatcher = NO_MATCH;
 		try {
-			matcher = PathMatcher.createPathMatcher(pattern,
+			candidateMatcher = PathMatcher.createPathMatcher(pattern,
 					Character.valueOf(FastIgnoreRule.PATH_SEPARATOR), dirOnly);
 		} catch (InvalidPatternException e) {
-			matcher = NO_MATCH;
+			// ignore: invalid patterns are silently ignored
 		}
-
+		this.matcher = candidateMatcher;
 		this.pattern = pattern;
 	}
 
 	/**
 	 * @return True if the pattern should match directories only
+	 * @since 4.3
 	 */
-	public boolean dirOnly() {
+	public boolean isDirOnly() {
 		return dirOnly;
 	}
 
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 a1e1d15..fc4cc90 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -62,6 +62,7 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -84,16 +85,43 @@
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.RawParseUtils;
 import org.eclipse.jgit.util.SystemReader;
-import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
+import org.eclipse.jgit.util.io.EolStreamTypeUtil;
 
 /**
  * This class handles checking out one or two trees merging with the index.
  */
 public class DirCacheCheckout {
 	private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
+
+	/**
+	 * Metadata used in checkout process
+	 *
+	 * @since 4.3
+	 */
+	public static class CheckoutMetadata {
+		/** git attributes */
+		public final EolStreamType eolStreamType;
+
+		/** filter command to apply */
+		public final String smudgeFilterCommand;
+
+		/**
+		 * @param eolStreamType
+		 * @param smudgeFilterCommand
+		 */
+		public CheckoutMetadata(EolStreamType eolStreamType,
+				String smudgeFilterCommand) {
+			this.eolStreamType = eolStreamType;
+			this.smudgeFilterCommand = smudgeFilterCommand;
+		}
+
+		static CheckoutMetadata EMPTY = new CheckoutMetadata(
+				EolStreamType.DIRECT, null);
+	}
+
 	private Repository repo;
 
-	private HashMap<String, String> updated = new HashMap<String, String>();
+	private HashMap<String, CheckoutMetadata> updated = new HashMap<String, CheckoutMetadata>();
 
 	private ArrayList<String> conflicts = new ArrayList<String>();
 
@@ -120,7 +148,7 @@
 	/**
 	 * @return a list of updated paths and smudgeFilterCommands
 	 */
-	public Map<String, String> getUpdated() {
+	public Map<String, CheckoutMetadata> getUpdated() {
 		return updated;
 	}
 
@@ -252,8 +280,9 @@
 
 		addTree(walk, headCommitTree);
 		addTree(walk, mergeCommitTree);
-		walk.addTree(new DirCacheBuildIterator(builder));
+		int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
 		walk.addTree(workingTree);
+		workingTree.setDirCacheIterator(walk, dciPos);
 
 		while (walk.next()) {
 			processEntry(walk.getTree(0, CanonicalTreeParser.class),
@@ -292,8 +321,9 @@
 
 		walk = new NameConflictTreeWalk(repo);
 		addTree(walk, mergeCommitTree);
-		walk.addTree(new DirCacheBuildIterator(builder));
+		int dciPos = walk.addTree(new DirCacheBuildIterator(builder));
 		walk.addTree(workingTree);
+		workingTree.setDirCacheIterator(walk, dciPos);
 
 		while (walk.next()) {
 			processEntry(walk.getTree(0, CanonicalTreeParser.class),
@@ -450,11 +480,12 @@
 			if (file != null)
 				removeEmptyParents(file);
 
-			for (String path : updated.keySet()) {
+			for (Map.Entry<String, CheckoutMetadata> e : updated.entrySet()) {
+				String path = e.getKey();
+				CheckoutMetadata meta = e.getValue();
 				DirCacheEntry entry = dc.getEntry(path);
 				if (!FileMode.GITLINK.equals(entry.getRawMode()))
-					checkoutEntry(repo, entry, objectReader, false,
-							updated.get(path));
+					checkoutEntry(repo, entry, objectReader, false, meta);
 			}
 
 			// commit the index builder - a new index is persisted
@@ -1006,8 +1037,8 @@
 	private void update(String path, ObjectId mId, FileMode mode)
 			throws IOException {
 		if (!FileMode.TREE.equals(mode)) {
-			updated.put(path,
-					walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE));
+			updated.put(path, new CheckoutMetadata(walk.getEolStreamType(),
+					walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)));
 
 			DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
 			entry.setObjectId(mId);
@@ -1064,8 +1095,10 @@
 	private boolean isModifiedSubtree_IndexWorkingtree(String path)
 			throws CorruptObjectException, IOException {
 		try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
-			tw.addTree(new DirCacheIterator(dc));
-			tw.addTree(new FileTreeIterator(repo));
+			int dciPos = tw.addTree(new DirCacheIterator(dc));
+			FileTreeIterator fti = new FileTreeIterator(repo);
+			tw.addTree(fti);
+			fti.setDirCacheIterator(tw, dciPos);
 			tw.setRecursive(true);
 			tw.setFilter(PathFilter.create(path));
 			DirCacheIterator dcIt;
@@ -1190,52 +1223,22 @@
 	 * @param deleteRecursive
 	 *            true to recursively delete final path if it exists on the file
 	 *            system
-	 *
-	 * @throws IOException
-	 * @since 4.2
-	 */
-	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
-			ObjectReader or, boolean deleteRecursive) throws IOException {
-		checkoutEntry(repo, entry, or, deleteRecursive, null);
-	}
-
-	/**
-	 * Updates the file in the working tree with content and mode from an entry
-	 * in the index. The new content is first written to a new temporary file in
-	 * the same directory as the real file. Then that new file is renamed to the
-	 * final filename.
-	 *
-	 * <p>
-	 * <b>Note:</b> if the entry path on local file system exists as a file, it
-	 * will be deleted and if it exists as a directory, it will be deleted
-	 * recursively, independently if has any content.
-	 * </p>
-	 *
-	 * <p>
-	 * TODO: this method works directly on File IO, we may need another
-	 * abstraction (like WorkingTreeIterator). This way we could tell e.g.
-	 * Eclipse that Files in the workspace got changed
-	 * </p>
-	 *
-	 * @param repo
-	 *            repository managing the destination work tree.
-	 * @param entry
-	 *            the entry containing new mode and content
-	 * @param or
-	 *            object reader to use for checkout
-	 * @param deleteRecursive
-	 *            true to recursively delete final path if it exists on the file
-	 *            system
-	 * @param smudgeFilterCommand
-	 *            the filter command to be run for smudging the entry to be
-	 *            checked out
+	 * @param checkoutMetadata
+	 *            containing
+	 *            <ul>
+	 *            <li>smudgeFilterCommand to be run for smudging the entry to be
+	 *            checked out</li>
+	 *            <li>eolStreamType used for stream conversion</li>
+	 *            </ul>
 	 *
 	 * @throws IOException
 	 * @since 4.2
 	 */
 	public static void checkoutEntry(Repository repo, DirCacheEntry entry,
 			ObjectReader or, boolean deleteRecursive,
-			String smudgeFilterCommand) throws IOException {
+			CheckoutMetadata checkoutMetadata) throws IOException {
+		if (checkoutMetadata == null)
+			checkoutMetadata = CheckoutMetadata.EMPTY;
 		ObjectLoader ol = or.open(entry.getObjectId());
 		File f = new File(repo.getWorkTree(), entry.getPathString());
 		File parentDir = f.getParentFile();
@@ -1257,12 +1260,19 @@
 
 		File tmpFile = File.createTempFile(
 				"._" + f.getName(), null, parentDir); //$NON-NLS-1$
-		OutputStream channel = new FileOutputStream(tmpFile);
-		if (opt.getAutoCRLF() == AutoCRLF.TRUE)
-			channel = new AutoCRLFOutputStream(channel);
-		if (smudgeFilterCommand != null) {
-			ProcessBuilder filterProcessBuilder = fs
-					.runInShell(smudgeFilterCommand, new String[0]);
+		EolStreamType nonNullEolStreamType;
+		if (checkoutMetadata.eolStreamType != null) {
+			nonNullEolStreamType = checkoutMetadata.eolStreamType;
+		} else if (opt.getAutoCRLF() == AutoCRLF.TRUE) {
+			nonNullEolStreamType = EolStreamType.AUTO_CRLF;
+		} else {
+			nonNullEolStreamType = EolStreamType.DIRECT;
+		}
+		OutputStream channel = EolStreamTypeUtil.wrapOutputStream(
+				new FileOutputStream(tmpFile), nonNullEolStreamType);
+		if (checkoutMetadata.smudgeFilterCommand != null) {
+			ProcessBuilder filterProcessBuilder = fs.runInShell(
+					checkoutMetadata.smudgeFilterCommand, new String[0]);
 			filterProcessBuilder.directory(repo.getWorkTree());
 			filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
 					repo.getDirectory().getAbsolutePath());
@@ -1278,14 +1288,16 @@
 				}
 			} catch (IOException | InterruptedException e) {
 				throw new IOException(new FilterFailedException(e,
-						smudgeFilterCommand, entry.getPathString()));
+						checkoutMetadata.smudgeFilterCommand,
+						entry.getPathString()));
 
 			} finally {
 				channel.close();
 			}
 			if (rc != 0) {
 				throw new IOException(new FilterFailedException(rc,
-						smudgeFilterCommand, entry.getPathString(),
+						checkoutMetadata.smudgeFilterCommand,
+						entry.getPathString(),
 						result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
 						RawParseUtils.decode(result.getStderr()
 								.toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
@@ -1301,10 +1313,11 @@
 		// was filtered (either by autocrlf handling or smudge filters) ask the
 		// filesystem again for the length. Otherwise the objectloader knows the
 		// size
-		if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) {
-			entry.setLength(tmpFile.length());
-		} else {
+		if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT
+				&& checkoutMetadata.smudgeFilterCommand == null) {
 			entry.setLength(ol.getSize());
+		} else {
+			entry.setLength(tmpFile.length());
 		}
 
 		if (opt.isFileMode() && fs.supportsExecute()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index 0cbb83d..8bcf4bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -249,7 +249,15 @@
 		return children[i];
 	}
 
-	ObjectId getObjectId() {
+	/**
+	 * Get the tree's ObjectId.
+	 * <p>
+	 * If {@link #isValid()} returns false this method will return null.
+	 *
+	 * @return ObjectId of this tree or null.
+	 * @since 4.3
+	 */
+	public ObjectId getObjectId() {
 		return id;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 7eb9550..796b422 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -80,7 +80,7 @@
 	private final String baseUrl;
 	private final String defaultBranch;
 	private final Repository rootRepo;
-	private final Map<String, String> remotes;
+	private final Map<String, Remote> remotes;
 	private final Set<String> plusGroups;
 	private final Set<String> minusGroups;
 	private final List<RepoProject> projects;
@@ -146,7 +146,7 @@
 			}
 		}
 
-		remotes = new HashMap<String, String>();
+		remotes = new HashMap<String, Remote>();
 		projects = new ArrayList<RepoProject>();
 		filteredProjects = new ArrayList<RepoProject>();
 	}
@@ -195,14 +195,14 @@
 		} else if ("remote".equals(qName)) { //$NON-NLS-1$
 			String alias = attributes.getValue("alias"); //$NON-NLS-1$
 			String fetch = attributes.getValue("fetch"); //$NON-NLS-1$
-			remotes.put(attributes.getValue("name"), fetch); //$NON-NLS-1$
+			String revision = attributes.getValue("revision"); //$NON-NLS-1$
+			Remote remote = new Remote(fetch, revision);
+			remotes.put(attributes.getValue("name"), remote); //$NON-NLS-1$
 			if (alias != null)
-				remotes.put(alias, fetch);
+				remotes.put(alias, remote);
 		} else if ("default".equals(qName)) { //$NON-NLS-1$
 			defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
 			defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
-			if (defaultRevision == null)
-				defaultRevision = defaultBranch;
 		} else if ("copyfile".equals(qName)) { //$NON-NLS-1$
 			if (currentProject == null)
 				throw new SAXException(RepoText.get().invalidManifest);
@@ -268,8 +268,18 @@
 		} catch (URISyntaxException e) {
 			throw new SAXException(e);
 		}
+		if (defaultRevision == null && defaultRemote != null) {
+			Remote remote = remotes.get(defaultRemote);
+			if (remote != null) {
+				defaultRevision = remote.revision;
+			}
+			if (defaultRevision == null) {
+				defaultRevision = defaultBranch;
+			}
+		}
 		for (RepoProject proj : projects) {
 			String remote = proj.getRemote();
+			String revision = defaultRevision;
 			if (remote == null) {
 				if (defaultRemote == null) {
 					if (filename != null)
@@ -281,16 +291,22 @@
 								RepoText.get().errorNoDefault);
 				}
 				remote = defaultRemote;
+			} else {
+				Remote r = remotes.get(remote);
+				if (r != null && r.revision != null) {
+					revision = r.revision;
+				}
 			}
 			String remoteUrl = remoteUrls.get(remote);
 			if (remoteUrl == null) {
-				remoteUrl = baseUri.resolve(remotes.get(remote)).toString();
+				remoteUrl =
+						baseUri.resolve(remotes.get(remote).fetch).toString();
 				if (!remoteUrl.endsWith("/")) //$NON-NLS-1$
 					remoteUrl = remoteUrl + "/"; //$NON-NLS-1$
 				remoteUrls.put(remote, remoteUrl);
 			}
 			proj.setUrl(remoteUrl + proj.getName())
-					.setDefaultRevision(defaultRevision);
+					.setDefaultRevision(revision);
 		}
 
 		filteredProjects.addAll(projects);
@@ -389,4 +405,14 @@
 		}
 		return false;
 	}
+
+	private static class Remote {
+		final String fetch;
+		final String revision;
+
+		Remote(String fetch, String revision) {
+			this.fetch = fetch;
+			this.revision = revision;
+		}
+	}
 }
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 ff9f233..ee937f5 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.List;
 import java.util.Map;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.GitCommand;
 import org.eclipse.jgit.api.SubmoduleAddCommand;
@@ -103,7 +104,6 @@
  * @since 3.4
  */
 public class RepoCommand extends GitCommand<RevCommit> {
-
 	private String path;
 	private String uri;
 	private String groups;
@@ -114,6 +114,7 @@
 	private RemoteReader callback;
 	private InputStream inputStream;
 	private IncludedFileReader includedReader;
+	private boolean ignoreRemoteFailures = false;
 
 	private List<RepoProject> bareProjects;
 	private Git git;
@@ -137,9 +138,11 @@
 		 *            The URI of the remote repository
 		 * @param ref
 		 *            The ref (branch/tag/etc.) to read
-		 * @return the sha1 of the remote repository
+		 * @return the sha1 of the remote repository, or null if the ref does
+		 *         not exist.
 		 * @throws GitAPIException
 		 */
+		@Nullable
 		public ObjectId sha1(String uri, String ref) throws GitAPIException;
 
 		/**
@@ -318,7 +321,7 @@
 	}
 
 	/**
-	 * Set whether the branch name should be recorded in .gitmodules
+	 * Set whether the branch name should be recorded in .gitmodules.
 	 * <p>
 	 * Submodule entries in .gitmodules can include a "branch" field
 	 * to indicate what remote branch each submodule tracks.
@@ -355,6 +358,26 @@
 	}
 
 	/**
+	 * Set whether to skip projects whose commits don't exist remotely.
+	 * <p>
+	 * When set to true, we'll just skip the manifest entry and continue
+	 * on to the next one.
+	 * <p>
+	 * When set to false (default), we'll throw an error when remote
+	 * failures occur.
+	 * <p>
+	 * Not implemented for non-bare repositories.
+	 *
+	 * @param ignore Whether to ignore the remote failures.
+	 * @return this command
+	 * @since 4.3
+	 */
+	public RepoCommand setIgnoreRemoteFailures(boolean ignore) {
+		this.ignoreRemoteFailures = ignore;
+		return this;
+	}
+
+	/**
 	 * Set the author/committer for the bare repository commit.
 	 * <p>
 	 * For non-bare repositories, the current user will be used and this will be
@@ -452,22 +475,29 @@
 				for (RepoProject proj : bareProjects) {
 					String name = proj.getPath();
 					String nameUri = proj.getName();
-					cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
-					cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
-					// create gitlink
-					DirCacheEntry dcEntry = new DirCacheEntry(name);
 					ObjectId objectId;
-					if (ObjectId.isId(proj.getRevision())) {
+					if (ObjectId.isId(proj.getRevision())
+							&& !ignoreRemoteFailures) {
 						objectId = ObjectId.fromString(proj.getRevision());
 					} else {
 						objectId = callback.sha1(nameUri, proj.getRevision());
-						if (recordRemoteBranch)
+						if (objectId == null) {
+							if (ignoreRemoteFailures) {
+								continue;
+							}
+							throw new RemoteUnavailableException(nameUri);
+						}
+						if (recordRemoteBranch) {
 							// can be branch or tag
 							cfg.setString("submodule", name, "branch", //$NON-NLS-1$ //$NON-NLS-2$
 									proj.getRevision());
+						}
 					}
-					if (objectId == null)
-						throw new RemoteUnavailableException(nameUri);
+					cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
+					cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
+
+					// create gitlink
+					DirCacheEntry dcEntry = new DirCacheEntry(name);
 					dcEntry.setObjectId(objectId);
 					dcEntry.setFileMode(FileMode.GITLINK);
 					builder.add(dcEntry);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java
new file mode 100644
index 0000000..014eab2
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchConstants.TERM;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.TreeFormatter;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The initial {@link Round} for a leaderless repository, used to establish a
+ * leader.
+ */
+class ElectionRound extends Round {
+	private static final Logger log = LoggerFactory.getLogger(ElectionRound.class);
+
+	private long term;
+
+	ElectionRound(KetchLeader leader, LogIndex head) {
+		super(leader, head);
+	}
+
+	@Override
+	void start() throws IOException {
+		ObjectId id;
+		try (Repository git = leader.openRepository();
+				ObjectInserter inserter = git.newObjectInserter()) {
+			id = bumpTerm(git, inserter);
+			inserter.flush();
+		}
+		runAsync(id);
+	}
+
+	@Override
+	void success() {
+		// Do nothing upon election, KetchLeader will copy the term.
+	}
+
+	long getTerm() {
+		return term;
+	}
+
+	private ObjectId bumpTerm(Repository git, ObjectInserter inserter)
+			throws IOException {
+		CommitBuilder b = new CommitBuilder();
+		if (!ObjectId.zeroId().equals(acceptedOldIndex)) {
+			try (RevWalk rw = new RevWalk(git)) {
+				RevCommit c = rw.parseCommit(acceptedOldIndex);
+				b.setTreeId(c.getTree());
+				b.setParentId(acceptedOldIndex);
+				term = parseTerm(c.getFooterLines(TERM)) + 1;
+			}
+		} else {
+			term = 1;
+			b.setTreeId(inserter.insert(new TreeFormatter()));
+		}
+
+		StringBuilder msg = new StringBuilder();
+		msg.append(KetchConstants.TERM.getName())
+				.append(": ") //$NON-NLS-1$
+				.append(term);
+
+		String tag = leader.getSystem().newLeaderTag();
+		if (tag != null && !tag.isEmpty()) {
+			msg.append(' ').append(tag);
+		}
+
+		b.setAuthor(leader.getSystem().newCommitter());
+		b.setCommitter(b.getAuthor());
+		b.setMessage(msg.toString());
+
+		if (log.isDebugEnabled()) {
+			log.debug("Trying to elect myself " + b.getMessage()); //$NON-NLS-1$
+		}
+		return inserter.insert(b);
+	}
+
+	private static long parseTerm(List<String> footer) {
+		if (footer.isEmpty()) {
+			return 0;
+		}
+
+		String s = footer.get(0);
+		int p = s.indexOf(' ');
+		if (p > 0) {
+			s = s.substring(0, p);
+		}
+		return Long.parseLong(s, 10);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java
new file mode 100644
index 0000000..171c059
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchConstants.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import org.eclipse.jgit.revwalk.FooterKey;
+
+/** Frequently used constants in a Ketch system. */
+public class KetchConstants {
+	/**
+	 * Default reference namespace holding {@link #ACCEPTED} and
+	 * {@link #COMMITTED} references and the {@link #STAGE} sub-namespace.
+	 */
+	public static final String DEFAULT_TXN_NAMESPACE = "refs/txn/"; //$NON-NLS-1$
+
+	/** Reference name holding the RefTree accepted by a follower. */
+	public static final String ACCEPTED = "accepted"; //$NON-NLS-1$
+
+	/** Reference name holding the RefTree known to be committed. */
+	public static final String COMMITTED = "committed"; //$NON-NLS-1$
+
+	/** Reference subdirectory holding proposed heads. */
+	public static final String STAGE = "stage/"; //$NON-NLS-1$
+
+	/** Footer containing the current term. */
+	public static final FooterKey TERM = new FooterKey("Term"); //$NON-NLS-1$
+
+	/** Section for Ketch configuration ({@code ketch}). */
+	public static final String CONFIG_SECTION_KETCH = "ketch"; //$NON-NLS-1$
+
+	/** Behavior for a replica ({@code remote.$name.ketch-type}) */
+	public static final String CONFIG_KEY_TYPE = "ketch-type"; //$NON-NLS-1$
+
+	/** Behavior for a replica ({@code remote.$name.ketch-commit}) */
+	public static final String CONFIG_KEY_COMMIT = "ketch-commit"; //$NON-NLS-1$
+
+	/** Behavior for a replica ({@code remote.$name.ketch-speed}) */
+	public static final String CONFIG_KEY_SPEED = "ketch-speed"; //$NON-NLS-1$
+
+	private KetchConstants() {
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
new file mode 100644
index 0000000..3bcd6bc
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeader.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchLeader.State.CANDIDATE;
+import static org.eclipse.jgit.internal.ketch.KetchLeader.State.LEADER;
+import static org.eclipse.jgit.internal.ketch.KetchLeader.State.SHUTDOWN;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.Participation.FOLLOWER_ONLY;
+import static org.eclipse.jgit.internal.ketch.Proposal.State.QUEUED;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.jgit.internal.storage.reftree.RefTree;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A leader managing consensus across remote followers.
+ * <p>
+ * A leader instance starts up in {@link State#CANDIDATE} and tries to begin a
+ * new term by sending an {@link ElectionRound} to all replicas. Its term starts
+ * if a majority of replicas have accepted this leader instance for the term.
+ * <p>
+ * Once elected by a majority the instance enters {@link State#LEADER} and runs
+ * proposals offered to {@link #queueProposal(Proposal)}. This continues until
+ * the leader is timed out for inactivity, or is deposed by a competing leader
+ * gaining its own majority.
+ * <p>
+ * Once timed out or deposed this {@code KetchLeader} instance should be
+ * discarded, and a new instance takes over.
+ * <p>
+ * Each leader instance coordinates a group of {@link KetchReplica}s. Replica
+ * instances are owned by the leader instance and must be discarded when the
+ * leader is discarded.
+ * <p>
+ * In Ketch all push requests are issued through the leader. The steps are as
+ * follows (see {@link KetchPreReceive} for an example):
+ * <ul>
+ * <li>Create a {@link Proposal} with the
+ * {@link org.eclipse.jgit.transport.ReceiveCommand}s that represent the push.
+ * <li>Invoke {@link #queueProposal(Proposal)} on the leader instance.
+ * <li>Wait for consensus with {@link Proposal#await()}.
+ * <li>To examine the status of the push, check {@link Proposal#getCommands()},
+ * looking at
+ * {@link org.eclipse.jgit.internal.storage.reftree.Command#getResult()}.
+ * </ul>
+ * <p>
+ * The leader gains consensus by first pushing the needed objects and a
+ * {@link RefTree} representing the desired target repository state to the
+ * {@code refs/txn/accepted} branch on each of the replicas. Once a majority has
+ * succeeded, the leader commits the state by either pushing the
+ * {@code refs/txn/accepted} value to {@code refs/txn/committed} (for
+ * Ketch-aware replicas) or by pushing updates to {@code refs/heads/master},
+ * etc. for stock Git replicas.
+ * <p>
+ * Internally, the actual transport to replicas is performed on background
+ * threads via the {@link KetchSystem}'s executor service. For performance, the
+ * {@link KetchLeader}, {@link KetchReplica} and {@link Proposal} objects share
+ * some state, and may invoke each other's methods on different threads. This
+ * access is protected by the leader's {@link #lock} object. Care must be taken
+ * to prevent concurrent access by correctly obtaining the leader's lock.
+ */
+public abstract class KetchLeader {
+	private static final Logger log = LoggerFactory.getLogger(KetchLeader.class);
+
+	/** Current state of the leader instance. */
+	public static enum State {
+		/** Newly created instance trying to elect itself leader. */
+		CANDIDATE,
+
+		/** Leader instance elected by a majority. */
+		LEADER,
+
+		/** Instance has been deposed by another with a more recent term. */
+		DEPOSED,
+
+		/** Leader has been gracefully shutdown, e.g. due to inactivity. */
+		SHUTDOWN;
+	}
+
+	private final KetchSystem system;
+
+	/** Leader's knowledge of replicas for this repository. */
+	private KetchReplica[] voters;
+	private KetchReplica[] followers;
+	private LocalReplica self;
+
+	/**
+	 * Lock protecting all data within this leader instance.
+	 * <p>
+	 * This lock extends into the {@link KetchReplica} instances used by the
+	 * leader. They share the same lock instance to simplify concurrency.
+	 */
+	final Lock lock;
+
+	private State state = CANDIDATE;
+
+	/** Term of this leader, once elected. */
+	private long term;
+
+	/**
+	 * Pending proposals accepted into the queue in FIFO order.
+	 * <p>
+	 * These proposals were preflighted and do not contain any conflicts with
+	 * each other and their expectations matched the leader's local view of the
+	 * agreed upon {@code refs/txn/accepted} tree.
+	 */
+	private final List<Proposal> queued;
+
+	/**
+	 * State of the repository's RefTree after applying all entries in
+	 * {@link #queued}. New proposals must be consistent with this tree to be
+	 * appended to the end of {@link #queued}.
+	 * <p>
+	 * Must be deep-copied with {@link RefTree#copy()} if
+	 * {@link #roundHoldsReferenceToRefTree} is {@code true}.
+	 */
+	private RefTree refTree;
+
+	/**
+	 * If {@code true} {@link #refTree} must be duplicated before queuing the
+	 * next proposal. The {@link #refTree} was passed into the constructor of a
+	 * {@link ProposalRound}, and that external reference to the {@link RefTree}
+	 * object is held by the proposal until it materializes the tree object in
+	 * the object store. This field is set {@code true} when the proposal begins
+	 * execution and set {@code false} once tree objects are persisted in the
+	 * local repository's object store or {@link #refTree} is replaced with a
+	 * copy to isolate it from any running rounds.
+	 * <p>
+	 * If proposals arrive less frequently than the {@code RefTree} is written
+	 * out to the repository the {@link #roundHoldsReferenceToRefTree} behavior
+	 * avoids duplicating {@link #refTree}, reducing both time and memory used.
+	 * However if proposals arrive more frequently {@link #refTree} must be
+	 * duplicated to prevent newly queued proposals from corrupting the
+	 * {@link #runningRound}.
+	 */
+	volatile boolean roundHoldsReferenceToRefTree;
+
+	/** End of the leader's log. */
+	private LogIndex headIndex;
+
+	/** Leader knows this (and all prior) states are committed. */
+	private LogIndex committedIndex;
+
+	/**
+	 * Is the leader idle with no work pending? If {@code true} there is no work
+	 * for the leader (normal state). This field is {@code false} when the
+	 * leader thread is scheduled for execution, or while {@link #runningRound}
+	 * defines a round in progress.
+	 */
+	private boolean idle;
+
+	/** Current round the leader is preparing and waiting for a vote on. */
+	private Round runningRound;
+
+	/**
+	 * Construct a leader for a Ketch instance.
+	 *
+	 * @param system
+	 *            Ketch system configuration the leader must adhere to.
+	 */
+	protected KetchLeader(KetchSystem system) {
+		this.system = system;
+		this.lock = new ReentrantLock(true /* fair */);
+		this.queued = new ArrayList<>(4);
+		this.idle = true;
+	}
+
+	/** @return system configuration. */
+	KetchSystem getSystem() {
+		return system;
+	}
+
+	/**
+	 * Configure the replicas used by this Ketch instance.
+	 * <p>
+	 * Replicas should be configured once at creation before any proposals are
+	 * executed. Once elections happen, <b>reconfiguration is a complicated
+	 * concept that is not currently supported</b>.
+	 *
+	 * @param replicas
+	 *            members participating with the same repository.
+	 */
+	public void setReplicas(Collection<KetchReplica> replicas) {
+		List<KetchReplica> v = new ArrayList<>(5);
+		List<KetchReplica> f = new ArrayList<>(5);
+		for (KetchReplica r : replicas) {
+			switch (r.getParticipation()) {
+			case FULL:
+				v.add(r);
+				break;
+
+			case FOLLOWER_ONLY:
+				f.add(r);
+				break;
+			}
+		}
+
+		Collection<Integer> validVoters = validVoterCounts();
+		if (!validVoters.contains(Integer.valueOf(v.size()))) {
+			throw new IllegalArgumentException(MessageFormat.format(
+					KetchText.get().unsupportedVoterCount,
+					Integer.valueOf(v.size()),
+					validVoters));
+		}
+
+		LocalReplica me = findLocal(v);
+		if (me == null) {
+			throw new IllegalArgumentException(
+					KetchText.get().localReplicaRequired);
+		}
+
+		lock.lock();
+		try {
+			voters = v.toArray(new KetchReplica[v.size()]);
+			followers = f.toArray(new KetchReplica[f.size()]);
+			self = me;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	private static Collection<Integer> validVoterCounts() {
+		@SuppressWarnings("boxing")
+		Integer[] valid = {
+				// An odd number of voting replicas is required.
+				1, 3, 5, 7, 9 };
+		return Arrays.asList(valid);
+	}
+
+	private static LocalReplica findLocal(Collection<KetchReplica> voters) {
+		for (KetchReplica r : voters) {
+			if (r instanceof LocalReplica) {
+				return (LocalReplica) r;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Get an instance of the repository for use by a leader thread.
+	 * <p>
+	 * The caller will close the repository.
+	 *
+	 * @return opened repository for use by the leader thread.
+	 * @throws IOException
+	 *             cannot reopen the repository for the leader.
+	 */
+	protected abstract Repository openRepository() throws IOException;
+
+	/**
+	 * Queue a reference update proposal for consensus.
+	 * <p>
+	 * This method does not wait for consensus to be reached. The proposal is
+	 * checked to look for risks of conflicts, and then submitted into the queue
+	 * for distribution as soon as possible.
+	 * <p>
+	 * Callers must use {@link Proposal#await()} to see if the proposal is done.
+	 *
+	 * @param proposal
+	 *            the proposed reference updates to queue for consideration.
+	 *            Once execution is complete the individual reference result
+	 *            fields will be populated with the outcome.
+	 * @throws InterruptedException
+	 *             current thread was interrupted. The proposal may have been
+	 *             aborted if it was not yet queued for execution.
+	 * @throws IOException
+	 *             unrecoverable error preventing proposals from being attempted
+	 *             by this leader.
+	 */
+	public void queueProposal(Proposal proposal)
+			throws InterruptedException, IOException {
+		try {
+			lock.lockInterruptibly();
+		} catch (InterruptedException e) {
+			proposal.abort();
+			throw e;
+		}
+		try {
+			if (refTree == null) {
+				initialize();
+				for (Proposal p : queued) {
+					refTree.apply(p.getCommands());
+				}
+			} else if (roundHoldsReferenceToRefTree) {
+				refTree = refTree.copy();
+				roundHoldsReferenceToRefTree = false;
+			}
+
+			if (!refTree.apply(proposal.getCommands())) {
+				// A conflict exists so abort the proposal.
+				proposal.abort();
+				return;
+			}
+
+			queued.add(proposal);
+			proposal.notifyState(QUEUED);
+
+			if (idle) {
+				scheduleLeader();
+			}
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	private void initialize() throws IOException {
+		try (Repository git = openRepository(); RevWalk rw = new RevWalk(git)) {
+			self.initialize(git);
+
+			ObjectId accepted = self.getTxnAccepted();
+			if (!ObjectId.zeroId().equals(accepted)) {
+				RevCommit c = rw.parseCommit(accepted);
+				headIndex = LogIndex.unknown(accepted);
+				refTree = RefTree.read(rw.getObjectReader(), c.getTree());
+			} else {
+				headIndex = LogIndex.unknown(ObjectId.zeroId());
+				refTree = RefTree.newEmptyTree();
+			}
+		}
+	}
+
+	private void scheduleLeader() {
+		idle = false;
+		system.getExecutor().execute(new Runnable() {
+			@Override
+			public void run() {
+				runLeader();
+			}
+		});
+	}
+
+	private void runLeader() {
+		Round round;
+		lock.lock();
+		try {
+			switch (state) {
+			case CANDIDATE:
+				round = new ElectionRound(this, headIndex);
+				break;
+
+			case LEADER:
+				round = newProposalRound();
+				break;
+
+			case DEPOSED:
+			case SHUTDOWN:
+			default:
+				log.warn("Leader cannot run {}", state); //$NON-NLS-1$
+				// TODO(sop): Redirect proposals.
+				return;
+			}
+		} finally {
+			lock.unlock();
+		}
+
+		try {
+			round.start();
+		} catch (IOException e) {
+			// TODO(sop) Depose leader if it cannot use its repository.
+			log.error(KetchText.get().leaderFailedToStore, e);
+			lock.lock();
+			try {
+				nextRound();
+			} finally {
+				lock.unlock();
+			}
+		}
+	}
+
+	private ProposalRound newProposalRound() {
+		List<Proposal> todo = new ArrayList<>(queued);
+		queued.clear();
+		roundHoldsReferenceToRefTree = true;
+		return new ProposalRound(this, headIndex, todo, refTree);
+	}
+
+	/** @return term of this leader's reign. */
+	long getTerm() {
+		return term;
+	}
+
+	/** @return end of the leader's log. */
+	LogIndex getHead() {
+		return headIndex;
+	}
+
+	/**
+	 * @return state leader knows it has committed across a quorum of replicas.
+	 */
+	LogIndex getCommitted() {
+		return committedIndex;
+	}
+
+	boolean isIdle() {
+		return idle;
+	}
+
+	void runAsync(Round round) {
+		lock.lock();
+		try {
+			// End of the log is this round. Once transport begins it is
+			// reasonable to assume at least one replica will eventually get
+			// this, and there is reasonable probability it commits.
+			headIndex = round.acceptedNewIndex;
+			runningRound = round;
+
+			for (KetchReplica replica : voters) {
+				replica.pushTxnAcceptedAsync(round);
+			}
+			for (KetchReplica replica : followers) {
+				replica.pushTxnAcceptedAsync(round);
+			}
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/**
+	 * Asynchronous signal from a replica after completion.
+	 * <p>
+	 * Must be called while {@link #lock} is held by the replica.
+	 *
+	 * @param replica
+	 *            replica posting a completion event.
+	 */
+	void onReplicaUpdate(KetchReplica replica) {
+		if (log.isDebugEnabled()) {
+			log.debug("Replica {} finished:\n{}", //$NON-NLS-1$
+					replica.describeForLog(), snapshot());
+		}
+
+		if (replica.getParticipation() == FOLLOWER_ONLY) {
+			// Followers cannot vote, so votes haven't changed.
+			return;
+		} else if (runningRound == null) {
+			// No round running, no need to tally votes.
+			return;
+		}
+
+		assert headIndex.equals(runningRound.acceptedNewIndex);
+		int matching = 0;
+		for (KetchReplica r : voters) {
+			if (r.hasAccepted(headIndex)) {
+				matching++;
+			}
+		}
+
+		int quorum = voters.length / 2 + 1;
+		boolean success = matching >= quorum;
+		if (!success) {
+			return;
+		}
+
+		switch (state) {
+		case CANDIDATE:
+			term = ((ElectionRound) runningRound).getTerm();
+			state = LEADER;
+			if (log.isDebugEnabled()) {
+				log.debug("Won election, running term " + term); //$NON-NLS-1$
+			}
+
+			//$FALL-THROUGH$
+		case LEADER:
+			committedIndex = headIndex;
+			if (log.isDebugEnabled()) {
+				log.debug("Committed {} in term {}", //$NON-NLS-1$
+						committedIndex.describeForLog(),
+						Long.valueOf(term));
+			}
+			nextRound();
+			commitAsync(replica);
+			notifySuccess(runningRound);
+			if (log.isDebugEnabled()) {
+				log.debug("Leader state:\n{}", snapshot()); //$NON-NLS-1$
+			}
+			break;
+
+		default:
+			log.debug("Leader ignoring replica while in {}", state); //$NON-NLS-1$
+			break;
+		}
+	}
+
+	private void notifySuccess(Round round) {
+		// Drop the leader lock while notifying Proposal listeners.
+		lock.unlock();
+		try {
+			round.success();
+		} finally {
+			lock.lock();
+		}
+	}
+
+	private void commitAsync(KetchReplica caller) {
+		for (KetchReplica r : voters) {
+			if (r == caller) {
+				continue;
+			}
+			if (r.shouldPushUnbatchedCommit(committedIndex, isIdle())) {
+				r.pushCommitAsync(committedIndex);
+			}
+		}
+		for (KetchReplica r : followers) {
+			if (r == caller) {
+				continue;
+			}
+			if (r.shouldPushUnbatchedCommit(committedIndex, isIdle())) {
+				r.pushCommitAsync(committedIndex);
+			}
+		}
+	}
+
+	/** Schedule the next round; invoked while {@link #lock} is held. */
+	void nextRound() {
+		runningRound = null;
+
+		if (queued.isEmpty()) {
+			idle = true;
+		} else {
+			// Caller holds lock. Reschedule leader on a new thread so
+			// the call stack can unwind and lock is not held unexpectedly
+			// during prepare for the next round.
+			scheduleLeader();
+		}
+	}
+
+	/** @return snapshot this leader. */
+	public LeaderSnapshot snapshot() {
+		lock.lock();
+		try {
+			LeaderSnapshot s = new LeaderSnapshot();
+			s.state = state;
+			s.term = term;
+			s.headIndex = headIndex;
+			s.committedIndex = committedIndex;
+			s.idle = isIdle();
+			for (KetchReplica r : voters) {
+				s.replicas.add(r.snapshot());
+			}
+			for (KetchReplica r : followers) {
+				s.replicas.add(r.snapshot());
+			}
+			return s;
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	/** Gracefully shutdown this leader and cancel outstanding operations. */
+	public void shutdown() {
+		lock.lock();
+		try {
+			if (state != SHUTDOWN) {
+				state = SHUTDOWN;
+				for (KetchReplica r : voters) {
+					r.shutdown();
+				}
+				for (KetchReplica r : followers) {
+					r.shutdown();
+				}
+			}
+		} finally {
+			lock.unlock();
+		}
+	}
+
+	@Override
+	public String toString() {
+		return snapshot().toString();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java
new file mode 100644
index 0000000..ba033c1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchLeaderCache.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import java.net.URISyntaxException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * A cache of live leader instances, keyed by repository.
+ * <p>
+ * Ketch only assigns a leader to a repository when needed. If
+ * {@link #get(Repository)} is called for a repository that does not have a
+ * leader, the leader is created and added to the cache.
+ */
+public class KetchLeaderCache {
+	private final KetchSystem system;
+	private final ConcurrentMap<String, KetchLeader> leaders;
+	private final Lock startLock;
+
+	/**
+	 * Initialize a new leader cache.
+	 *
+	 * @param system
+	 *            system configuration for the leaders
+	 */
+	public KetchLeaderCache(KetchSystem system) {
+		this.system = system;
+		leaders = new ConcurrentHashMap<>();
+		startLock = new ReentrantLock(true /* fair */);
+	}
+
+	/**
+	 * Lookup the leader instance for a given repository.
+	 *
+	 * @param repo
+	 *            repository to get the leader for.
+	 * @return the leader instance for the repository.
+	 * @throws URISyntaxException
+	 *             remote configuration contains an invalid URL.
+	 */
+	public KetchLeader get(Repository repo)
+			throws URISyntaxException {
+		String key = computeKey(repo);
+		KetchLeader leader = leaders.get(key);
+		if (leader != null) {
+			return leader;
+		}
+		return startLeader(key, repo);
+	}
+
+	private KetchLeader startLeader(String key, Repository repo)
+			throws URISyntaxException {
+		startLock.lock();
+		try {
+			KetchLeader leader = leaders.get(key);
+			if (leader != null) {
+				return leader;
+			}
+			leader = system.createLeader(repo);
+			leaders.put(key, leader);
+			return leader;
+		} finally {
+			startLock.unlock();
+		}
+	}
+
+	private static String computeKey(Repository repo) {
+		if (repo instanceof DfsRepository) {
+			DfsRepository dfs = (DfsRepository) repo;
+			return dfs.getDescription().getRepositoryName();
+		}
+
+		if (repo.getDirectory() != null) {
+			return repo.getDirectory().toURI().toString();
+		}
+
+		throw new IllegalArgumentException();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java
new file mode 100644
index 0000000..1b4307f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchPreReceive.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.eclipse.jgit.internal.ketch.Proposal.State.EXECUTED;
+import static org.eclipse.jgit.internal.ketch.Proposal.State.QUEUED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.transport.PreReceiveHook;
+import org.eclipse.jgit.transport.ProgressSpinner;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * PreReceiveHook for handling push traffic in a Ketch system.
+ * <p>
+ * Install an instance on {@link ReceivePack} to capture the commands and other
+ * connection state and relay them through the {@link KetchLeader}, allowing the
+ * leader to gain consensus about the new reference state.
+ */
+public class KetchPreReceive implements PreReceiveHook {
+	private static final Logger log = LoggerFactory.getLogger(KetchPreReceive.class);
+
+	private final KetchLeader leader;
+
+	/**
+	 * Construct a hook executing updates through a {@link KetchLeader}.
+	 *
+	 * @param leader
+	 *            leader for this repository.
+	 */
+	public KetchPreReceive(KetchLeader leader) {
+		this.leader = leader;
+	}
+
+	@Override
+	public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> cmds) {
+		cmds = ReceiveCommand.filter(cmds, NOT_ATTEMPTED);
+		if (cmds.isEmpty()) {
+			return;
+		}
+
+		try {
+			Proposal proposal = new Proposal(rp.getRevWalk(), cmds)
+				.setPushCertificate(rp.getPushCertificate())
+				.setAuthor(rp.getRefLogIdent())
+				.setMessage("push"); //$NON-NLS-1$
+			leader.queueProposal(proposal);
+			if (proposal.isDone()) {
+				// This failed fast, e.g. conflict or bad precondition.
+				return;
+			}
+
+			ProgressSpinner spinner = new ProgressSpinner(
+					rp.getMessageOutputStream());
+			if (proposal.getState() == QUEUED) {
+				waitForQueue(proposal, spinner);
+			}
+			if (!proposal.isDone()) {
+				waitForPropose(proposal, spinner);
+			}
+		} catch (IOException | InterruptedException e) {
+			String msg = JGitText.get().transactionAborted;
+			for (ReceiveCommand cmd : cmds) {
+				if (cmd.getResult() == NOT_ATTEMPTED) {
+					cmd.setResult(REJECTED_OTHER_REASON, msg);
+				}
+			}
+			log.error(msg, e);
+		}
+	}
+
+	private void waitForQueue(Proposal proposal, ProgressSpinner spinner)
+			throws InterruptedException {
+		spinner.beginTask(KetchText.get().waitingForQueue, 1, SECONDS);
+		while (!proposal.awaitStateChange(QUEUED, 250, MILLISECONDS)) {
+			spinner.update();
+		}
+		switch (proposal.getState()) {
+		case RUNNING:
+		default:
+			spinner.endTask(KetchText.get().starting);
+			break;
+
+		case EXECUTED:
+			spinner.endTask(KetchText.get().accepted);
+			break;
+
+		case ABORTED:
+			spinner.endTask(KetchText.get().failed);
+			break;
+		}
+	}
+
+	private void waitForPropose(Proposal proposal, ProgressSpinner spinner)
+			throws InterruptedException {
+		spinner.beginTask(KetchText.get().proposingUpdates, 2, SECONDS);
+		while (!proposal.await(250, MILLISECONDS)) {
+			spinner.update();
+		}
+		spinner.endTask(proposal.getState() == EXECUTED
+				? KetchText.get().accepted
+				: KetchText.get().failed);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
new file mode 100644
index 0000000..a30bbb2
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchReplica.java
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitSpeed.BATCHED;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitSpeed.FAST;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.CURRENT;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.LAGGING;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.OFFLINE;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.UNKNOWN;
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.reftree.RefTree;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.SystemReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Ketch replica, either {@link LocalReplica} or {@link RemoteGitReplica}.
+ * <p>
+ * Replicas can be either a stock Git replica, or a Ketch-aware replica.
+ * <p>
+ * A stock Git replica has no special knowledge of Ketch and simply stores
+ * objects and references. Ketch communicates with the stock Git replica using
+ * the Git push wire protocol. The {@link KetchLeader} commits an agreed upon
+ * state by pushing all references to the Git replica, for example
+ * {@code "refs/heads/master"} is pushed during commit. Stock Git replicas use
+ * {@link CommitMethod#ALL_REFS} to record the final state.
+ * <p>
+ * Ketch-aware replicas understand the {@code RefTree} sent during the proposal
+ * and during commit are able to update their own reference space to match the
+ * state represented by the {@code RefTree}. Ketch-aware replicas typically use
+ * a {@link org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase} and
+ * {@link CommitMethod#TXN_COMMITTED} to record the final state.
+ * <p>
+ * KetchReplica instances are tightly coupled with a single {@link KetchLeader}.
+ * Some state may be accessed by the leader thread and uses the leader's own
+ * {@link KetchLeader#lock} to protect shared data.
+ */
+public abstract class KetchReplica {
+	static final Logger log = LoggerFactory.getLogger(KetchReplica.class);
+	private static final byte[] PEEL = { ' ', '^' };
+
+	/** Participation of a replica in establishing consensus. */
+	public enum Participation {
+		/** Replica can vote. */
+		FULL,
+
+		/** Replica does not vote, but tracks leader. */
+		FOLLOWER_ONLY;
+	}
+
+	/** How this replica wants to receive Ketch commit operations. */
+	public enum CommitMethod {
+		/** All references are pushed to the peer as standard Git. */
+		ALL_REFS,
+
+		/** Only {@code refs/txn/committed} is written/updated. */
+		TXN_COMMITTED;
+	}
+
+	/** Delay before committing to a replica. */
+	public enum CommitSpeed {
+		/**
+		 * Send the commit immediately, even if it could be batched with the
+		 * next proposal.
+		 */
+		FAST,
+
+		/**
+		 * If the next proposal is available, batch the commit with it,
+		 * otherwise just send the commit. This generates less network use, but
+		 * may provide slower consistency on the replica.
+		 */
+		BATCHED;
+	}
+
+	/** Current state of a replica. */
+	public enum State {
+		/** Leader has not yet contacted the replica. */
+		UNKNOWN,
+
+		/** Replica is behind the consensus. */
+		LAGGING,
+
+		/** Replica matches the consensus. */
+		CURRENT,
+
+		/** Replica has a different (or unknown) history. */
+		DIVERGENT,
+
+		/** Replica's history contains the leader's history. */
+		AHEAD,
+
+		/** Replica can not be contacted. */
+		OFFLINE;
+	}
+
+	private final KetchLeader leader;
+	private final String replicaName;
+	private final Participation participation;
+	private final CommitMethod commitMethod;
+	private final CommitSpeed commitSpeed;
+	private final long minRetryMillis;
+	private final long maxRetryMillis;
+	private final Map<ObjectId, List<ReceiveCommand>> staged;
+	private final Map<String, ReceiveCommand> running;
+	private final Map<String, ReceiveCommand> waiting;
+	private final List<ReplicaPushRequest> queued;
+
+	/**
+	 * Value known for {@code "refs/txn/accepted"}.
+	 * <p>
+	 * Raft literature refers to this as {@code matchIndex}.
+	 */
+	private ObjectId txnAccepted;
+
+	/**
+	 * Value known for {@code "refs/txn/committed"}.
+	 * <p>
+	 * Raft literature refers to this as {@code commitIndex}. In traditional
+	 * Raft this is a state variable inside the follower implementation, but
+	 * Ketch keeps it in the leader.
+	 */
+	private ObjectId txnCommitted;
+
+	/** What is happening with this replica. */
+	private State state = UNKNOWN;
+	private String error;
+
+	/** Scheduled retry due to communication failure. */
+	private Future<?> retryFuture;
+	private long lastRetryMillis;
+	private long retryAtMillis;
+
+	/**
+	 * Configure a replica representation.
+	 *
+	 * @param leader
+	 *            instance this replica follows.
+	 * @param name
+	 *            unique-ish name identifying this replica for debugging.
+	 * @param cfg
+	 *            how Ketch should treat the replica.
+	 */
+	protected KetchReplica(KetchLeader leader, String name, ReplicaConfig cfg) {
+		this.leader = leader;
+		this.replicaName = name;
+		this.participation = cfg.getParticipation();
+		this.commitMethod = cfg.getCommitMethod();
+		this.commitSpeed = cfg.getCommitSpeed();
+		this.minRetryMillis = cfg.getMinRetry(MILLISECONDS);
+		this.maxRetryMillis = cfg.getMaxRetry(MILLISECONDS);
+		this.staged = new HashMap<>();
+		this.running = new HashMap<>();
+		this.waiting = new HashMap<>();
+		this.queued = new ArrayList<>(4);
+	}
+
+	/** @return system configuration. */
+	public KetchSystem getSystem() {
+		return getLeader().getSystem();
+	}
+
+	/** @return leader instance this replica follows. */
+	public KetchLeader getLeader() {
+		return leader;
+	}
+
+	/** @return unique-ish name for debugging. */
+	public String getName() {
+		return replicaName;
+	}
+
+	/** @return description of this replica for error/debug logging purposes. */
+	protected String describeForLog() {
+		return getName();
+	}
+
+	/** @return how the replica participates in this Ketch system. */
+	public Participation getParticipation() {
+		return participation;
+	}
+
+	/** @return how Ketch will commit to the repository. */
+	public CommitMethod getCommitMethod() {
+		return commitMethod;
+	}
+
+	/** @return when Ketch will commit to the repository. */
+	public CommitSpeed getCommitSpeed() {
+		return commitSpeed;
+	}
+
+	/**
+	 * Called by leader to perform graceful shutdown.
+	 * <p>
+	 * Default implementation cancels any scheduled retry. Subclasses may add
+	 * additional logic before or after calling {@code super.shutdown()}.
+	 * <p>
+	 * Called with {@link KetchLeader#lock} held by caller.
+	 */
+	protected void shutdown() {
+		Future<?> f = retryFuture;
+		if (f != null) {
+			retryFuture = null;
+			f.cancel(true);
+		}
+	}
+
+	ReplicaSnapshot snapshot() {
+		ReplicaSnapshot s = new ReplicaSnapshot(this);
+		s.accepted = txnAccepted;
+		s.committed = txnCommitted;
+		s.state = state;
+		s.error = error;
+		s.retryAtMillis = waitingForRetry() ? retryAtMillis : 0;
+		return s;
+	}
+
+	/**
+	 * Update the leader's view of the replica after a poll.
+	 * <p>
+	 * Called with {@link KetchLeader#lock} held by caller.
+	 *
+	 * @param refs
+	 *            map of refs from the replica.
+	 */
+	void initialize(Map<String, Ref> refs) {
+		if (txnAccepted == null) {
+			txnAccepted = getId(refs.get(getSystem().getTxnAccepted()));
+		}
+		if (txnCommitted == null) {
+			txnCommitted = getId(refs.get(getSystem().getTxnCommitted()));
+		}
+	}
+
+	ObjectId getTxnAccepted() {
+		return txnAccepted;
+	}
+
+	boolean hasAccepted(LogIndex id) {
+		return equals(txnAccepted, id);
+	}
+
+	private static boolean equals(@Nullable ObjectId a, LogIndex b) {
+		return a != null && b != null && AnyObjectId.equals(a, b);
+	}
+
+	/**
+	 * Schedule a proposal round with the replica.
+	 * <p>
+	 * Called with {@link KetchLeader#lock} held by caller.
+	 *
+	 * @param round
+	 *            current round being run by the leader.
+	 */
+	void pushTxnAcceptedAsync(Round round) {
+		List<ReceiveCommand> cmds = new ArrayList<>();
+		if (commitSpeed == BATCHED) {
+			LogIndex committedIndex = leader.getCommitted();
+			if (equals(txnAccepted, committedIndex)
+					&& !equals(txnCommitted, committedIndex)) {
+				prepareTxnCommitted(cmds, committedIndex);
+			}
+		}
+
+		// TODO(sop) Lagging replicas should build accept on the fly.
+		if (round.stageCommands != null) {
+			for (ReceiveCommand cmd : round.stageCommands) {
+				// TODO(sop): Do not send certain object graphs to replica.
+				cmds.add(copy(cmd));
+			}
+		}
+		cmds.add(new ReceiveCommand(
+				round.acceptedOldIndex, round.acceptedNewIndex,
+				getSystem().getTxnAccepted()));
+		pushAsync(new ReplicaPushRequest(this, cmds));
+	}
+
+	private static ReceiveCommand copy(ReceiveCommand c) {
+		return new ReceiveCommand(c.getOldId(), c.getNewId(), c.getRefName());
+	}
+
+	boolean shouldPushUnbatchedCommit(LogIndex committed, boolean leaderIdle) {
+		return (leaderIdle || commitSpeed == FAST) && hasAccepted(committed);
+	}
+
+	void pushCommitAsync(LogIndex committed) {
+		List<ReceiveCommand> cmds = new ArrayList<>();
+		prepareTxnCommitted(cmds, committed);
+		pushAsync(new ReplicaPushRequest(this, cmds));
+	}
+
+	private void prepareTxnCommitted(List<ReceiveCommand> cmds,
+			ObjectId committed) {
+		removeStaged(cmds, committed);
+		cmds.add(new ReceiveCommand(
+				txnCommitted, committed,
+				getSystem().getTxnCommitted()));
+	}
+
+	private void removeStaged(List<ReceiveCommand> cmds, ObjectId committed) {
+		List<ReceiveCommand> a = staged.remove(committed);
+		if (a != null) {
+			delete(cmds, a);
+		}
+		if (staged.isEmpty() || !(committed instanceof LogIndex)) {
+			return;
+		}
+
+		LogIndex committedIndex = (LogIndex) committed;
+		Iterator<Map.Entry<ObjectId, List<ReceiveCommand>>> itr = staged
+				.entrySet().iterator();
+		while (itr.hasNext()) {
+			Map.Entry<ObjectId, List<ReceiveCommand>> e = itr.next();
+			if (e.getKey() instanceof LogIndex) {
+				LogIndex stagedIndex = (LogIndex) e.getKey();
+				if (stagedIndex.isBefore(committedIndex)) {
+					delete(cmds, e.getValue());
+					itr.remove();
+				}
+			}
+		}
+	}
+
+	private static void delete(List<ReceiveCommand> cmds,
+			List<ReceiveCommand> createCmds) {
+		for (ReceiveCommand cmd : createCmds) {
+			ObjectId id = cmd.getNewId();
+			String name = cmd.getRefName();
+			cmds.add(new ReceiveCommand(id, ObjectId.zeroId(), name));
+		}
+	}
+
+	/**
+	 * Determine the next push for this replica (if any) and start it.
+	 * <p>
+	 * If the replica has successfully accepted the committed state of the
+	 * leader, this method will push all references to the replica using the
+	 * configured {@link CommitMethod}.
+	 * <p>
+	 * If the replica is {@link State#LAGGING} this method will begin catch up
+	 * by sending a more recent {@code refs/txn/accepted}.
+	 * <p>
+	 * Must be invoked with {@link KetchLeader#lock} held by caller.
+	 */
+	private void runNextPushRequest() {
+		LogIndex committed = leader.getCommitted();
+		if (!equals(txnCommitted, committed)
+				&& shouldPushUnbatchedCommit(committed, leader.isIdle())) {
+			pushCommitAsync(committed);
+		}
+
+		if (queued.isEmpty() || !running.isEmpty() || waitingForRetry()) {
+			return;
+		}
+
+		// Collapse all queued requests into a single request.
+		Map<String, ReceiveCommand> cmdMap = new HashMap<>();
+		for (ReplicaPushRequest req : queued) {
+			for (ReceiveCommand cmd : req.getCommands()) {
+				String name = cmd.getRefName();
+				ReceiveCommand old = cmdMap.remove(name);
+				if (old != null) {
+					cmd = new ReceiveCommand(
+							old.getOldId(), cmd.getNewId(),
+							name);
+				}
+				cmdMap.put(name, cmd);
+			}
+		}
+		queued.clear();
+		waiting.clear();
+
+		List<ReceiveCommand> next = new ArrayList<>(cmdMap.values());
+		for (ReceiveCommand cmd : next) {
+			running.put(cmd.getRefName(), cmd);
+		}
+		startPush(new ReplicaPushRequest(this, next));
+	}
+
+	private void pushAsync(ReplicaPushRequest req) {
+		if (defer(req)) {
+			// TODO(sop) Collapse during long retry outage.
+			for (ReceiveCommand cmd : req.getCommands()) {
+				waiting.put(cmd.getRefName(), cmd);
+			}
+			queued.add(req);
+		} else {
+			for (ReceiveCommand cmd : req.getCommands()) {
+				running.put(cmd.getRefName(), cmd);
+			}
+			startPush(req);
+		}
+	}
+
+	private boolean defer(ReplicaPushRequest req) {
+		if (waitingForRetry()) {
+			// Prior communication failure; everything is deferred.
+			return true;
+		}
+
+		for (ReceiveCommand nextCmd : req.getCommands()) {
+			ReceiveCommand priorCmd = waiting.get(nextCmd.getRefName());
+			if (priorCmd == null) {
+				priorCmd = running.get(nextCmd.getRefName());
+			}
+			if (priorCmd != null) {
+				// Another request pending on same ref; that must go first.
+				// Verify priorCmd.newId == nextCmd.oldId?
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean waitingForRetry() {
+		Future<?> f = retryFuture;
+		return f != null && !f.isDone();
+	}
+
+	private void retryLater(ReplicaPushRequest req) {
+		Collection<ReceiveCommand> cmds = req.getCommands();
+		for (ReceiveCommand cmd : cmds) {
+			cmd.setResult(NOT_ATTEMPTED, null);
+			if (!waiting.containsKey(cmd.getRefName())) {
+				waiting.put(cmd.getRefName(), cmd);
+			}
+		}
+		queued.add(0, new ReplicaPushRequest(this, cmds));
+
+		if (!waitingForRetry()) {
+			long delay = KetchSystem.delay(
+					lastRetryMillis,
+					minRetryMillis, maxRetryMillis);
+			if (log.isDebugEnabled()) {
+				log.debug("Retrying {} after {} ms", //$NON-NLS-1$
+						describeForLog(), Long.valueOf(delay));
+			}
+			lastRetryMillis = delay;
+			retryAtMillis = SystemReader.getInstance().getCurrentTime() + delay;
+			retryFuture = getSystem().getExecutor()
+					.schedule(new WeakRetryPush(this), delay, MILLISECONDS);
+		}
+	}
+
+	/** Weakly holds a retrying replica, allowing it to garbage collect. */
+	static class WeakRetryPush extends WeakReference<KetchReplica>
+			implements Callable<Void> {
+		WeakRetryPush(KetchReplica r) {
+			super(r);
+		}
+
+		@Override
+		public Void call() throws Exception {
+			KetchReplica r = get();
+			if (r != null) {
+				r.doRetryPush();
+			}
+			return null;
+		}
+	}
+
+	private void doRetryPush() {
+		leader.lock.lock();
+		try {
+			retryFuture = null;
+			runNextPushRequest();
+		} finally {
+			leader.lock.unlock();
+		}
+	}
+
+	/**
+	 * Begin executing a single push.
+	 * <p>
+	 * This method must move processing onto another thread.
+	 * Called with {@link KetchLeader#lock} held by caller.
+	 *
+	 * @param req
+	 *            the request to send to the replica.
+	 */
+	protected abstract void startPush(ReplicaPushRequest req);
+
+	/**
+	 * Callback from {@link ReplicaPushRequest} upon success or failure.
+	 * <p>
+	 * Acquires the {@link KetchLeader#lock} and updates the leader's internal
+	 * knowledge about this replica to reflect what has been learned during a
+	 * push to the replica. In some cases of divergence this method may take
+	 * some time to determine how the replica has diverged; to reduce contention
+	 * this is evaluated before acquiring the leader lock.
+	 *
+	 * @param repo
+	 *            local repository instance used by the push thread.
+	 * @param req
+	 *            push request just attempted.
+	 */
+	void afterPush(@Nullable Repository repo, ReplicaPushRequest req) {
+		ReceiveCommand acceptCmd = null;
+		ReceiveCommand commitCmd = null;
+		List<ReceiveCommand> stages = null;
+
+		for (ReceiveCommand cmd : req.getCommands()) {
+			String name = cmd.getRefName();
+			if (name.equals(getSystem().getTxnAccepted())) {
+				acceptCmd = cmd;
+			} else if (name.equals(getSystem().getTxnCommitted())) {
+				commitCmd = cmd;
+			} else if (cmd.getResult() == OK && cmd.getType() == CREATE
+					&& name.startsWith(getSystem().getTxnStage())) {
+				if (stages == null) {
+					stages = new ArrayList<>();
+				}
+				stages.add(cmd);
+			}
+		}
+
+		State newState = null;
+		ObjectId acceptId = readId(req, acceptCmd);
+		if (repo != null && acceptCmd != null && acceptCmd.getResult() != OK
+				&& req.getException() == null) {
+			try (LagCheck lag = new LagCheck(this, repo)) {
+				newState = lag.check(acceptId, acceptCmd);
+				acceptId = lag.getRemoteId();
+			}
+		}
+
+		leader.lock.lock();
+		try {
+			for (ReceiveCommand cmd : req.getCommands()) {
+				running.remove(cmd.getRefName());
+			}
+
+			Throwable err = req.getException();
+			if (err != null) {
+				state = OFFLINE;
+				error = err.toString();
+				retryLater(req);
+				leader.onReplicaUpdate(this);
+				return;
+			}
+
+			lastRetryMillis = 0;
+			error = null;
+			updateView(req, acceptId, commitCmd);
+
+			if (acceptCmd != null && acceptCmd.getResult() == OK) {
+				state = hasAccepted(leader.getHead()) ? CURRENT : LAGGING;
+				if (stages != null) {
+					staged.put(acceptCmd.getNewId(), stages);
+				}
+			} else if (newState != null) {
+				state = newState;
+			}
+
+			leader.onReplicaUpdate(this);
+			runNextPushRequest();
+		} finally {
+			leader.lock.unlock();
+		}
+	}
+
+	private void updateView(ReplicaPushRequest req, @Nullable ObjectId acceptId,
+			ReceiveCommand commitCmd) {
+		if (acceptId != null) {
+			txnAccepted = acceptId;
+		}
+
+		ObjectId committed = readId(req, commitCmd);
+		if (committed != null) {
+			txnCommitted = committed;
+		} else if (acceptId != null && txnCommitted == null) {
+			// Initialize during first conversation.
+			Map<String, Ref> adv = req.getRefs();
+			if (adv != null) {
+				Ref refs = adv.get(getSystem().getTxnCommitted());
+				txnCommitted = getId(refs);
+			}
+		}
+	}
+
+	@Nullable
+	private static ObjectId readId(ReplicaPushRequest req,
+			@Nullable ReceiveCommand cmd) {
+		if (cmd == null) {
+			// Ref was not in the command list, do not trust advertisement.
+			return null;
+
+		} else if (cmd.getResult() == OK) {
+			// Currently at newId.
+			return cmd.getNewId();
+		}
+
+		Map<String, Ref> refs = req.getRefs();
+		return refs != null ? getId(refs.get(cmd.getRefName())) : null;
+	}
+
+	/**
+	 * Fetch objects from the remote using the calling thread.
+	 * <p>
+	 * Called without {@link KetchLeader#lock}.
+	 *
+	 * @param repo
+	 *            local repository to fetch objects into.
+	 * @param req
+	 *            the request to fetch from a replica.
+	 * @throws IOException
+	 *             communication with the replica was not possible.
+	 */
+	protected abstract void blockingFetch(Repository repo,
+			ReplicaFetchRequest req) throws IOException;
+
+	/**
+	 * Build a list of commands to commit {@link CommitMethod#ALL_REFS}.
+	 *
+	 * @param git
+	 *            local leader repository to read committed state from.
+	 * @param current
+	 *            all known references in the replica's repository. Typically
+	 *            this comes from a push advertisement.
+	 * @param committed
+	 *            state being pushed to {@code refs/txn/committed}.
+	 * @return commands to update during commit.
+	 * @throws IOException
+	 *             cannot read the committed state.
+	 */
+	protected Collection<ReceiveCommand> prepareCommit(Repository git,
+			Map<String, Ref> current, ObjectId committed) throws IOException {
+		List<ReceiveCommand> delta = new ArrayList<>();
+		Map<String, Ref> remote = new HashMap<>(current);
+		try (RevWalk rw = new RevWalk(git);
+				TreeWalk tw = new TreeWalk(rw.getObjectReader())) {
+			tw.setRecursive(true);
+			tw.addTree(rw.parseCommit(committed).getTree());
+			while (tw.next()) {
+				if (tw.getRawMode(0) != TYPE_GITLINK
+						|| tw.isPathSuffix(PEEL, 2)) {
+					// Symbolic references cannot be pushed.
+					// Caching peeled values is handled remotely.
+					continue;
+				}
+
+				// TODO(sop) Do not send certain ref names to replica.
+				String name = RefTree.refName(tw.getPathString());
+				Ref oldRef = remote.remove(name);
+				ObjectId oldId = getId(oldRef);
+				ObjectId newId = tw.getObjectId(0);
+				if (!AnyObjectId.equals(oldId, newId)) {
+					delta.add(new ReceiveCommand(oldId, newId, name));
+				}
+			}
+		}
+
+		// Delete any extra references not in the committed state.
+		for (Ref ref : remote.values()) {
+			if (canDelete(ref)) {
+				delta.add(new ReceiveCommand(
+					ref.getObjectId(), ObjectId.zeroId(),
+					ref.getName()));
+			}
+		}
+		return delta;
+	}
+
+	boolean canDelete(Ref ref) {
+		String name = ref.getName();
+		if (HEAD.equals(name)) {
+			return false;
+		}
+		if (name.startsWith(getSystem().getTxnNamespace())) {
+			return false;
+		}
+		// TODO(sop) Do not delete precious names from replica.
+		return true;
+	}
+
+	@NonNull
+	static ObjectId getId(@Nullable Ref ref) {
+		if (ref != null) {
+			ObjectId id = ref.getObjectId();
+			if (id != null) {
+				return id;
+			}
+		}
+		return ObjectId.zeroId();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
new file mode 100644
index 0000000..71e872e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchSystem.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchConstants.ACCEPTED;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.COMMITTED;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_TYPE;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_SECTION_KETCH;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.DEFAULT_TXN_NAMESPACE;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.STAGE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_NAME;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_REMOTE;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Ketch system-wide configuration.
+ * <p>
+ * This class provides useful defaults for testing and small proof of concepts.
+ * Full scale installations are expected to subclass and override methods to
+ * provide consistent configuration across all managed repositories.
+ * <p>
+ * Servers should configure their own {@link ScheduledExecutorService}.
+ */
+public class KetchSystem {
+	private static final Random RNG = new Random();
+
+	/** @return default executor, one thread per available processor. */
+	public static ScheduledExecutorService defaultExecutor() {
+		return DefaultExecutorHolder.I;
+	}
+
+	private final ScheduledExecutorService executor;
+	private final String txnNamespace;
+	private final String txnAccepted;
+	private final String txnCommitted;
+	private final String txnStage;
+
+	/** Create a default system with a thread pool of 1 thread per CPU. */
+	public KetchSystem() {
+		this(defaultExecutor(), DEFAULT_TXN_NAMESPACE);
+	}
+
+	/**
+	 * Create a Ketch system with the provided executor service.
+	 *
+	 * @param executor
+	 *            thread pool to run background operations.
+	 * @param txnNamespace
+	 *            reference namespace for the RefTree graph and associated
+	 *            transaction state. Must begin with {@code "refs/"} and end
+	 *            with {@code '/'}, for example {@code "refs/txn/"}.
+	 */
+	public KetchSystem(ScheduledExecutorService executor, String txnNamespace) {
+		this.executor = executor;
+		this.txnNamespace = txnNamespace;
+		this.txnAccepted = txnNamespace + ACCEPTED;
+		this.txnCommitted = txnNamespace + COMMITTED;
+		this.txnStage = txnNamespace + STAGE;
+	}
+
+	/** @return executor to perform background operations. */
+	public ScheduledExecutorService getExecutor() {
+		return executor;
+	}
+
+	/**
+	 * Get the namespace used for the RefTree graph and transaction management.
+	 *
+	 * @return reference namespace such as {@code "refs/txn/"}.
+	 */
+	public String getTxnNamespace() {
+		return txnNamespace;
+	}
+
+	/** @return name of the accepted RefTree graph. */
+	public String getTxnAccepted() {
+		return txnAccepted;
+	}
+
+	/** @return name of the committed RefTree graph. */
+	public String getTxnCommitted() {
+		return txnCommitted;
+	}
+
+	/** @return prefix for staged objects, e.g. {@code "refs/txn/stage/"}. */
+	public String getTxnStage() {
+		return txnStage;
+	}
+
+	/** @return identity line for the committer header of a RefTreeGraph. */
+	public PersonIdent newCommitter() {
+		String name = "ketch"; //$NON-NLS-1$
+		String email = "ketch@system"; //$NON-NLS-1$
+		return new PersonIdent(name, email);
+	}
+
+	/**
+	 * Construct a random tag to identify a candidate during leader election.
+	 * <p>
+	 * Multiple processes trying to elect themselves leaders at exactly the same
+	 * time (rounded to seconds) using the same {@link #newCommitter()} identity
+	 * strings, for the same term, may generate the same ObjectId for the
+	 * election commit and falsely assume they have both won.
+	 * <p>
+	 * Candidates add this tag to their election ballot commit to disambiguate
+	 * the election. The tag only needs to be unique for a given triplet of
+	 * {@link #newCommitter()}, system time (rounded to seconds), and term. If
+	 * every replica in the system uses a unique {@code newCommitter} (such as
+	 * including the host name after the {@code "@"} in the email address) the
+	 * tag could be the empty string.
+	 * <p>
+	 * The default implementation generates a few bytes of random data.
+	 *
+	 * @return unique tag; null or empty string if {@code newCommitter()} is
+	 *         sufficiently unique to identify the leader.
+	 */
+	@Nullable
+	public String newLeaderTag() {
+		int n = RNG.nextInt(1 << (6 * 4));
+		return String.format("%06x", Integer.valueOf(n)); //$NON-NLS-1$
+	}
+
+	/**
+	 * Construct the KetchLeader instance of a repository.
+	 *
+	 * @param repo
+	 *            local repository stored by the leader.
+	 * @return leader instance.
+	 * @throws URISyntaxException
+	 *             a follower configuration contains an unsupported URI.
+	 */
+	public KetchLeader createLeader(final Repository repo)
+			throws URISyntaxException {
+		KetchLeader leader = new KetchLeader(this) {
+			@Override
+			protected Repository openRepository() {
+				repo.incrementOpen();
+				return repo;
+			}
+		};
+		leader.setReplicas(createReplicas(leader, repo));
+		return leader;
+	}
+
+	/**
+	 * Get the collection of replicas for a repository.
+	 * <p>
+	 * The collection of replicas must include the local repository.
+	 *
+	 * @param leader
+	 *            the leader driving these replicas.
+	 * @param repo
+	 *            repository to get the replicas of.
+	 * @return collection of replicas for the specified repository.
+	 * @throws URISyntaxException
+	 *             a configured URI is invalid.
+	 */
+	protected List<KetchReplica> createReplicas(KetchLeader leader,
+			Repository repo) throws URISyntaxException {
+		List<KetchReplica> replicas = new ArrayList<>();
+		Config cfg = repo.getConfig();
+		String localName = getLocalName(cfg);
+		for (String name : cfg.getSubsections(CONFIG_KEY_REMOTE)) {
+			if (!hasParticipation(cfg, name)) {
+				continue;
+			}
+
+			ReplicaConfig kc = ReplicaConfig.newFromConfig(cfg, name);
+			if (name.equals(localName)) {
+				replicas.add(new LocalReplica(leader, name, kc));
+				continue;
+			}
+
+			RemoteConfig rc = new RemoteConfig(cfg, name);
+			List<URIish> uris = rc.getPushURIs();
+			if (uris.isEmpty()) {
+				uris = rc.getURIs();
+			}
+			for (URIish uri : uris) {
+				String n = uris.size() == 1 ? name : uri.getHost();
+				replicas.add(new RemoteGitReplica(leader, n, uri, kc, rc));
+			}
+		}
+		return replicas;
+	}
+
+	private static boolean hasParticipation(Config cfg, String name) {
+		return cfg.getString(CONFIG_KEY_REMOTE, name, CONFIG_KEY_TYPE) != null;
+	}
+
+	private static String getLocalName(Config cfg) {
+		return cfg.getString(CONFIG_SECTION_KETCH, null, CONFIG_KEY_NAME);
+	}
+
+	static class DefaultExecutorHolder {
+		private static final Logger log = LoggerFactory.getLogger(KetchSystem.class);
+		static final ScheduledExecutorService I = create();
+
+		private static ScheduledExecutorService create() {
+			int cores = Runtime.getRuntime().availableProcessors();
+			int threads = Math.max(5, cores);
+			log.info("Using {} threads", Integer.valueOf(threads)); //$NON-NLS-1$
+			return Executors.newScheduledThreadPool(
+				threads,
+				new ThreadFactory() {
+					private final AtomicInteger threadCnt = new AtomicInteger();
+
+					@Override
+					public Thread newThread(Runnable r) {
+						int id = threadCnt.incrementAndGet();
+						Thread thr = new Thread(r);
+						thr.setName("KetchExecutor-" + id); //$NON-NLS-1$
+						return thr;
+					}
+				});
+		}
+
+		private DefaultExecutorHolder() {
+		}
+	}
+
+	/**
+	 * Compute a delay in a {@code min..max} interval with random jitter.
+	 *
+	 * @param last
+	 *            amount of delay waited before the last attempt. This is used
+	 *            to seed the next delay interval. Should be 0 if there was no
+	 *            prior delay.
+	 * @param min
+	 *            shortest amount of allowable delay between attempts.
+	 * @param max
+	 *            longest amount of allowable delay between attempts.
+	 * @return new amount of delay to wait before the next attempt.
+	 */
+	static long delay(long last, long min, long max) {
+		long r = Math.max(0, last * 3 - min);
+		if (r > 0) {
+			int c = (int) Math.min(r + 1, Integer.MAX_VALUE);
+			r = RNG.nextInt(c);
+		}
+		return Math.max(Math.min(min + r, max), min);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java
new file mode 100644
index 0000000..b6c3bc9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/KetchText.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import org.eclipse.jgit.nls.NLS;
+import org.eclipse.jgit.nls.TranslationBundle;
+
+/** Translation bundle for the Ketch implementation. */
+public class KetchText extends TranslationBundle {
+	/** @return instance of this translation bundle. */
+	public static KetchText get() {
+		return NLS.getBundleFor(KetchText.class);
+	}
+
+	// @formatter:off
+	/***/ public String accepted;
+	/***/ public String cannotFetchFromLocalReplica;
+	/***/ public String failed;
+	/***/ public String invalidFollowerUri;
+	/***/ public String leaderFailedToStore;
+	/***/ public String localReplicaRequired;
+	/***/ public String mismatchedTxnNamespace;
+	/***/ public String outsideTxnNamespace;
+	/***/ public String proposingUpdates;
+	/***/ public String queuedProposalFailedToApply;
+	/***/ public String starting;
+	/***/ public String unsupportedVoterCount;
+	/***/ public String waitingForQueue;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
new file mode 100644
index 0000000..35327ea
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.AHEAD;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.DIVERGENT;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.LAGGING;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.UNKNOWN;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * A helper to check if a {@link KetchReplica} is ahead or behind the leader.
+ */
+class LagCheck implements AutoCloseable {
+	private final KetchReplica replica;
+	private final Repository repo;
+	private RevWalk rw;
+	private ObjectId remoteId;
+
+	LagCheck(KetchReplica replica, Repository repo) {
+		this.replica = replica;
+		this.repo = repo;
+		initRevWalk();
+	}
+
+	private void initRevWalk() {
+		if (rw != null) {
+			rw.close();
+		}
+
+		rw = new RevWalk(repo);
+		rw.setRetainBody(false);
+	}
+
+	public void close() {
+		if (rw != null) {
+			rw.close();
+			rw = null;
+		}
+	}
+
+	ObjectId getRemoteId() {
+		return remoteId;
+	}
+
+	KetchReplica.State check(ObjectId acceptId, ReceiveCommand acceptCmd) {
+		remoteId = acceptId;
+		if (remoteId == null) {
+			// Nothing advertised by the replica, value is unknown.
+			return UNKNOWN;
+		}
+
+		if (AnyObjectId.equals(remoteId, ObjectId.zeroId())) {
+			// Replica does not have the txnAccepted reference.
+			return LAGGING;
+		}
+
+		try {
+			RevCommit remote;
+			try {
+				remote = parseRemoteCommit(acceptCmd.getRefName());
+			} catch (RefGoneException gone) {
+				// Replica does not have the txnAccepted reference.
+				return LAGGING;
+			} catch (MissingObjectException notFound) {
+				// Local repository does not know this commit so it cannot
+				// be including the replica's log.
+				return DIVERGENT;
+			}
+
+			RevCommit head = rw.parseCommit(acceptCmd.getNewId());
+			if (rw.isMergedInto(remote, head)) {
+				return LAGGING;
+			}
+
+			// TODO(sop) Check term to see if my leader was deposed.
+			if (rw.isMergedInto(head, remote)) {
+				return AHEAD;
+			} else {
+				return DIVERGENT;
+			}
+		} catch (IOException err) {
+			KetchReplica.log.error(String.format(
+					"Cannot compare %s", //$NON-NLS-1$
+					acceptCmd.getRefName()), err);
+			return UNKNOWN;
+		}
+	}
+
+	private RevCommit parseRemoteCommit(String refName)
+			throws IOException, MissingObjectException, RefGoneException {
+		try {
+			return rw.parseCommit(remoteId);
+		} catch (MissingObjectException notLocal) {
+			// Fall through and try to acquire the object by fetching it.
+		}
+
+		ReplicaFetchRequest fetch = new ReplicaFetchRequest(
+				Collections.singleton(refName),
+				Collections.<ObjectId> emptySet());
+		try {
+			replica.blockingFetch(repo, fetch);
+		} catch (IOException fetchErr) {
+			KetchReplica.log.error(String.format(
+					"Cannot fetch %s (%s) from %s", //$NON-NLS-1$
+					remoteId.abbreviate(8).name(), refName,
+					replica.describeForLog()), fetchErr);
+			throw new MissingObjectException(remoteId, OBJ_COMMIT);
+		}
+
+		Map<String, Ref> adv = fetch.getRefs();
+		if (adv == null) {
+			throw new MissingObjectException(remoteId, OBJ_COMMIT);
+		}
+
+		Ref ref = adv.get(refName);
+		if (ref == null || ref.getObjectId() == null) {
+			throw new RefGoneException();
+		}
+
+		initRevWalk();
+		remoteId = ref.getObjectId();
+		return rw.parseCommit(remoteId);
+	}
+
+	private static class RefGoneException extends Exception {
+		private static final long serialVersionUID = 1L;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java
new file mode 100644
index 0000000..28a49df
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LeaderSnapshot.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchReplica.State.OFFLINE;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.ObjectId;
+
+/** A snapshot of a leader and its view of the world. */
+public class LeaderSnapshot {
+	final List<ReplicaSnapshot> replicas = new ArrayList<>();
+	KetchLeader.State state;
+	long term;
+	LogIndex headIndex;
+	LogIndex committedIndex;
+	boolean idle;
+
+	LeaderSnapshot() {
+	}
+
+	/** @return unmodifiable view of configured replicas. */
+	public Collection<ReplicaSnapshot> getReplicas() {
+		return Collections.unmodifiableList(replicas);
+	}
+
+	/** @return current state of the leader. */
+	public KetchLeader.State getState() {
+		return state;
+	}
+
+	/**
+	 * @return {@code true} if the leader is not running a round to reach
+	 *         consensus, and has no rounds queued.
+	 */
+	public boolean isIdle() {
+		return idle;
+	}
+
+	/**
+	 * @return term of this leader. Valid only if {@link #getState()} is
+	 *         currently {@link KetchLeader.State#LEADER}.
+	 */
+	public long getTerm() {
+		return term;
+	}
+
+	/**
+	 * @return end of the leader's log; null if leader hasn't started up enough
+	 *         to begin its own election.
+	 */
+	@Nullable
+	public LogIndex getHead() {
+		return headIndex;
+	}
+
+	/**
+	 * @return state the leader knows is committed on a majority of participant
+	 *         replicas. Null until the leader instance has committed a log
+	 *         index within its own term.
+	 */
+	@Nullable
+	public LogIndex getCommitted() {
+		return committedIndex;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder s = new StringBuilder();
+		s.append(isIdle() ? "IDLE" : "RUNNING"); //$NON-NLS-1$ //$NON-NLS-2$
+		s.append(" state ").append(getState()); //$NON-NLS-1$
+		if (getTerm() > 0) {
+			s.append(" term ").append(getTerm()); //$NON-NLS-1$
+		}
+		s.append('\n');
+		s.append(String.format(
+				"%-10s %12s %12s\n", //$NON-NLS-1$
+				"Replica", "Accepted", "Committed")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		s.append("------------------------------------\n"); //$NON-NLS-1$
+		debug(s, "(leader)", getHead(), getCommitted()); //$NON-NLS-1$
+		s.append('\n');
+		for (ReplicaSnapshot r : getReplicas()) {
+			debug(s, r);
+			s.append('\n');
+		}
+		s.append('\n');
+		return s.toString();
+	}
+
+	private static void debug(StringBuilder b, ReplicaSnapshot s) {
+		KetchReplica replica = s.getReplica();
+		debug(b, replica.getName(), s.getAccepted(), s.getCommitted());
+		b.append(String.format(" %-8s %s", //$NON-NLS-1$
+				replica.getParticipation(), s.getState()));
+		if (s.getState() == OFFLINE) {
+			String err = s.getErrorMessage();
+			if (err != null) {
+				b.append(" (").append(err).append(')'); //$NON-NLS-1$
+			}
+		}
+	}
+
+	private static void debug(StringBuilder s, String name,
+			ObjectId accepted, ObjectId committed) {
+		s.append(String.format(
+				"%-10s %-12s %-12s", //$NON-NLS-1$
+				name, str(accepted), str(committed)));
+	}
+
+	static String str(ObjectId c) {
+		if (c instanceof LogIndex) {
+			return ((LogIndex) c).describeForLog();
+		} else if (c != null) {
+			return c.abbreviate(8).name();
+		}
+		return "-"; //$NON-NLS-1$
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
new file mode 100644
index 0000000..e297bca
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LocalReplica.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod.ALL_REFS;
+import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod.TXN_COMMITTED;
+import static org.eclipse.jgit.lib.RefDatabase.ALL;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** Ketch replica running on the same system as the {@link KetchLeader}. */
+public class LocalReplica extends KetchReplica {
+	/**
+	 * Configure a local replica.
+	 *
+	 * @param leader
+	 *            instance this replica follows.
+	 * @param name
+	 *            unique-ish name identifying this replica for debugging.
+	 * @param cfg
+	 *            how Ketch should treat the local system.
+	 */
+	public LocalReplica(KetchLeader leader, String name, ReplicaConfig cfg) {
+		super(leader, name, cfg);
+	}
+
+	@Override
+	protected String describeForLog() {
+		return String.format("%s (leader)", getName()); //$NON-NLS-1$
+	}
+
+	/**
+	 * Initializes local replica by reading accepted and committed references.
+	 * <p>
+	 * Loads accepted and committed references from the reference database of
+	 * the local replica and stores their current ObjectIds in memory.
+	 *
+	 * @param repo
+	 *            repository to initialize state from.
+	 * @throws IOException
+	 *             cannot read repository state.
+	 */
+	void initialize(Repository repo) throws IOException {
+		RefDatabase refdb = repo.getRefDatabase();
+		if (refdb instanceof RefTreeDatabase) {
+			RefTreeDatabase treeDb = (RefTreeDatabase) refdb;
+			String txnNamespace = getSystem().getTxnNamespace();
+			if (!txnNamespace.equals(treeDb.getTxnNamespace())) {
+				throw new IOException(MessageFormat.format(
+						KetchText.get().mismatchedTxnNamespace,
+						txnNamespace, treeDb.getTxnNamespace()));
+			}
+			refdb = treeDb.getBootstrap();
+		}
+		initialize(refdb.exactRef(
+				getSystem().getTxnAccepted(),
+				getSystem().getTxnCommitted()));
+	}
+
+	@Override
+	protected void startPush(final ReplicaPushRequest req) {
+		getSystem().getExecutor().execute(new Runnable() {
+			@Override
+			public void run() {
+				try (Repository git = getLeader().openRepository()) {
+					try {
+						update(git, req);
+						req.done(git);
+					} catch (Throwable err) {
+						req.setException(git, err);
+					}
+				} catch (IOException err) {
+					req.setException(null, err);
+				}
+			}
+		});
+	}
+
+	@Override
+	protected void blockingFetch(Repository repo, ReplicaFetchRequest req)
+			throws IOException {
+		throw new IOException(KetchText.get().cannotFetchFromLocalReplica);
+	}
+
+	private void update(Repository git, ReplicaPushRequest req)
+			throws IOException {
+		RefDatabase refdb = git.getRefDatabase();
+		CommitMethod method = getCommitMethod();
+
+		// Local replica probably uses RefTreeDatabase, the request should
+		// be only for the txnNamespace, so drop to the bootstrap layer.
+		if (refdb instanceof RefTreeDatabase) {
+			if (!isOnlyTxnNamespace(req.getCommands())) {
+				return;
+			}
+
+			refdb = ((RefTreeDatabase) refdb).getBootstrap();
+			method = TXN_COMMITTED;
+		}
+
+		BatchRefUpdate batch = refdb.newBatchUpdate();
+		batch.setRefLogIdent(getSystem().newCommitter());
+		batch.setRefLogMessage("ketch", false); //$NON-NLS-1$
+		batch.setAllowNonFastForwards(true);
+
+		// RefDirectory updates multiple references sequentially.
+		// Run everything else first, then accepted (if present),
+		// then committed (if present). This ensures an earlier
+		// failure will not update these critical references.
+		ReceiveCommand accepted = null;
+		ReceiveCommand committed = null;
+		for (ReceiveCommand cmd : req.getCommands()) {
+			String name = cmd.getRefName();
+			if (name.equals(getSystem().getTxnAccepted())) {
+				accepted = cmd;
+			} else if (name.equals(getSystem().getTxnCommitted())) {
+				committed = cmd;
+			} else {
+				batch.addCommand(cmd);
+			}
+		}
+		if (committed != null && method == ALL_REFS) {
+			Map<String, Ref> refs = refdb.getRefs(ALL);
+			batch.addCommand(prepareCommit(git, refs, committed.getNewId()));
+		}
+		if (accepted != null) {
+			batch.addCommand(accepted);
+		}
+		if (committed != null) {
+			batch.addCommand(committed);
+		}
+
+		try (RevWalk rw = new RevWalk(git)) {
+			batch.execute(rw, NullProgressMonitor.INSTANCE);
+		}
+
+		// KetchReplica only cares about accepted and committed in
+		// advertisement. If they failed, store the current values
+		// back in the ReplicaPushRequest.
+		List<String> failed = new ArrayList<>(2);
+		checkFailed(failed, accepted);
+		checkFailed(failed, committed);
+		if (!failed.isEmpty()) {
+			String[] arr = failed.toArray(new String[failed.size()]);
+			req.setRefs(refdb.exactRef(arr));
+		}
+	}
+
+	private static void checkFailed(List<String> failed, ReceiveCommand cmd) {
+		if (cmd != null && cmd.getResult() != OK) {
+			failed.add(cmd.getRefName());
+		}
+	}
+
+	private boolean isOnlyTxnNamespace(Collection<ReceiveCommand> cmdList) {
+		// Be paranoid and reject non txnNamespace names, this
+		// is a programming error in Ketch that should not occur.
+
+		String txnNamespace = getSystem().getTxnNamespace();
+		for (ReceiveCommand cmd : cmdList) {
+			if (!cmd.getRefName().startsWith(txnNamespace)) {
+				cmd.setResult(REJECTED_OTHER_REASON,
+						MessageFormat.format(
+								KetchText.get().outsideTxnNamespace,
+								cmd.getRefName(), txnNamespace));
+				ReceiveCommand.abort(cmdList);
+				return false;
+			}
+		}
+		return true;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java
new file mode 100644
index 0000000..350c8ed
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LogIndex.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * An ObjectId for a commit extended with incrementing log index.
+ * <p>
+ * For any two LogIndex instances, {@code A} is an ancestor of {@code C}
+ * reachable through parent edges in the graph if {@code A.index < C.index}.
+ * LogIndex provides a performance optimization for Ketch, the same information
+ * can be obtained from {@link org.eclipse.jgit.revwalk.RevWalk}.
+ * <p>
+ * Index values are only valid within a single {@link KetchLeader} instance
+ * after it has won an election. By restricting scope to a single leader new
+ * leaders do not need to traverse the entire history to determine the next
+ * {@code index} for new proposals. This differs from Raft, where leader
+ * election uses the log index and the term number to determine which replica
+ * holds a sufficiently up-to-date log. Since Ketch uses Git objects for storage
+ * of its replicated log, it keeps the term number as Raft does but uses
+ * standard Git operations to imply the log index.
+ * <p>
+ * {@link Round#runAsync(AnyObjectId)} bumps the index as each new round is
+ * constructed.
+ */
+public class LogIndex extends ObjectId {
+	static LogIndex unknown(AnyObjectId id) {
+		return new LogIndex(id, 0);
+	}
+
+	private final long index;
+
+	private LogIndex(AnyObjectId id, long index) {
+		super(id);
+		this.index = index;
+	}
+
+	LogIndex nextIndex(AnyObjectId id) {
+		return new LogIndex(id, index + 1);
+	}
+
+	/** @return index provided by the current leader instance. */
+	public long getIndex() {
+		return index;
+	}
+
+	/**
+	 * Check if this log position committed before another log position.
+	 * <p>
+	 * Only valid for log positions in memory for the current leader.
+	 *
+	 * @param c
+	 *            other (more recent) log position.
+	 * @return true if this log position was before {@code c} or equal to c and
+	 *         therefore any agreement of {@code c} implies agreement on this
+	 *         log position.
+	 */
+	boolean isBefore(LogIndex c) {
+		return index <= c.index;
+	}
+
+	/**
+	 * @return string suitable for debug logging containing the log index and
+	 *         abbreviated ObjectId.
+	 */
+	@SuppressWarnings("boxing")
+	public String describeForLog() {
+		return String.format("%5d/%s", index, abbreviate(6).name()); //$NON-NLS-1$
+	}
+
+	@SuppressWarnings("boxing")
+	@Override
+	public String toString() {
+		return String.format("LogId[%5d/%s]", index, name()); //$NON-NLS-1$
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java
new file mode 100644
index 0000000..0876eb5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Proposal.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.Proposal.State.ABORTED;
+import static org.eclipse.jgit.internal.ketch.Proposal.State.EXECUTED;
+import static org.eclipse.jgit.internal.ketch.Proposal.State.NEW;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.reftree.Command;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PushCertificate;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * A proposal to be applied in a Ketch system.
+ * <p>
+ * Pushing to a Ketch leader results in the leader making a proposal. The
+ * proposal includes the list of reference updates. The leader attempts to send
+ * the proposal to a quorum of replicas by pushing the proposal to a "staging"
+ * area under the {@code refs/txn/stage/} namespace. If the proposal succeeds
+ * then the changes are durable and the leader can commit the proposal.
+ * <p>
+ * Proposals are executed by {@link KetchLeader#queueProposal(Proposal)}, which
+ * runs them asynchronously in the background. Proposals are thread-safe futures
+ * allowing callers to {@link #await()} for results or be notified by callback
+ * using {@link #addListener(Runnable)}.
+ */
+public class Proposal {
+	/** Current state of the proposal. */
+	public enum State {
+		/** Proposal has not yet been given to a {@link KetchLeader}. */
+		NEW(false),
+
+		/**
+		 * Proposal was validated and has entered the queue, but a round
+		 * containing this proposal has not started yet.
+		 */
+		QUEUED(false),
+
+		/** Round containing the proposal has begun and is in progress. */
+		RUNNING(false),
+
+		/**
+		 * Proposal was executed through a round. Individual results from
+		 * {@link Proposal#getCommands()}, {@link Command#getResult()} explain
+		 * the success or failure outcome.
+		 */
+		EXECUTED(true),
+
+		/** Proposal was aborted and did not reach consensus. */
+		ABORTED(true);
+
+		private final boolean done;
+
+		private State(boolean done) {
+			this.done = done;
+		}
+
+		/** @return true if this is a terminal state. */
+		public boolean isDone() {
+			return done;
+		}
+	}
+
+	private final List<Command> commands;
+	private PersonIdent author;
+	private String message;
+	private PushCertificate pushCert;
+	private final List<Runnable> listeners = new CopyOnWriteArrayList<>();
+	private final AtomicReference<State> state = new AtomicReference<>(NEW);
+
+	/**
+	 * Create a proposal from a list of Ketch commands.
+	 *
+	 * @param cmds
+	 *            prepared list of commands.
+	 */
+	public Proposal(List<Command> cmds) {
+		commands = Collections.unmodifiableList(new ArrayList<>(cmds));
+	}
+
+	/**
+	 * Create a proposal from a collection of received commands.
+	 *
+	 * @param rw
+	 *            walker to assist in preparing commands.
+	 * @param cmds
+	 *            list of pending commands.
+	 * @throws MissingObjectException
+	 *             newId of a command is not found locally.
+	 * @throws IOException
+	 *             local objects cannot be accessed.
+	 */
+	public Proposal(RevWalk rw, Collection<ReceiveCommand> cmds)
+			throws MissingObjectException, IOException {
+		commands = asCommandList(rw, cmds);
+	}
+
+	private static List<Command> asCommandList(RevWalk rw,
+			Collection<ReceiveCommand> cmds)
+					throws MissingObjectException, IOException {
+		List<Command> commands = new ArrayList<>(cmds.size());
+		for (ReceiveCommand cmd : cmds) {
+			commands.add(new Command(rw, cmd));
+		}
+		return Collections.unmodifiableList(commands);
+	}
+
+	/** @return commands from this proposal. */
+	public Collection<Command> getCommands() {
+		return commands;
+	}
+
+	/** @return optional author of the proposal. */
+	@Nullable
+	public PersonIdent getAuthor() {
+		return author;
+	}
+
+	/**
+	 * Set the author for the proposal.
+	 *
+	 * @param who
+	 *            optional identity of the author of the proposal.
+	 * @return {@code this}
+	 */
+	public Proposal setAuthor(@Nullable PersonIdent who) {
+		author = who;
+		return this;
+	}
+
+	/** @return optional message for the commit log of the RefTree. */
+	@Nullable
+	public String getMessage() {
+		return message;
+	}
+
+	/**
+	 * Set the message to appear in the commit log of the RefTree.
+	 *
+	 * @param msg
+	 *            message text for the commit.
+	 * @return {@code this}
+	 */
+	public Proposal setMessage(@Nullable String msg) {
+		message = msg != null && !msg.isEmpty() ? msg : null;
+		return this;
+	}
+
+	/** @return optional certificate signing the references. */
+	@Nullable
+	public PushCertificate getPushCertificate() {
+		return pushCert;
+	}
+
+	/**
+	 * Set the push certificate signing the references.
+	 *
+	 * @param cert
+	 *            certificate, may be null.
+	 * @return {@code this}
+	 */
+	public Proposal setPushCertificate(@Nullable PushCertificate cert) {
+		pushCert = cert;
+		return this;
+	}
+
+	/**
+	 * Add a callback to be invoked when the proposal is done.
+	 * <p>
+	 * A proposal is done when it has entered either {@link State#EXECUTED} or
+	 * {@link State#ABORTED} state. If the proposal is already done
+	 * {@code callback.run()} is immediately invoked on the caller's thread.
+	 *
+	 * @param callback
+	 *            method to run after the proposal is done. The callback may be
+	 *            run on a Ketch system thread and should be completed quickly.
+	 */
+	public void addListener(Runnable callback) {
+		boolean runNow = false;
+		synchronized (state) {
+			if (state.get().isDone()) {
+				runNow = true;
+			} else {
+				listeners.add(callback);
+			}
+		}
+		if (runNow) {
+			callback.run();
+		}
+	}
+
+	/** Set command result as OK. */
+	void success() {
+		for (Command c : commands) {
+			if (c.getResult() == NOT_ATTEMPTED) {
+				c.setResult(OK);
+			}
+		}
+		notifyState(EXECUTED);
+	}
+
+	/** Mark commands as "transaction aborted". */
+	void abort() {
+		Command.abort(commands, null);
+		notifyState(ABORTED);
+	}
+
+	/** @return read the current state of the proposal. */
+	public State getState() {
+		return state.get();
+	}
+
+	/**
+	 * @return {@code true} if the proposal was attempted. A true value does not
+	 *         mean consensus was reached, only that the proposal was considered
+	 *         and will not be making any more progress beyond its current
+	 *         state.
+	 */
+	public boolean isDone() {
+		return state.get().isDone();
+	}
+
+	/**
+	 * Wait for the proposal to be attempted and {@link #isDone()} to be true.
+	 *
+	 * @throws InterruptedException
+	 *             caller was interrupted before proposal executed.
+	 */
+	public void await() throws InterruptedException {
+		synchronized (state) {
+			while (!state.get().isDone()) {
+				state.wait();
+			}
+		}
+	}
+
+	/**
+	 * Wait for the proposal to be attempted and {@link #isDone()} to be true.
+	 *
+	 * @param wait
+	 *            how long to wait.
+	 * @param unit
+	 *            unit describing the wait time.
+	 * @return true if the proposal is done; false if the method timed out.
+	 * @throws InterruptedException
+	 *             caller was interrupted before proposal executed.
+	 */
+	public boolean await(long wait, TimeUnit unit) throws InterruptedException {
+		synchronized (state) {
+			if (state.get().isDone()) {
+				return true;
+			}
+			state.wait(unit.toMillis(wait));
+			return state.get().isDone();
+		}
+	}
+
+	/**
+	 * Wait for the proposal to exit a state.
+	 *
+	 * @param notIn
+	 *            state the proposal should not be in to return.
+	 * @param wait
+	 *            how long to wait.
+	 * @param unit
+	 *            unit describing the wait time.
+	 * @return true if the proposal exited the state; false on time out.
+	 * @throws InterruptedException
+	 *             caller was interrupted before proposal executed.
+	 */
+	public boolean awaitStateChange(State notIn, long wait, TimeUnit unit)
+			throws InterruptedException {
+		synchronized (state) {
+			if (state.get() != notIn) {
+				return true;
+			}
+			state.wait(unit.toMillis(wait));
+			return state.get() != notIn;
+		}
+	}
+
+	void notifyState(State s) {
+		synchronized (state) {
+			state.set(s);
+			state.notifyAll();
+		}
+		if (s.isDone()) {
+			for (Runnable callback : listeners) {
+				callback.run();
+			}
+			listeners.clear();
+		}
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder s = new StringBuilder();
+		s.append("Ketch Proposal {\n"); //$NON-NLS-1$
+		s.append("  ").append(state.get()).append('\n'); //$NON-NLS-1$
+		if (author != null) {
+			s.append("  author ").append(author).append('\n'); //$NON-NLS-1$
+		}
+		if (message != null) {
+			s.append("  message ").append(message).append('\n'); //$NON-NLS-1$
+		}
+		for (Command c : commands) {
+			s.append("  "); //$NON-NLS-1$
+			format(s, c.getOldRef(), "CREATE"); //$NON-NLS-1$
+			s.append(' ');
+			format(s, c.getNewRef(), "DELETE"); //$NON-NLS-1$
+			s.append(' ').append(c.getRefName());
+			if (c.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
+				s.append(' ').append(c.getResult()); // $NON-NLS-1$
+			}
+			s.append('\n');
+		}
+		s.append('}');
+		return s.toString();
+	}
+
+	private static void format(StringBuilder s, @Nullable Ref r, String n) {
+		if (r == null) {
+			s.append(n);
+		} else if (r.isSymbolic()) {
+			s.append(r.getTarget().getName());
+		} else {
+			ObjectId id = r.getObjectId();
+			if (id != null) {
+				s.append(id.abbreviate(8).name());
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
new file mode 100644
index 0000000..d34477a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.Proposal.State.RUNNING;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.reftree.Command;
+import org.eclipse.jgit.internal.storage.reftree.RefTree;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** A {@link Round} that aggregates and sends user {@link Proposal}s. */
+class ProposalRound extends Round {
+	private final List<Proposal> todo;
+	private RefTree queuedTree;
+
+	ProposalRound(KetchLeader leader, LogIndex head, List<Proposal> todo,
+			@Nullable RefTree tree) {
+		super(leader, head);
+		this.todo = todo;
+
+		if (tree != null && canCombine(todo)) {
+			this.queuedTree = tree;
+		} else {
+			leader.roundHoldsReferenceToRefTree = false;
+		}
+	}
+
+	private static boolean canCombine(List<Proposal> todo) {
+		Proposal first = todo.get(0);
+		for (int i = 1; i < todo.size(); i++) {
+			if (!canCombine(first, todo.get(i))) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	private static boolean canCombine(Proposal a, Proposal b) {
+		String aMsg = nullToEmpty(a.getMessage());
+		String bMsg = nullToEmpty(b.getMessage());
+		return aMsg.equals(bMsg) && canCombine(a.getAuthor(), b.getAuthor());
+	}
+
+	private static String nullToEmpty(@Nullable String str) {
+		return str != null ? str : ""; //$NON-NLS-1$
+	}
+
+	private static boolean canCombine(@Nullable PersonIdent a,
+			@Nullable PersonIdent b) {
+		if (a != null && b != null) {
+			// Same name and email address. Combine timestamp as the two
+			// proposals are running concurrently and appear together or
+			// not at all from the point of view of an outside reader.
+			return a.getName().equals(b.getName())
+					&& a.getEmailAddress().equals(b.getEmailAddress());
+		}
+
+		// If a and b are null, both will be the system identity.
+		return a == null && b == null;
+	}
+
+	void start() throws IOException {
+		for (Proposal p : todo) {
+			p.notifyState(RUNNING);
+		}
+		try {
+			ObjectId id;
+			try (Repository git = leader.openRepository()) {
+				id = insertProposals(git);
+			}
+			runAsync(id);
+		} catch (NoOp e) {
+			for (Proposal p : todo) {
+				p.success();
+			}
+			leader.lock.lock();
+			try {
+				leader.nextRound();
+			} finally {
+				leader.lock.unlock();
+			}
+		} catch (IOException e) {
+			abort();
+			throw e;
+		}
+	}
+
+	private ObjectId insertProposals(Repository git)
+			throws IOException, NoOp {
+		ObjectId id;
+		try (ObjectInserter inserter = git.newObjectInserter()) {
+			// TODO(sop) Process signed push certificates.
+
+			if (queuedTree != null) {
+				id = insertSingleProposal(git, inserter);
+			} else {
+				id = insertMultiProposal(git, inserter);
+			}
+
+			stageCommands = makeStageList(git, inserter);
+			inserter.flush();
+		}
+		return id;
+	}
+
+	private ObjectId insertSingleProposal(Repository git,
+			ObjectInserter inserter) throws IOException, NoOp {
+		// Fast path: tree is passed in with all proposals applied.
+		ObjectId treeId = queuedTree.writeTree(inserter);
+		queuedTree = null;
+		leader.roundHoldsReferenceToRefTree = false;
+
+		if (!ObjectId.zeroId().equals(acceptedOldIndex)) {
+			try (RevWalk rw = new RevWalk(git)) {
+				RevCommit c = rw.parseCommit(acceptedOldIndex);
+				if (treeId.equals(c.getTree())) {
+					throw new NoOp();
+				}
+			}
+		}
+
+		Proposal p = todo.get(0);
+		CommitBuilder b = new CommitBuilder();
+		b.setTreeId(treeId);
+		if (!ObjectId.zeroId().equals(acceptedOldIndex)) {
+			b.setParentId(acceptedOldIndex);
+		}
+		b.setCommitter(leader.getSystem().newCommitter());
+		b.setAuthor(p.getAuthor() != null ? p.getAuthor() : b.getCommitter());
+		b.setMessage(message(p));
+		return inserter.insert(b);
+	}
+
+	private ObjectId insertMultiProposal(Repository git,
+			ObjectInserter inserter) throws IOException, NoOp {
+		// The tree was not passed in, or there are multiple proposals
+		// each needing their own commit. Reset the tree and replay each
+		// proposal in order as individual commits.
+		ObjectId lastIndex = acceptedOldIndex;
+		ObjectId oldTreeId;
+		RefTree tree;
+		if (ObjectId.zeroId().equals(lastIndex)) {
+			oldTreeId = ObjectId.zeroId();
+			tree = RefTree.newEmptyTree();
+		} else {
+			try (RevWalk rw = new RevWalk(git)) {
+				RevCommit c = rw.parseCommit(lastIndex);
+				oldTreeId = c.getTree();
+				tree = RefTree.read(rw.getObjectReader(), c.getTree());
+			}
+		}
+
+		PersonIdent committer = leader.getSystem().newCommitter();
+		for (Proposal p : todo) {
+			if (!tree.apply(p.getCommands())) {
+				// This should not occur, previously during queuing the
+				// commands were successfully applied to the pending tree.
+				// Abort the entire round.
+				throw new IOException(
+						KetchText.get().queuedProposalFailedToApply);
+			}
+
+			ObjectId treeId = tree.writeTree(inserter);
+			if (treeId.equals(oldTreeId)) {
+				continue;
+			}
+
+			CommitBuilder b = new CommitBuilder();
+			b.setTreeId(treeId);
+			if (!ObjectId.zeroId().equals(lastIndex)) {
+				b.setParentId(lastIndex);
+			}
+			b.setAuthor(p.getAuthor() != null ? p.getAuthor() : committer);
+			b.setCommitter(committer);
+			b.setMessage(message(p));
+			lastIndex = inserter.insert(b);
+		}
+		if (lastIndex.equals(acceptedOldIndex)) {
+			throw new NoOp();
+		}
+		return lastIndex;
+	}
+
+	private String message(Proposal p) {
+		StringBuilder m = new StringBuilder();
+		String msg = p.getMessage();
+		if (msg != null && !msg.isEmpty()) {
+			m.append(msg);
+			while (m.length() < 2 || m.charAt(m.length() - 2) != '\n'
+					|| m.charAt(m.length() - 1) != '\n') {
+				m.append('\n');
+			}
+		}
+		m.append(KetchConstants.TERM.getName())
+				.append(": ") //$NON-NLS-1$
+				.append(leader.getTerm());
+		return m.toString();
+	}
+
+	void abort() {
+		for (Proposal p : todo) {
+			p.abort();
+		}
+	}
+
+	void success() {
+		for (Proposal p : todo) {
+			p.success();
+		}
+	}
+
+	private List<ReceiveCommand> makeStageList(Repository git,
+			ObjectInserter inserter) throws IOException {
+		// For each branch, collapse consecutive updates to only most recent,
+		// avoiding sending multiple objects in a rapid fast-forward chain, or
+		// rewritten content.
+		Map<String, ObjectId> byRef = new HashMap<>();
+		for (Proposal p : todo) {
+			for (Command c : p.getCommands()) {
+				Ref n = c.getNewRef();
+				if (n != null && !n.isSymbolic()) {
+					byRef.put(n.getName(), n.getObjectId());
+				}
+			}
+		}
+		if (byRef.isEmpty()) {
+			return Collections.emptyList();
+		}
+
+		Set<ObjectId> newObjs = new HashSet<>(byRef.values());
+		StageBuilder b = new StageBuilder(
+				leader.getSystem().getTxnStage(),
+				acceptedNewIndex);
+		return b.makeStageList(newObjs, git, inserter);
+	}
+
+
+	private static class NoOp extends Exception {
+		private static final long serialVersionUID = 1L;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
new file mode 100644
index 0000000..6f4a178
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod.ALL_REFS;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NODELETE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+import static org.eclipse.jgit.lib.Ref.Storage.NETWORK;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.PushConnection;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Representation of a Git repository on a remote replica system.
+ * <p>
+ * {@link KetchLeader} will contact the replica using the Git wire protocol.
+ * <p>
+ * The remote replica may be fully Ketch-aware, or a standard Git server.
+ */
+public class RemoteGitReplica extends KetchReplica {
+	private final URIish uri;
+	private final RemoteConfig remoteConfig;
+
+	/**
+	 * Configure a new remote.
+	 *
+	 * @param leader
+	 *            instance this replica follows.
+	 * @param name
+	 *            unique-ish name identifying this remote for debugging.
+	 * @param uri
+	 *            URI to connect to the follower's repository.
+	 * @param cfg
+	 *            how Ketch should treat the remote system.
+	 * @param rc
+	 *            optional remote configuration describing how to contact the
+	 *            peer repository.
+	 */
+	public RemoteGitReplica(KetchLeader leader, String name, URIish uri,
+			ReplicaConfig cfg, @Nullable RemoteConfig rc) {
+		super(leader, name, cfg);
+		this.uri = uri;
+		this.remoteConfig = rc;
+	}
+
+	/** @return URI to contact the remote peer repository. */
+	public URIish getURI() {
+		return uri;
+	}
+
+	/** @return optional configuration describing how to contact the peer. */
+	@Nullable
+	protected RemoteConfig getRemoteConfig() {
+		return remoteConfig;
+	}
+
+	@Override
+	protected String describeForLog() {
+		return String.format("%s @ %s", getName(), getURI()); //$NON-NLS-1$
+	}
+
+	@Override
+	protected void startPush(final ReplicaPushRequest req) {
+		getSystem().getExecutor().execute(new Runnable() {
+			@Override
+			public void run() {
+				try (Repository git = getLeader().openRepository()) {
+					try {
+						push(git, req);
+						req.done(git);
+					} catch (Throwable err) {
+						req.setException(git, err);
+					}
+				} catch (IOException err) {
+					req.setException(null, err);
+				}
+			}
+		});
+	}
+
+	private void push(Repository repo, ReplicaPushRequest req)
+			throws NotSupportedException, TransportException, IOException {
+		Map<String, Ref> adv;
+		List<RemoteCommand> cmds = asUpdateList(req.getCommands());
+		try (Transport transport = Transport.open(repo, uri)) {
+			RemoteConfig rc = getRemoteConfig();
+			if (rc != null) {
+				transport.applyConfig(rc);
+			}
+			transport.setPushAtomic(true);
+			adv = push(repo, transport, cmds);
+		}
+		for (RemoteCommand c : cmds) {
+			c.copyStatusToResult();
+		}
+		req.setRefs(adv);
+	}
+
+	private Map<String, Ref> push(Repository git, Transport transport,
+			List<RemoteCommand> cmds) throws IOException {
+		Map<String, RemoteRefUpdate> updates = asUpdateMap(cmds);
+		try (PushConnection connection = transport.openPush()) {
+			Map<String, Ref> adv = connection.getRefsMap();
+			RemoteRefUpdate accepted = updates.get(getSystem().getTxnAccepted());
+			if (accepted != null && !isExpectedValue(adv, accepted)) {
+				abort(cmds);
+				return adv;
+			}
+
+			RemoteRefUpdate committed = updates.get(getSystem().getTxnCommitted());
+			if (committed != null && !isExpectedValue(adv, committed)) {
+				abort(cmds);
+				return adv;
+			}
+			if (committed != null && getCommitMethod() == ALL_REFS) {
+				prepareCommit(git, cmds, updates, adv,
+						committed.getNewObjectId());
+			}
+
+			connection.push(NullProgressMonitor.INSTANCE, updates);
+			return adv;
+		}
+	}
+
+	private static boolean isExpectedValue(Map<String, Ref> adv,
+			RemoteRefUpdate u) {
+		Ref r = adv.get(u.getRemoteName());
+		if (!AnyObjectId.equals(getId(r), u.getExpectedOldObjectId())) {
+			((RemoteCommand) u).cmd.setResult(LOCK_FAILURE);
+			return false;
+		}
+		return true;
+	}
+
+	private void prepareCommit(Repository git, List<RemoteCommand> cmds,
+			Map<String, RemoteRefUpdate> updates, Map<String, Ref> adv,
+			ObjectId committed) throws IOException {
+		for (ReceiveCommand cmd : prepareCommit(git, adv, committed)) {
+			RemoteCommand c = new RemoteCommand(cmd);
+			cmds.add(c);
+			updates.put(c.getRemoteName(), c);
+		}
+	}
+
+	private static List<RemoteCommand> asUpdateList(
+			Collection<ReceiveCommand> cmds) {
+		try {
+			List<RemoteCommand> toPush = new ArrayList<>(cmds.size());
+			for (ReceiveCommand cmd : cmds) {
+				toPush.add(new RemoteCommand(cmd));
+			}
+			return toPush;
+		} catch (IOException e) {
+			// Cannot occur as no IO was required to build the command.
+			throw new IllegalStateException(e);
+		}
+	}
+
+	private static Map<String, RemoteRefUpdate> asUpdateMap(
+			List<RemoteCommand> cmds) {
+		Map<String, RemoteRefUpdate> m = new LinkedHashMap<>();
+		for (RemoteCommand cmd : cmds) {
+			m.put(cmd.getRemoteName(), cmd);
+		}
+		return m;
+	}
+
+	private static void abort(List<RemoteCommand> cmds) {
+		List<ReceiveCommand> tmp = new ArrayList<>(cmds.size());
+		for (RemoteCommand cmd : cmds) {
+			tmp.add(cmd.cmd);
+		}
+		ReceiveCommand.abort(tmp);
+	}
+
+	protected void blockingFetch(Repository repo, ReplicaFetchRequest req)
+			throws NotSupportedException, TransportException {
+		try (Transport transport = Transport.open(repo, uri)) {
+			RemoteConfig rc = getRemoteConfig();
+			if (rc != null) {
+				transport.applyConfig(rc);
+			}
+			fetch(transport, req);
+		}
+	}
+
+	private void fetch(Transport transport, ReplicaFetchRequest req)
+			throws NotSupportedException, TransportException {
+		try (FetchConnection conn = transport.openFetch()) {
+			Map<String, Ref> remoteRefs = conn.getRefsMap();
+			req.setRefs(remoteRefs);
+
+			List<Ref> want = new ArrayList<>();
+			for (String name : req.getWantRefs()) {
+				Ref ref = remoteRefs.get(name);
+				if (ref != null && ref.getObjectId() != null) {
+					want.add(ref);
+				}
+			}
+			for (ObjectId id : req.getWantObjects()) {
+				want.add(new ObjectIdRef.Unpeeled(NETWORK, id.name(), id));
+			}
+
+			conn.fetch(NullProgressMonitor.INSTANCE, want,
+					Collections.<ObjectId> emptySet());
+		}
+	}
+
+	static class RemoteCommand extends RemoteRefUpdate {
+		final ReceiveCommand cmd;
+
+		RemoteCommand(ReceiveCommand cmd) throws IOException {
+			super(null, null,
+					cmd.getNewId(), cmd.getRefName(),
+					true /* force update */,
+					null /* no local tracking ref */,
+					cmd.getOldId());
+			this.cmd = cmd;
+		}
+
+		void copyStatusToResult() {
+			if (cmd.getResult() == NOT_ATTEMPTED) {
+				switch (getStatus()) {
+				case OK:
+				case UP_TO_DATE:
+				case NON_EXISTING:
+					cmd.setResult(OK);
+					break;
+
+				case REJECTED_NODELETE:
+					cmd.setResult(REJECTED_NODELETE);
+					break;
+
+				case REJECTED_NONFASTFORWARD:
+					cmd.setResult(REJECTED_NONFASTFORWARD);
+					break;
+
+				case REJECTED_OTHER_REASON:
+					cmd.setResult(REJECTED_OTHER_REASON, getMessage());
+					break;
+
+				default:
+					cmd.setResult(REJECTED_OTHER_REASON, getStatus().name());
+					break;
+				}
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java
new file mode 100644
index 0000000..e16e63a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaConfig.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_COMMIT;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_SPEED;
+import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_TYPE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_REMOTE;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jgit.internal.ketch.KetchReplica.CommitMethod;
+import org.eclipse.jgit.internal.ketch.KetchReplica.CommitSpeed;
+import org.eclipse.jgit.internal.ketch.KetchReplica.Participation;
+import org.eclipse.jgit.lib.Config;
+
+/** Configures a {@link KetchReplica}. */
+public class ReplicaConfig {
+	/**
+	 * Read a configuration from a config block.
+	 *
+	 * @param cfg
+	 *            configuration to read.
+	 * @param name
+	 *            of the replica being configured.
+	 * @return replica configuration for {@code name}.
+	 */
+	public static ReplicaConfig newFromConfig(Config cfg, String name) {
+		return new ReplicaConfig().fromConfig(cfg, name);
+	}
+
+	private Participation participation = Participation.FULL;
+	private CommitMethod commitMethod = CommitMethod.ALL_REFS;
+	private CommitSpeed commitSpeed = CommitSpeed.BATCHED;
+	private long minRetry = SECONDS.toMillis(5);
+	private long maxRetry = MINUTES.toMillis(1);
+
+	/** @return participation of the replica in the system. */
+	public Participation getParticipation() {
+		return participation;
+	}
+
+	/** @return how Ketch should apply committed changes. */
+	public CommitMethod getCommitMethod() {
+		return commitMethod;
+	}
+
+	/** @return how quickly should Ketch commit. */
+	public CommitSpeed getCommitSpeed() {
+		return commitSpeed;
+	}
+
+	/**
+	 * Returns the minimum wait delay before retrying a failure.
+	 *
+	 * @param unit
+	 *            to get retry delay in.
+	 * @return minimum delay before retrying a failure.
+	 */
+	public long getMinRetry(TimeUnit unit) {
+		return unit.convert(minRetry, MILLISECONDS);
+	}
+
+	/**
+	 * Returns the maximum wait delay before retrying a failure.
+	 *
+	 * @param unit
+	 *            to get retry delay in.
+	 * @return maximum delay before retrying a failure.
+	 */
+	public long getMaxRetry(TimeUnit unit) {
+		return unit.convert(maxRetry, MILLISECONDS);
+	}
+
+	/**
+	 * Update the configuration from a config block.
+	 *
+	 * @param cfg
+	 *            configuration to read.
+	 * @param name
+	 *            of the replica being configured.
+	 * @return {@code this}
+	 */
+	public ReplicaConfig fromConfig(Config cfg, String name) {
+		participation = cfg.getEnum(
+				CONFIG_KEY_REMOTE, name, CONFIG_KEY_TYPE,
+				participation);
+		commitMethod = cfg.getEnum(
+				CONFIG_KEY_REMOTE, name, CONFIG_KEY_COMMIT,
+				commitMethod);
+		commitSpeed = cfg.getEnum(
+				CONFIG_KEY_REMOTE, name, CONFIG_KEY_SPEED,
+				commitSpeed);
+		minRetry = getMillis(cfg, name, "ketch-minRetry", minRetry); //$NON-NLS-1$
+		maxRetry = getMillis(cfg, name, "ketch-maxRetry", maxRetry); //$NON-NLS-1$
+		return this;
+	}
+
+	private static long getMillis(Config cfg, String name, String key,
+			long defaultValue) {
+		String valStr = cfg.getString(CONFIG_KEY_REMOTE, name, key);
+		if (valStr == null) {
+			return defaultValue;
+		}
+
+		valStr = valStr.trim();
+		if (valStr.isEmpty()) {
+			return defaultValue;
+		}
+
+		Matcher m = UnitMap.PATTERN.matcher(valStr);
+		if (!m.matches()) {
+			return defaultValue;
+		}
+
+		String digits = m.group(1);
+		String unitName = m.group(2).trim();
+		TimeUnit unit = UnitMap.UNITS.get(unitName);
+		if (unit == null) {
+			return defaultValue;
+		}
+
+		try {
+			if (digits.indexOf('.') == -1) {
+				return unit.toMillis(Long.parseLong(digits));
+			}
+
+			double val = Double.parseDouble(digits);
+			return (long) (val * unit.toMillis(1));
+		} catch (NumberFormatException nfe) {
+			return defaultValue;
+		}
+	}
+
+	static class UnitMap {
+		static final Pattern PATTERN = Pattern
+				.compile("^([1-9][0-9]*(?:\\.[0-9]*)?)\\s*(.*)$"); //$NON-NLS-1$
+
+		static final Map<String, TimeUnit> UNITS;
+
+		static {
+			Map<String, TimeUnit> m = new HashMap<>();
+			TimeUnit u = MILLISECONDS;
+			m.put("", u); //$NON-NLS-1$
+			m.put("ms", u); //$NON-NLS-1$
+			m.put("millis", u); //$NON-NLS-1$
+			m.put("millisecond", u); //$NON-NLS-1$
+			m.put("milliseconds", u); //$NON-NLS-1$
+
+			u = SECONDS;
+			m.put("s", u); //$NON-NLS-1$
+			m.put("sec", u); //$NON-NLS-1$
+			m.put("secs", u); //$NON-NLS-1$
+			m.put("second", u); //$NON-NLS-1$
+			m.put("seconds", u); //$NON-NLS-1$
+
+			u = MINUTES;
+			m.put("m", u); //$NON-NLS-1$
+			m.put("min", u); //$NON-NLS-1$
+			m.put("mins", u); //$NON-NLS-1$
+			m.put("minute", u); //$NON-NLS-1$
+			m.put("minutes", u); //$NON-NLS-1$
+
+			u = HOURS;
+			m.put("h", u); //$NON-NLS-1$
+			m.put("hr", u); //$NON-NLS-1$
+			m.put("hrs", u); //$NON-NLS-1$
+			m.put("hour", u); //$NON-NLS-1$
+			m.put("hours", u); //$NON-NLS-1$
+
+			u = DAYS;
+			m.put("d", u); //$NON-NLS-1$
+			m.put("day", u); //$NON-NLS-1$
+			m.put("days", u); //$NON-NLS-1$
+
+			UNITS = Collections.unmodifiableMap(m);
+		}
+
+		private UnitMap() {
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java
new file mode 100644
index 0000000..201d9e9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaFetchRequest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+
+/** A fetch request to obtain objects from a replica, and its result. */
+public class ReplicaFetchRequest {
+	private final Set<String> wantRefs;
+	private final Set<ObjectId> wantObjects;
+	private Map<String, Ref> refs;
+
+	/**
+	 * Construct a new fetch request for a replica.
+	 *
+	 * @param wantRefs
+	 *            named references to be fetched.
+	 * @param wantObjects
+	 *            specific objects to be fetched.
+	 */
+	public ReplicaFetchRequest(Set<String> wantRefs,
+			Set<ObjectId> wantObjects) {
+		this.wantRefs = wantRefs;
+		this.wantObjects = wantObjects;
+	}
+
+	/** @return references to be fetched. */
+	public Set<String> getWantRefs() {
+		return wantRefs;
+	}
+
+	/** @return objects to be fetched. */
+	public Set<ObjectId> getWantObjects() {
+		return wantObjects;
+	}
+
+	/** @return remote references, usually from the advertisement. */
+	@Nullable
+	public Map<String, Ref> getRefs() {
+		return refs;
+	}
+
+	/**
+	 * @param refs
+	 *            references observed from the replica.
+	 */
+	public void setRefs(Map<String, Ref> refs) {
+		this.refs = refs;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java
new file mode 100644
index 0000000..691b142
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaPushRequest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * A push request sending objects to a replica, and its result.
+ * <p>
+ * Implementors of {@link KetchReplica} must populate the command result fields,
+ * {@link #setRefs(Map)}, and call one of
+ * {@link #setException(Repository, Throwable)} or {@link #done(Repository)} to
+ * finish processing.
+ */
+public class ReplicaPushRequest {
+	private final KetchReplica replica;
+	private final Collection<ReceiveCommand> commands;
+	private Map<String, Ref> refs;
+	private Throwable exception;
+	private boolean notified;
+
+	/**
+	 * Construct a new push request for a replica.
+	 *
+	 * @param replica
+	 *            the replica being pushed to.
+	 * @param commands
+	 *            commands to be executed.
+	 */
+	public ReplicaPushRequest(KetchReplica replica,
+			Collection<ReceiveCommand> commands) {
+		this.replica = replica;
+		this.commands = commands;
+	}
+
+	/** @return commands to be executed, and their results. */
+	public Collection<ReceiveCommand> getCommands() {
+		return commands;
+	}
+
+	/** @return remote references, usually from the advertisement. */
+	@Nullable
+	public Map<String, Ref> getRefs() {
+		return refs;
+	}
+
+	/**
+	 * @param refs
+	 *            references observed from the replica.
+	 */
+	public void setRefs(Map<String, Ref> refs) {
+		this.refs = refs;
+	}
+
+	/** @return exception thrown, if any. */
+	@Nullable
+	public Throwable getException() {
+		return exception;
+	}
+
+	/**
+	 * Mark the request as crashing with a communication error.
+	 * <p>
+	 * This method may take significant time acquiring the leader lock and
+	 * updating the Ketch state machine with the failure.
+	 *
+	 * @param repo
+	 *            local repository reference used by the push attempt.
+	 * @param err
+	 *            exception thrown during communication.
+	 */
+	public void setException(@Nullable Repository repo, Throwable err) {
+		if (KetchReplica.log.isErrorEnabled()) {
+			KetchReplica.log.error(describe("failed"), err); //$NON-NLS-1$
+		}
+		if (!notified) {
+			notified = true;
+			exception = err;
+			replica.afterPush(repo, this);
+		}
+	}
+
+	/**
+	 * Mark the request as completed without exception.
+	 * <p>
+	 * This method may take significant time acquiring the leader lock and
+	 * updating the Ketch state machine with results from this replica.
+	 *
+	 * @param repo
+	 *            local repository reference used by the push attempt.
+	 */
+	public void done(Repository repo) {
+		if (KetchReplica.log.isDebugEnabled()) {
+			KetchReplica.log.debug(describe("completed")); //$NON-NLS-1$
+		}
+		if (!notified) {
+			notified = true;
+			replica.afterPush(repo, this);
+		}
+	}
+
+	private String describe(String heading) {
+		StringBuilder b = new StringBuilder();
+		b.append("push to "); //$NON-NLS-1$
+		b.append(replica.describeForLog());
+		b.append(' ').append(heading).append(":\n"); //$NON-NLS-1$
+		for (ReceiveCommand cmd : commands) {
+			b.append(String.format(
+					"  %-12s %-12s %s %s", //$NON-NLS-1$
+					LeaderSnapshot.str(cmd.getOldId()),
+					LeaderSnapshot.str(cmd.getNewId()),
+					cmd.getRefName(),
+					cmd.getResult()));
+			if (cmd.getMessage() != null) {
+				b.append(' ').append(cmd.getMessage());
+			}
+			b.append('\n');
+		}
+		return b.toString();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java
new file mode 100644
index 0000000..8c3de02
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ReplicaSnapshot.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import java.util.Date;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * A snapshot of a replica.
+ *
+ * @see LeaderSnapshot
+ */
+public class ReplicaSnapshot {
+	final KetchReplica replica;
+	ObjectId accepted;
+	ObjectId committed;
+	KetchReplica.State state;
+	String error;
+	long retryAtMillis;
+
+	ReplicaSnapshot(KetchReplica replica) {
+		this.replica = replica;
+	}
+
+	/** @return the replica this snapshot describes the state of. */
+	public KetchReplica getReplica() {
+		return replica;
+	}
+
+	/** @return current state of the replica. */
+	public KetchReplica.State getState() {
+		return state;
+	}
+
+	/** @return last known Git commit at {@code refs/txn/accepted}. */
+	@Nullable
+	public ObjectId getAccepted() {
+		return accepted;
+	}
+
+	/** @return last known Git commit at {@code refs/txn/committed}. */
+	@Nullable
+	public ObjectId getCommitted() {
+		return committed;
+	}
+
+	/**
+	 * @return if {@link #getState()} == {@link KetchReplica.State#OFFLINE} an
+	 *         optional human-readable message from the transport system
+	 *         explaining the failure.
+	 */
+	@Nullable
+	public String getErrorMessage() {
+		return error;
+	}
+
+	/**
+	 * @return time (usually in the future) when the leader will retry
+	 *         communication with the offline or lagging replica; null if no
+	 *         retry is scheduled or necessary.
+	 */
+	@Nullable
+	public Date getRetryAt() {
+		return retryAtMillis > 0 ? new Date(retryAtMillis) : null;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java
new file mode 100644
index 0000000..1335b85
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/Round.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/**
+ * One round-trip to all replicas proposing a log entry.
+ * <p>
+ * In Raft a log entry represents a state transition at a specific index in the
+ * replicated log. The leader can only append log entries to the log.
+ * <p>
+ * In Ketch a log entry is recorded under the {@code refs/txn} namespace. This
+ * occurs when:
+ * <ul>
+ * <li>a replica wants to establish itself as a new leader by proposing a new
+ * term (see {@link ElectionRound})
+ * <li>an established leader wants to gain consensus on new {@link Proposal}s
+ * (see {@link ProposalRound})
+ * </ul>
+ */
+abstract class Round {
+	final KetchLeader leader;
+	final LogIndex acceptedOldIndex;
+	LogIndex acceptedNewIndex;
+	List<ReceiveCommand> stageCommands;
+
+	Round(KetchLeader leader, LogIndex head) {
+		this.leader = leader;
+		this.acceptedOldIndex = head;
+	}
+
+	/**
+	 * Creates a commit for {@code refs/txn/accepted} and calls
+	 * {@link #runAsync(AnyObjectId)} to begin execution of the round across
+	 * the system.
+	 * <p>
+	 * If references are being updated (such as in a {@link ProposalRound}) the
+	 * RefTree may be modified.
+	 * <p>
+	 * Invoked without {@link KetchLeader#lock} to build objects.
+	 *
+	 * @throws IOException
+	 *             the round cannot build new objects within the leader's
+	 *             repository. The leader may be unable to execute.
+	 */
+	abstract void start() throws IOException;
+
+	/**
+	 * Asynchronously distribute the round's new value for
+	 * {@code refs/txn/accepted} to all replicas.
+	 * <p>
+	 * Invoked by {@link #start()} after new commits have been created for the
+	 * log. The method passes {@code newId} to {@link KetchLeader} to be
+	 * distributed to all known replicas.
+	 *
+	 * @param newId
+	 *            new value for {@code refs/txn/accepted}.
+	 */
+	void runAsync(AnyObjectId newId) {
+		acceptedNewIndex = acceptedOldIndex.nextIndex(newId);
+		leader.runAsync(this);
+	}
+
+	/**
+	 * Notify the round it was accepted by a majority of the system.
+	 * <p>
+	 * Invoked by the leader with {@link KetchLeader#lock} held by the caller.
+	 */
+	abstract void success();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java
new file mode 100644
index 0000000..61871a4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/StageBuilder.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.ketch;
+
+import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.treewalk.EmptyTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+
+/** Constructs a set of commands to stage content during a proposal. */
+public class StageBuilder {
+	/**
+	 * Acceptable number of references to send in a single stage transaction.
+	 * <p>
+	 * If the number of unique objects exceeds this amount the builder will
+	 * attempt to decrease the reference count by chaining commits..
+	 */
+	private static final int SMALL_BATCH_SIZE = 5;
+
+	/**
+	 * Acceptable number of commits to chain together using parent pointers.
+	 * <p>
+	 * When staging many unique commits the {@link StageBuilder} batches
+	 * together unrelated commits as parents of a temporary commit. After the
+	 * proposal completes the temporary commit is discarded and can be garbage
+	 * collected by all replicas.
+	 */
+	private static final int TEMP_PARENT_BATCH_SIZE = 128;
+
+	private static final byte[] PEEL = { ' ', '^' };
+
+	private final String txnStage;
+	private final String txnId;
+
+	/**
+	 * Construct a stage builder for a transaction.
+	 *
+	 * @param txnStageNamespace
+	 *            namespace for transaction references to build
+	 *            {@code "txnStageNamespace/txnId.n"} style names.
+	 * @param txnId
+	 *            identifier used to name temporary staging refs.
+	 */
+	public StageBuilder(String txnStageNamespace, ObjectId txnId) {
+		this.txnStage = txnStageNamespace;
+		this.txnId = txnId.name();
+	}
+
+	/**
+	 * Compare two RefTrees and return commands to stage new objects.
+	 * <p>
+	 * This method ignores the lineage between the two RefTrees and does a
+	 * straight diff on the two trees. New objects will be staged. The diff
+	 * strategy is useful to catch-up a lagging replica, without sending every
+	 * intermediate step. This may mean the replica does not have the same
+	 * object set as other replicas if there are rewinds or branch deletes.
+	 *
+	 * @param git
+	 *            source repository to read {@code oldTree} and {@code newTree}
+	 *            from.
+	 * @param oldTree
+	 *            accepted RefTree on the replica ({@code refs/txn/accepted}).
+	 *            Use {@link ObjectId#zeroId()} if the remote does not have any
+	 *            ref tree, e.g. a new replica catching up.
+	 * @param newTree
+	 *            RefTree being sent to the replica. The trees will be compared.
+	 * @return list of commands to create {@code "refs/txn/stage/..."}
+	 *         references on replicas anchoring new objects into the repository
+	 *         while a transaction gains consensus.
+	 * @throws IOException
+	 *             {@code git} cannot be accessed to compare {@code oldTree} and
+	 *             {@code newTree} to build the object set.
+	 */
+	public List<ReceiveCommand> makeStageList(Repository git, ObjectId oldTree,
+			ObjectId newTree) throws IOException {
+		try (RevWalk rw = new RevWalk(git);
+				TreeWalk tw = new TreeWalk(rw.getObjectReader());
+				ObjectInserter ins = git.newObjectInserter()) {
+			if (AnyObjectId.equals(oldTree, ObjectId.zeroId())) {
+				tw.addTree(new EmptyTreeIterator());
+			} else {
+				tw.addTree(rw.parseTree(oldTree));
+			}
+			tw.addTree(rw.parseTree(newTree));
+			tw.setFilter(TreeFilter.ANY_DIFF);
+			tw.setRecursive(true);
+
+			Set<ObjectId> newObjs = new HashSet<>();
+			while (tw.next()) {
+				if (tw.getRawMode(1) == TYPE_GITLINK
+						&& !tw.isPathSuffix(PEEL, 2)) {
+					newObjs.add(tw.getObjectId(1));
+				}
+			}
+
+			List<ReceiveCommand> cmds = makeStageList(newObjs, git, ins);
+			ins.flush();
+			return cmds;
+		}
+	}
+
+	/**
+	 * Construct a set of commands to stage objects on a replica.
+	 *
+	 * @param newObjs
+	 *            objects to send to a replica.
+	 * @param git
+	 *            local repository to read source objects from. Required to
+	 *            perform minification of {@code newObjs}.
+	 * @param inserter
+	 *            inserter to write temporary commit objects during minification
+	 *            if many new branches are created by {@code newObjs}.
+	 * @return list of commands to create {@code "refs/txn/stage/..."}
+	 *         references on replicas anchoring {@code newObjs} into the
+	 *         repository while a transaction gains consensus.
+	 * @throws IOException
+	 *             {@code git} cannot be accessed to perform minification of
+	 *             {@code newObjs}.
+	 */
+	public List<ReceiveCommand> makeStageList(Set<ObjectId> newObjs,
+			@Nullable Repository git, @Nullable ObjectInserter inserter)
+					throws IOException {
+		if (git == null || newObjs.size() <= SMALL_BATCH_SIZE) {
+			// Without a source repository can only construct unique set.
+			List<ReceiveCommand> cmds = new ArrayList<>(newObjs.size());
+			for (ObjectId id : newObjs) {
+				stage(cmds, id);
+			}
+			return cmds;
+		}
+
+		List<ReceiveCommand> cmds = new ArrayList<>();
+		List<RevCommit> commits = new ArrayList<>();
+		reduceObjects(cmds, commits, git, newObjs);
+
+		if (inserter == null || commits.size() <= 1
+				|| (cmds.size() + commits.size()) <= SMALL_BATCH_SIZE) {
+			// Without an inserter to aggregate commits, or for a small set of
+			// commits just send one stage ref per commit.
+			for (RevCommit c : commits) {
+				stage(cmds, c.copy());
+			}
+			return cmds;
+		}
+
+		// 'commits' is sorted most recent to least recent commit.
+		// Group batches of commits and build a chain.
+		// TODO(sop) Cluster by restricted graphs to support filtering.
+		ObjectId tip = null;
+		for (int end = commits.size(); end > 0;) {
+			int start = Math.max(0, end - TEMP_PARENT_BATCH_SIZE);
+			List<RevCommit> batch = commits.subList(start, end);
+			List<ObjectId> parents = new ArrayList<>(1 + batch.size());
+			if (tip != null) {
+				parents.add(tip);
+			}
+			parents.addAll(batch);
+
+			CommitBuilder b = new CommitBuilder();
+			b.setTreeId(batch.get(0).getTree());
+			b.setParentIds(parents);
+			b.setAuthor(tmpAuthor(batch));
+			b.setCommitter(b.getAuthor());
+			tip = inserter.insert(b);
+			end = start;
+		}
+		stage(cmds, tip);
+		return cmds;
+	}
+
+	private static PersonIdent tmpAuthor(List<RevCommit> commits) {
+		// Construct a predictable author using most recent commit time.
+		int t = 0;
+		for (int i = 0; i < commits.size();) {
+			t = Math.max(t, commits.get(i).getCommitTime());
+		}
+		String name = "Ketch Stage"; //$NON-NLS-1$
+		String email = "tmp@tmp"; //$NON-NLS-1$
+		return new PersonIdent(name, email, t * 1000L, 0);
+	}
+
+	private void reduceObjects(List<ReceiveCommand> cmds,
+			List<RevCommit> commits, Repository git,
+			Set<ObjectId> newObjs) throws IOException {
+		try (RevWalk rw = new RevWalk(git)) {
+			rw.setRetainBody(false);
+
+			for (ObjectId id : newObjs) {
+				RevObject obj = rw.parseAny(id);
+				if (obj instanceof RevCommit) {
+					rw.markStart((RevCommit) obj);
+				} else {
+					stage(cmds, id);
+				}
+			}
+
+			for (RevCommit c; (c = rw.next()) != null;) {
+				commits.add(c);
+				rw.markUninteresting(c);
+			}
+		}
+	}
+
+	private void stage(List<ReceiveCommand> cmds, ObjectId id) {
+		int estLen = txnStage.length() + txnId.length() + 5;
+		StringBuilder n = new StringBuilder(estLen);
+		n.append(txnStage).append(txnId).append('.');
+		n.append(Integer.toHexString(cmds.size()));
+		cmds.add(new ReceiveCommand(ObjectId.zeroId(), id, n.toString()));
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java
new file mode 100644
index 0000000..dfe0375
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Distributed consensus system built on Git.
+ */
+package org.eclipse.jgit.internal.ketch;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index 2e170a5..ecd4b23 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -185,14 +185,6 @@
 		for (int i = 0; i < loadLocks.length; i++)
 			loadLocks[i] = new ReentrantLock(true /* fair */);
 
-		int eb = (int) (tableSize * .1);
-		if (64 < eb)
-			eb = 64;
-		else if (eb < 4)
-			eb = 4;
-		if (tableSize < eb)
-			eb = tableSize;
-
 		maxBytes = cfg.getBlockLimit();
 		maxStreamThroughCache = (long) (maxBytes * cfg.getStreamRatio());
 		blockSize = cfg.getBlockSize();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index 784507d..33be3b1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -194,7 +194,7 @@
 			refdb.refresh();
 			objdb.clearCache();
 
-			Collection<Ref> refsBefore = RefTreeNames.allRefs(refdb);
+			Collection<Ref> refsBefore = getAllRefs();
 			packsBefore = packsToRebuild();
 			if (packsBefore.isEmpty())
 				return true;
@@ -235,6 +235,18 @@
 		}
 	}
 
+	private Collection<Ref> getAllRefs() throws IOException {
+		Collection<Ref> refs = refdb.getRefs(RefDatabase.ALL).values();
+		List<Ref> addl = refdb.getAdditionalRefs();
+		if (!addl.isEmpty()) {
+			List<Ref> all = new ArrayList<>(refs.size() + addl.size());
+			all.addAll(refs);
+			all.addAll(addl);
+			return all;
+		}
+		return refs;
+	}
+
 	private List<DfsPackFile> packsToRebuild() throws IOException {
 		DfsPackFile[] packs = objdb.getPacks();
 		List<DfsPackFile> out = new ArrayList<DfsPackFile>(packs.length);
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 2ce0d47..9cdb753 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
@@ -111,6 +111,8 @@
 public class GC {
 	private static final String PRUNE_EXPIRE_DEFAULT = "2.weeks.ago"; //$NON-NLS-1$
 
+	private static final String PRUNE_PACK_EXPIRE_DEFAULT = "1.hour.ago"; //$NON-NLS-1$
+
 	private final FileRepository repo;
 
 	private ProgressMonitor pm;
@@ -119,6 +121,10 @@
 
 	private Date expire;
 
+	private long packExpireAgeMillis = -1;
+
+	private Date packExpire;
+
 	private PackConfig pconfig = null;
 
 	/**
@@ -186,7 +192,7 @@
 	 */
 	private void deleteOldPacks(Collection<PackFile> oldPacks,
 			Collection<PackFile> newPacks) throws ParseException {
-		long expireDate = getExpireDate();
+		long packExpireDate = getPackExpireDate();
 		oldPackLoop: for (PackFile oldPack : oldPacks) {
 			String oldName = oldPack.getPackName();
 			// check whether an old pack file is also among the list of new
@@ -196,7 +202,7 @@
 					continue oldPackLoop;
 
 			if (!oldPack.shouldBeKept()
-					&& oldPack.getPackFile().lastModified() < expireDate) {
+					&& oldPack.getPackFile().lastModified() < packExpireDate) {
 				oldPack.close();
 				prunePack(oldName);
 			}
@@ -449,6 +455,26 @@
 		return expireDate;
 	}
 
+	private long getPackExpireDate() throws ParseException {
+		long packExpireDate = Long.MAX_VALUE;
+
+		if (packExpire == null && packExpireAgeMillis == -1) {
+			String prunePackExpireStr = repo.getConfig().getString(
+					ConfigConstants.CONFIG_GC_SECTION, null,
+					ConfigConstants.CONFIG_KEY_PRUNEPACKEXPIRE);
+			if (prunePackExpireStr == null)
+				prunePackExpireStr = PRUNE_PACK_EXPIRE_DEFAULT;
+			packExpire = GitDateParser.parse(prunePackExpireStr, null,
+					SystemReader.getInstance().getLocale());
+			packExpireAgeMillis = -1;
+		}
+		if (packExpire != null)
+			packExpireDate = packExpire.getTime();
+		if (packExpireAgeMillis != -1)
+			packExpireDate = System.currentTimeMillis() - packExpireAgeMillis;
+		return packExpireDate;
+	}
+
 	/**
 	 * Remove all entries from a map which key is the id of an object referenced
 	 * by the given ObjectWalk
@@ -629,15 +655,16 @@
 	}
 
 	/**
-	 * Returns a map of all refs and additional refs (e.g. FETCH_HEAD,
+	 * Returns a collection of all refs and additional refs (e.g. FETCH_HEAD,
 	 * MERGE_HEAD, ...)
 	 *
-	 * @return a map where names of refs point to ref objects
+	 * @return a collection of refs pointing to live objects.
 	 * @throws IOException
 	 */
 	private Collection<Ref> getAllRefs() throws IOException {
-		Collection<Ref> refs = RefTreeNames.allRefs(repo.getRefDatabase());
-		List<Ref> addl = repo.getRefDatabase().getAdditionalRefs();
+		RefDatabase refdb = repo.getRefDatabase();
+		Collection<Ref> refs = refdb.getRefs(RefDatabase.ALL).values();
+		List<Ref> addl = refdb.getAdditionalRefs();
 		if (!addl.isEmpty()) {
 			List<Ref> all = new ArrayList<>(refs.size() + addl.size());
 			all.addAll(refs);
@@ -982,6 +1009,20 @@
 	}
 
 	/**
+	 * During gc() or prune() packfiles which are created or modified in the
+	 * last <code>packExpireAgeMillis</code> milliseconds will not be deleted.
+	 * Only older packfiles may be deleted. If set to 0 then every packfile is a
+	 * candidate for deletion.
+	 *
+	 * @param packExpireAgeMillis
+	 *            minimal age of packfiles to be deleted in milliseconds.
+	 */
+	public void setPackExpireAgeMillis(long packExpireAgeMillis) {
+		this.packExpireAgeMillis = packExpireAgeMillis;
+		expire = null;
+	}
+
+	/**
 	 * Set the PackConfig used when (re-)writing packfiles. This allows to
 	 * influence how packs are written and to implement something similar to
 	 * "git gc --aggressive"
@@ -1010,4 +1051,18 @@
 		this.expire = expire;
 		expireAgeMillis = -1;
 	}
+
+	/**
+	 * During gc() or prune() packfiles which are created or modified after or
+	 * at <code>packExpire</code> will not be deleted. Only older packfiles may
+	 * be deleted. If set to null then every packfile is a candidate for
+	 * deletion.
+	 *
+	 * @param packExpire
+	 *            instant in time which defines packfile expiration
+	 */
+	public void setPackExpire(Date packExpire) {
+		this.packExpire = packExpire;
+		packExpireAgeMillis = -1;
+	}
 }
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 b8c2fb4..4bb2982 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
@@ -203,10 +203,10 @@
 
 	@Override
 	public void close() {
-		// We have no resources to close.
+		clearReferences();
 	}
 
-	void rescan() {
+	private void clearReferences() {
 		looseRefs.set(RefList.<LooseRef> emptyList());
 		packedRefs.set(PackedRefList.NO_PACKED_REFS);
 	}
@@ -214,7 +214,7 @@
 	@Override
 	public void refresh() {
 		super.refresh();
-		rescan();
+		clearReferences();
 	}
 
 	@Override
@@ -312,11 +312,10 @@
 
 	@Override
 	public Map<String, Ref> getRefs(String prefix) throws IOException {
-		final RefList<Ref> packed = getPackedRefs();
 		final RefList<LooseRef> oldLoose = looseRefs.get();
-
 		LooseScanner scan = new LooseScanner(oldLoose);
 		scan.scan(prefix);
+		final RefList<Ref> packed = getPackedRefs();
 
 		RefList<LooseRef> loose;
 		if (scan.newLoose != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
index dc60311..df93ce8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
@@ -43,10 +43,13 @@
 
 package org.eclipse.jgit.internal.storage.reftree;
 
+import static org.eclipse.jgit.lib.Constants.HEAD;
 import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -187,12 +190,19 @@
 
 	@Override
 	public Ref getRef(String name) throws IOException {
-		return findRef(getRefs(ALL), name);
+		String[] needle = new String[SEARCH_PATH.length];
+		for (int i = 0; i < SEARCH_PATH.length; i++) {
+			needle[i] = SEARCH_PATH[i] + name;
+		}
+		return firstExactRef(needle);
 	}
 
 	@Override
 	public Ref exactRef(String name) throws IOException {
-		if (conflictsWithBootstrap(name)) {
+		if (!repo.isBare() && name.indexOf('/') < 0 && !HEAD.equals(name)) {
+			// Pass through names like MERGE_HEAD, ORIG_HEAD, FETCH_HEAD.
+			return bootstrap.exactRef(name);
+		} else if (conflictsWithBootstrap(name)) {
 			return null;
 		}
 
@@ -250,7 +260,23 @@
 
 	@Override
 	public List<Ref> getAdditionalRefs() throws IOException {
-		return Collections.emptyList();
+		Collection<Ref> txnRefs;
+		if (txnNamespace != null) {
+			txnRefs = bootstrap.getRefs(txnNamespace).values();
+		} else {
+			Ref r = bootstrap.exactRef(txnCommitted);
+			if (r != null && r.getObjectId() != null) {
+				txnRefs = Collections.singleton(r);
+			} else {
+				txnRefs = Collections.emptyList();
+			}
+		}
+
+		List<Ref> otherRefs = bootstrap.getAdditionalRefs();
+		List<Ref> all = new ArrayList<>(txnRefs.size() + otherRefs.size());
+		all.addAll(txnRefs);
+		all.addAll(otherRefs);
+		return all;
 	}
 
 	@Override
@@ -293,6 +319,9 @@
 
 	@Override
 	public RefUpdate newUpdate(String name, boolean detach) throws IOException {
+		if (!repo.isBare() && name.indexOf('/') < 0 && !HEAD.equals(name)) {
+			return bootstrap.newUpdate(name, detach);
+		}
 		if (conflictsWithBootstrap(name)) {
 			return new AlwaysFailUpdate(this, name);
 		}
@@ -327,7 +356,13 @@
 			return true;
 		} else if (txnCommitted.equals(name)) {
 			return true;
-		} else if (name.length() > txnCommitted.length()
+		}
+
+		if (name.indexOf('/') < 0 && !HEAD.equals(name)) {
+			return true;
+		}
+
+		if (name.length() > txnCommitted.length()
 				&& name.charAt(txnCommitted.length()) == '/'
 				&& name.startsWith(txnCommitted)) {
 			return true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
index 239a745..c53d6de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
@@ -43,14 +43,6 @@
 
 package org.eclipse.jgit.internal.storage.reftree;
 
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 
 /** Magic reference name logic for RefTrees. */
@@ -92,33 +84,6 @@
 		return false;
 	}
 
-	/**
-	 * Snapshot all references from a RefTreeDatabase and its bootstrap.
-	 * <p>
-	 * There may be name conflicts with multiple {@link Ref} objects containing
-	 * the same name in the returned collection.
-	 *
-	 * @param refdb
-	 *            database instance.
-	 * @return all known references.
-	 * @throws IOException
-	 *             references cannot be enumerated.
-	 */
-	public static Collection<Ref> allRefs(RefDatabase refdb)
-			throws IOException {
-		Collection<Ref> refs = refdb.getRefs(ALL).values();
-		if (!(refdb instanceof RefTreeDatabase)) {
-			return refs;
-		}
-
-		RefDatabase bootstrap = ((RefTreeDatabase) refdb).getBootstrap();
-		Collection<Ref> br = bootstrap.getRefs(ALL).values();
-		List<Ref> all = new ArrayList<>(refs.size() + br.size());
-		all.addAll(refs);
-		all.addAll(br);
-		return all;
-	}
-
 	private RefTreeNames() {
 	}
 }
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 a89bcee..9e3e0b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -107,6 +107,13 @@
 	/** The "autocrlf" key */
 	public static final String CONFIG_KEY_AUTOCRLF = "autocrlf";
 
+	/**
+	 * The "eol" key
+	 *
+	 * @since 4.3
+	 */
+	public static final String CONFIG_KEY_EOL = "eol";
+
 	/** The "bare" key */
 	public static final String CONFIG_KEY_BARE = "bare";
 
@@ -228,6 +235,12 @@
 	 */
 	public static final String CONFIG_KEY_HIDEDOTFILES = "hidedotfiles";
 
+	/**
+	 * The "dirnogitlinks" key
+	 * @since 4.3
+	 */
+	public static final String CONFIG_KEY_DIRNOGITLINKS = "dirNoGitLinks";
+
 	/** The "precomposeunicode" key */
 	public static final String CONFIG_KEY_PRECOMPOSEUNICODE = "precomposeunicode";
 
@@ -235,6 +248,12 @@
 	public static final String CONFIG_KEY_PRUNEEXPIRE = "pruneexpire";
 
 	/**
+	 * The "prunepackexpire" key
+	 * @since 4.3
+	 */
+	public static final String CONFIG_KEY_PRUNEPACKEXPIRE = "prunepackexpire";
+
+	/**
 	 * The "aggressiveDepth" key
 	 * @since 3.6
 	 */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 5a7634a..83efd43 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -76,6 +76,46 @@
 	}
 
 	/**
+	 * Permissible values for {@code core.eol}.
+	 * <p>
+	 * https://git-scm.com/docs/gitattributes
+	 *
+	 * @since 4.3
+	 */
+	public static enum EOL {
+		/** checkin with LF, checkout with CRLF. */
+		CRLF,
+
+		/** checkin with LF, checkout without conversion. */
+		LF,
+
+		/** use the platform's native line ending. */
+		NATIVE;
+	}
+
+	/**
+	 * EOL stream conversion protocol
+	 *
+	 * @since 4.3
+	 */
+	public static enum EolStreamType {
+		/** convert to CRLF without binary detection */
+		TEXT_CRLF,
+
+		/** convert to LF without binary detection */
+		TEXT_LF,
+
+		/** convert to CRLF with binary detection */
+		AUTO_CRLF,
+
+		/** convert to LF with binary detection */
+		AUTO_LF,
+
+		/** do not convert */
+		DIRECT;
+	}
+
+	/**
 	 * Permissible values for {@code core.checkstat}
 	 *
 	 * @since 3.0
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index 4316cd0..c1027f0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -552,7 +552,8 @@
 	 */
 	public Result delete(final RevWalk walk) throws IOException {
 		final String myName = getRef().getLeaf().getName();
-		if (myName.startsWith(Constants.R_HEADS)) {
+		if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) {
+			// Don't allow the currently checked out branch to be deleted.
 			Ref head = getRefDatabase().getRef(Constants.HEAD);
 			while (head != null && head.isSymbolic()) {
 				head = head.getTarget();
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 f826613..ba0dea3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -110,7 +110,8 @@
 		return globalListeners;
 	}
 
-	private final AtomicInteger useCnt = new AtomicInteger(1);
+	/** Use counter */
+	final AtomicInteger useCnt = new AtomicInteger(1);
 
 	/** Metadata directory holding the repository's critical files. */
 	private final File gitDir;
@@ -864,6 +865,7 @@
 	public void close() {
 		if (useCnt.decrementAndGet() == 0) {
 			doClose();
+			RepositoryCache.unregister(this);
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index 23cc264..22b5fcd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -130,10 +130,10 @@
 	}
 
 	/**
-	 * Remove a repository from the cache.
+	 * Close and remove a repository from the cache.
 	 * <p>
-	 * Removes a repository from the cache, if it is still registered here,
-	 * permitting it to close.
+	 * Removes a repository from the cache, if it is still registered here, and
+	 * close it.
 	 *
 	 * @param db
 	 *            repository to unregister.
@@ -141,15 +141,35 @@
 	public static void close(final Repository db) {
 		if (db.getDirectory() != null) {
 			FileKey key = FileKey.exact(db.getDirectory(), db.getFS());
-			cache.unregisterRepository(key);
+			cache.unregisterAndCloseRepository(key);
 		}
 	}
 
 	/**
 	 * Remove a repository from the cache.
 	 * <p>
-	 * Removes a repository from the cache, if it is still registered here,
-	 * permitting it to close.
+	 * Removes a repository from the cache, if it is still registered here. This
+	 * method will not close the repository, only remove it from the cache. See
+	 * {@link RepositoryCache#close(Repository)} to remove and close the
+	 * repository.
+	 *
+	 * @param db
+	 *            repository to unregister.
+	 * @since 4.3
+	 */
+	public static void unregister(final Repository db) {
+		if (db.getDirectory() != null) {
+			unregister(FileKey.exact(db.getDirectory(), db.getFS()));
+		}
+	}
+
+	/**
+	 * Remove a repository from the cache.
+	 * <p>
+	 * Removes a repository from the cache, if it is still registered here. This
+	 * method will not close the repository, only remove it from the cache. See
+	 * {@link RepositoryCache#close(Repository)} to remove and close the
+	 * repository.
 	 *
 	 * @param location
 	 *            location of the repository to remove.
@@ -196,15 +216,17 @@
 					db = location.open(mustExist);
 					ref = new SoftReference<Repository>(db);
 					cacheMap.put(location, ref);
+				} else {
+					db.incrementOpen();
 				}
 			}
+		} else {
+			db.incrementOpen();
 		}
-		db.incrementOpen();
 		return db;
 	}
 
 	private void registerRepository(final Key location, final Repository db) {
-		db.incrementOpen();
 		SoftReference<Repository> newRef = new SoftReference<Repository>(db);
 		Reference<Repository> oldRef = cacheMap.put(location, newRef);
 		Repository oldDb = oldRef != null ? oldRef.get() : null;
@@ -212,11 +234,16 @@
 			oldDb.close();
 	}
 
-	private void unregisterRepository(final Key location) {
+	private Repository unregisterRepository(final Key location) {
 		Reference<Repository> oldRef = cacheMap.remove(location);
-		Repository oldDb = oldRef != null ? oldRef.get() : null;
-		if (oldDb != null)
+		return oldRef != null ? oldRef.get() : null;
+	}
+
+	private void unregisterAndCloseRepository(final Key location) {
+		Repository oldDb = unregisterRepository(location);
+		if (oldDb != null) {
 			oldDb.close();
+		}
 	}
 
 	private Collection<Key> getKeys() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index de08e4b..e224d71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -1005,13 +1005,14 @@
 		builder = dircache.builder();
 		DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder);
 
-		tw = new NameConflictTreeWalk(reader);
+		tw = new NameConflictTreeWalk(db, reader);
 		tw.addTree(baseTree);
 		tw.addTree(headTree);
 		tw.addTree(mergeTree);
-		tw.addTree(buildIt);
+		int dciPos = tw.addTree(buildIt);
 		if (workingTreeIterator != null) {
 			tw.addTree(workingTreeIterator);
+			workingTreeIterator.setDirCacheIterator(tw, dciPos);
 		} else {
 			tw.setFilter(TreeFilter.ANY_DIFF);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
index f18a522..ec903c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -106,7 +106,7 @@
 
 		InCoreMerger(final Repository local) {
 			super(local);
-			tw = new NameConflictTreeWalk(reader);
+			tw = new NameConflictTreeWalk(local, reader);
 			cache = DirCache.newInCore();
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
index 3941d3c..739ddcc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
@@ -70,8 +70,6 @@
 	public ChainingCredentialsProvider(CredentialsProvider... providers) {
 		this.credentialProviders = new ArrayList<CredentialsProvider>(
 				Arrays.asList(providers));
-		for (CredentialsProvider p : providers)
-			credentialProviders.add(p);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
index d9e0b93..1beec90 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
@@ -256,6 +256,16 @@
 	}
 
 	/**
+	 * Get the factory used to construct per-request ReceivePack.
+	 *
+	 * @return the factory.
+	 * @since 4.3
+	 */
+	public ReceivePackFactory<DaemonClient> getReceivePackFactory() {
+		return receivePackFactory;
+	}
+
+	/**
 	 * Set the factory to construct and configure per-request ReceivePack.
 	 *
 	 * @param factory
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 5948278..414e879 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -67,9 +67,6 @@
 import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.URL;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.X509Certificate;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -83,11 +80,6 @@
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-
 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
 import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.PackProtocolException;
@@ -538,7 +530,7 @@
 		HttpConnection conn = connectionFactory.create(u, proxy);
 
 		if (!http.sslVerify && "https".equals(u.getProtocol())) { //$NON-NLS-1$
-			disableSslVerify(conn);
+			HttpSupport.disableSslVerify(conn);
 		}
 
 		conn.setRequestMethod(method);
@@ -562,19 +554,6 @@
 		return conn;
 	}
 
-	private void disableSslVerify(HttpConnection conn)
-			throws IOException {
-		final TrustManager[] trustAllCerts = new TrustManager[] { new DummyX509TrustManager() };
-		try {
-			conn.configure(null, trustAllCerts, null);
-			conn.setHostnameVerifier(new DummyHostnameVerifier());
-		} catch (KeyManagementException e) {
-			throw new IOException(e.getMessage());
-		} catch (NoSuchAlgorithmException e) {
-			throw new IOException(e.getMessage());
-		}
-	}
-
 	final InputStream openInputStream(HttpConnection conn)
 			throws IOException {
 		InputStream input = conn.getInputStream();
@@ -1002,25 +981,4 @@
 			in.add(openInputStream(conn));
 		}
 	}
-
-	private static class DummyX509TrustManager implements X509TrustManager {
-		public X509Certificate[] getAcceptedIssuers() {
-			return null;
-		}
-
-		public void checkClientTrusted(X509Certificate[] certs, String authType) {
-			// no check
-		}
-
-		public void checkServerTrusted(X509Certificate[] certs, String authType) {
-			// no check
-		}
-	}
-
-	private static class DummyHostnameVerifier implements HostnameVerifier {
-		public boolean verify(String hostname, SSLSession session) {
-			// always accept
-			return true;
-		}
-	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 3ee2feb..3c5c8da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -376,8 +376,10 @@
 	public URIish(final URL u) {
 		scheme = u.getProtocol();
 		path = u.getPath();
+		path = cleanLeadingSlashes(path, scheme);
 		try {
 			rawPath = u.toURI().getRawPath();
+			rawPath = cleanLeadingSlashes(rawPath, scheme);
 		} catch (URISyntaxException e) {
 			throw new RuntimeException(e); // Impossible
 		}
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 101057f..8b642bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -742,10 +742,6 @@
 			if (!clientShallowCommits.isEmpty())
 				walk.assumeShallow(clientShallowCommits);
 			sendPack = negotiate();
-		} catch (PackProtocolException err) {
-			reportErrorDuringNegotiate(err.getMessage());
-			throw err;
-
 		} catch (ServiceMayNotContinueException err) {
 			if (!err.isOutput() && err.getMessage() != null) {
 				try {
@@ -756,15 +752,20 @@
 				}
 			}
 			throw err;
-
-		} catch (IOException err) {
-			reportErrorDuringNegotiate(JGitText.get().internalServerError);
-			throw err;
-		} catch (RuntimeException err) {
-			reportErrorDuringNegotiate(JGitText.get().internalServerError);
-			throw err;
-		} catch (Error err) {
-			reportErrorDuringNegotiate(JGitText.get().internalServerError);
+		} catch (IOException | RuntimeException | Error err) {
+			boolean output = false;
+			try {
+				String msg = err instanceof PackProtocolException
+						? err.getMessage()
+						: JGitText.get().internalServerError;
+				pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+				output = true;
+			} catch (Throwable err2) {
+				// Ignore this secondary failure, leave output false.
+			}
+			if (output) {
+				throw new UploadPackInternalServerErrorException(err);
+			}
 			throw err;
 		}
 
@@ -781,14 +782,6 @@
 		return ids;
 	}
 
-	private void reportErrorDuringNegotiate(String msg) {
-		try {
-			pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
-		} catch (Throwable err) {
-			// Ignore this secondary failure.
-		}
-	}
-
 	private void processShallow() throws IOException {
 		try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
 				walk.getObjectReader(), depth)) {
@@ -1186,9 +1179,7 @@
 			}
 			wantIds.clear();
 		} catch (MissingObjectException notFound) {
-			ObjectId id = notFound.getObjectId();
-			throw new PackProtocolException(MessageFormat.format(
-					JGitText.get().wantNotValid, id.name()), notFound);
+			throw new WantNotValidException(notFound.getObjectId(), notFound);
 		} finally {
 			q.release();
 		}
@@ -1213,8 +1204,7 @@
 			if (!up.isBiDirectionalPipe())
 				new ReachableCommitRequestValidator().checkWants(up, wants);
 			else if (!wants.isEmpty())
-				throw new PackProtocolException(MessageFormat.format(
-						JGitText.get().wantNotValid, wants.iterator().next().name()));
+				throw new WantNotValidException(wants.iterator().next());
 		}
 	}
 
@@ -1247,8 +1237,7 @@
 					refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values());
 				for (ObjectId obj : wants) {
 					if (!refIds.contains(obj))
-						throw new PackProtocolException(MessageFormat.format(
-								JGitText.get().wantNotValid, obj.name()));
+						throw new WantNotValidException(obj);
 				}
 			}
 		}
@@ -1294,14 +1283,11 @@
 			RevObject obj;
 			while ((obj = q.next()) != null) {
 				if (!(obj instanceof RevCommit))
-					throw new PackProtocolException(MessageFormat.format(
-						JGitText.get().wantNotValid, obj.name()));
+					throw new WantNotValidException(obj);
 				walk.markStart((RevCommit) obj);
 			}
 		} catch (MissingObjectException notFound) {
-			ObjectId id = notFound.getObjectId();
-			throw new PackProtocolException(MessageFormat.format(
-					JGitText.get().wantNotValid, id.name()), notFound);
+			throw new WantNotValidException(notFound.getObjectId(), notFound);
 		} finally {
 			q.release();
 		}
@@ -1315,9 +1301,7 @@
 
 		RevCommit bad = walk.next();
 		if (bad != null) {
-			throw new PackProtocolException(MessageFormat.format(
-					JGitText.get().wantNotValid,
-					bad.name()));
+			throw new WantNotValidException(bad);
 		}
 		walk.reset();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WantNotValidException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WantNotValidException.java
new file mode 100644
index 0000000..f0f6434
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WantNotValidException.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
+
+/**
+ * Indicates client requested an object the server does not want to serve.
+ * <p>
+ * Typically visible only inside of the server implementation; clients are
+ * usually looking at the text message from the server in a generic
+ * {@link PackProtocolException}.
+ *
+ * @since 4.3
+ */
+public class WantNotValidException extends PackProtocolException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Construct a {@code "want $id not valid"} exception.
+	 *
+	 * @param id
+	 *            invalid object identifier received from the client.
+	 */
+	public WantNotValidException(AnyObjectId id) {
+		super(msg(id));
+	}
+
+	/**
+	 * Construct a {@code "want $id not valid"} exception.
+	 *
+	 * @param id
+	 *            invalid object identifier received from the client.
+	 * @param cause
+	 *            root cause of the object being invalid, such as an IOException
+	 *            from the storage system.
+	 */
+	public WantNotValidException(AnyObjectId id, Throwable cause) {
+		super(msg(id), cause);
+	}
+
+	private static String msg(AnyObjectId id) {
+		return MessageFormat.format(JGitText.get().wantNotValid, id.name());
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 5813635..07fc829 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -50,6 +50,7 @@
 import java.nio.CharBuffer;
 
 import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesHandler;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -88,8 +89,14 @@
 	/** A dummy object id buffer that matches the zero ObjectId. */
 	protected static final byte[] zeroid = new byte[Constants.OBJECT_ID_LENGTH];
 
-	/** Iterator for the parent tree; null if we are the root iterator. */
-	final AbstractTreeIterator parent;
+	/**
+	 * Iterator for the parent tree; null if we are the root iterator.
+	 * <p>
+	 * Used by {@link TreeWalk} and {@link AttributesHandler}
+	 *
+	 * @since 4.3
+	 */
+	public final AbstractTreeIterator parent;
 
 	/** The iterator this current entry is path equal to. */
 	AbstractTreeIterator matches;
@@ -722,4 +729,13 @@
 	public String toString() {
 		return getClass().getSimpleName() + "[" + getEntryPathString() + "]"; //$NON-NLS-1$
 	}
+
+	/**
+	 * @return whether or not this Iterator is iterating through the Work Tree
+	 *
+	 * @since 4.3
+	 */
+	public boolean isWorkTree() {
+		return false;
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index accf495..db81e1a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -79,14 +79,42 @@
 	protected final FS fs;
 
 	/**
+	 * the strategy used to compute the FileMode for a FileEntry. Can be used to
+	 * control things such as whether to recurse into a directory or create a
+	 * gitlink.
+	 *
+	 * @since 4.3
+	 */
+	protected final FileModeStrategy fileModeStrategy;
+
+	/**
 	 * Create a new iterator to traverse the work tree and its children.
 	 *
 	 * @param repo
 	 *            the repository whose working tree will be scanned.
 	 */
 	public FileTreeIterator(Repository repo) {
+		this(repo,
+				repo.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks() ?
+						NoGitlinksStrategy.INSTANCE :
+						DefaultFileModeStrategy.INSTANCE);
+	}
+
+	/**
+	 * Create a new iterator to traverse the work tree and its children.
+	 *
+	 * @param repo
+	 *            the repository whose working tree will be scanned.
+	 * @param fileModeStrategy
+	 *            the strategy to use to determine the FileMode for a FileEntry;
+	 *            controls gitlinks etc.
+	 *
+	 * @since 4.3
+	 */
+	public FileTreeIterator(Repository repo, FileModeStrategy fileModeStrategy) {
 		this(repo.getWorkTree(), repo.getFS(),
-				repo.getConfig().get(WorkingTreeOptions.KEY));
+				repo.getConfig().get(WorkingTreeOptions.KEY),
+				fileModeStrategy);
 		initRootIterator(repo);
 	}
 
@@ -103,9 +131,32 @@
 	 *            working tree options to be used
 	 */
 	public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options) {
+		this(root, fs, options, DefaultFileModeStrategy.INSTANCE);
+	}
+
+	/**
+	 * Create a new iterator to traverse the given directory and its children.
+	 *
+	 * @param root
+	 *            the starting directory. This directory should correspond to
+	 *            the root of the repository.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to perform
+	 *            certain file system operations.
+	 * @param options
+	 *            working tree options to be used
+	 * @param fileModeStrategy
+	 *            the strategy to use to determine the FileMode for a FileEntry;
+	 *            controls gitlinks etc.
+	 *
+	 * @since 4.3
+	 */
+	public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options,
+							FileModeStrategy fileModeStrategy) {
 		super(options);
 		directory = root;
 		this.fs = fs;
+		this.fileModeStrategy = fileModeStrategy;
 		init(entries());
 	}
 
@@ -114,25 +165,72 @@
 	 *
 	 * @param p
 	 *            the parent iterator we were created from.
-	 * @param fs
-	 *            the file system abstraction which will be necessary to perform
-	 *            certain file system operations.
 	 * @param root
 	 *            the subdirectory. This should be a directory contained within
 	 *            the parent directory.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to perform
+	 *            certain file system operations.
+	 * @since 4.3
+	 * @deprecated use {@link #FileTreeIterator(FileTreeIterator, File, FS)}
+	 *             instead.
 	 */
+	@Deprecated
 	protected FileTreeIterator(final WorkingTreeIterator p, final File root,
 			FS fs) {
+		this(p, root, fs, DefaultFileModeStrategy.INSTANCE);
+	}
+
+	/**
+	 * Create a new iterator to traverse a subdirectory.
+	 *
+	 * @param p
+	 *            the parent iterator we were created from.
+	 * @param root
+	 *            the subdirectory. This should be a directory contained within
+	 *            the parent directory.
+	 * @param fs
+	 *            the file system abstraction which will be necessary to perform
+	 *            certain file system operations.
+	 *
+	 * @since 4.3
+	 */
+	protected FileTreeIterator(final FileTreeIterator p, final File root,
+			FS fs) {
+		this(p, root, fs, p.fileModeStrategy);
+	}
+
+	/**
+	 * Create a new iterator to traverse a subdirectory, given the specified
+	 * FileModeStrategy.
+	 *
+	 * @param p
+	 *            the parent iterator we were created from.
+	 * @param root
+	 *            the subdirectory. This should be a directory contained within
+	 *            the parent directory
+	 * @param fs
+	 *            the file system abstraction which will be necessary to perform
+	 *            certain file system operations.
+	 * @param fileModeStrategy
+	 *            the strategy to use to determine the FileMode for a given
+	 *            FileEntry.
+	 *
+	 * @since 4.3
+	 */
+	protected FileTreeIterator(final WorkingTreeIterator p, final File root,
+			FS fs, FileModeStrategy fileModeStrategy) {
 		super(p);
 		directory = root;
 		this.fs = fs;
+		this.fileModeStrategy = fileModeStrategy;
 		init(entries());
 	}
 
 	@Override
 	public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader)
 			throws IncorrectObjectTypeException, IOException {
-		return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs);
+		return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs, fileModeStrategy);
 	}
 
 	private Entry[] entries() {
@@ -141,11 +239,92 @@
 			return EOF;
 		final Entry[] r = new Entry[all.length];
 		for (int i = 0; i < r.length; i++)
-			r[i] = new FileEntry(all[i], fs);
+			r[i] = new FileEntry(all[i], fs, fileModeStrategy);
 		return r;
 	}
 
 	/**
+	 * An interface representing the methods used to determine the FileMode for
+	 * a FileEntry.
+	 *
+	 * @since 4.3
+	 */
+	public interface FileModeStrategy {
+		/**
+		 * Compute the FileMode for a given File, based on its attributes.
+		 *
+		 * @param f
+		 *            the file to return a FileMode for
+		 * @param attributes
+		 *            the attributes of a file
+		 * @return a FileMode indicating whether the file is a regular file, a
+		 *         directory, a gitlink, etc.
+		 */
+		FileMode getMode(File f, FS.Attributes attributes);
+	}
+
+	/**
+	 * A default implementation of a FileModeStrategy; defaults to treating
+	 * nested .git directories as gitlinks, etc.
+	 *
+	 * @since 4.3
+	 */
+	static public class DefaultFileModeStrategy implements FileModeStrategy {
+		/**
+		 * a singleton instance of the default FileModeStrategy
+		 */
+		public final static DefaultFileModeStrategy INSTANCE =
+				new DefaultFileModeStrategy();
+
+		@Override
+		public FileMode getMode(File f, FS.Attributes attributes) {
+			if (attributes.isSymbolicLink()) {
+				return FileMode.SYMLINK;
+			} else if (attributes.isDirectory()) {
+				if (new File(f, Constants.DOT_GIT).exists()) {
+					return FileMode.GITLINK;
+				} else {
+					return FileMode.TREE;
+				}
+			} else if (attributes.isExecutable()) {
+				return FileMode.EXECUTABLE_FILE;
+			} else {
+				return FileMode.REGULAR_FILE;
+			}
+		}
+	}
+
+	/**
+	 * A FileModeStrategy that implements native git's DIR_NO_GITLINKS
+	 * behavior. This is the same as the default FileModeStrategy, except
+	 * all directories will be treated as directories regardless of whether
+	 * or not they contain a .git directory or file.
+	 *
+	 * @since 4.3
+	 */
+	static public class NoGitlinksStrategy implements FileModeStrategy {
+
+		/**
+		 * a singleton instance of the default FileModeStrategy
+		 */
+		public final static NoGitlinksStrategy INSTANCE = new NoGitlinksStrategy();
+
+		@Override
+		public FileMode getMode(File f, FS.Attributes attributes) {
+			if (attributes.isSymbolicLink()) {
+				return FileMode.SYMLINK;
+			} else if (attributes.isDirectory()) {
+				return FileMode.TREE;
+			} else if (attributes.isExecutable()) {
+				return FileMode.EXECUTABLE_FILE;
+			} else {
+				return FileMode.REGULAR_FILE;
+			}
+		}
+	}
+
+
+	/**
 	 * Wrapper for a standard Java IO file
 	 */
 	static public class FileEntry extends Entry {
@@ -164,20 +343,27 @@
 		 *            file system
 		 */
 		public FileEntry(File f, FS fs) {
+			this(f, fs, DefaultFileModeStrategy.INSTANCE);
+		}
+
+		/**
+		 * Create a new file entry given the specified FileModeStrategy
+		 *
+		 * @param f
+		 *            file
+		 * @param fs
+		 *            file system
+		 * @param fileModeStrategy
+		 *            the strategy to use when determining the FileMode of a
+		 *            file; controls gitlinks etc.
+		 *
+		 * @since 4.3
+		 */
+		public FileEntry(File f, FS fs, FileModeStrategy fileModeStrategy) {
 			this.fs = fs;
 			f = fs.normalize(f);
 			attributes = fs.getAttributes(f);
-			if (attributes.isSymbolicLink())
-				mode = FileMode.SYMLINK;
-			else if (attributes.isDirectory()) {
-				if (new File(f, Constants.DOT_GIT).exists())
-					mode = FileMode.GITLINK;
-				else
-					mode = FileMode.TREE;
-			} else if (attributes.isExecutable())
-				mode = FileMode.EXECUTABLE_FILE;
-			else
-				mode = FileMode.REGULAR_FILE;
+			mode = fileModeStrategy.getMode(f, attributes);
 		}
 
 		@Override
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 d2195a8..b9293eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -104,6 +104,19 @@
 	/**
 	 * Create a new tree walker for a given repository.
 	 *
+	 * @param repo
+	 *            the repository the walker will obtain data from.
+	 * @param or
+	 *            the reader the walker will obtain tree data from.
+	 * @since 4.3
+	 */
+	public NameConflictTreeWalk(Repository repo, final ObjectReader or) {
+		super(repo, or);
+	}
+
+	/**
+	 * Create a new tree walker for a given repository.
+	 *
 	 * @param or
 	 *            the reader the walker will obtain tree data from.
 	 */
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 5cd713d..7dac50a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -52,12 +52,11 @@
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.attributes.Attribute;
-import org.eclipse.jgit.attributes.Attribute.State;
 import org.eclipse.jgit.attributes.Attributes;
-import org.eclipse.jgit.attributes.AttributesNode;
 import org.eclipse.jgit.attributes.AttributesNodeProvider;
 import org.eclipse.jgit.attributes.AttributesProvider;
 import org.eclipse.jgit.dircache.DirCacheBuildIterator;
+import org.eclipse.jgit.attributes.AttributesHandler;
 import org.eclipse.jgit.dircache.DirCacheIterator;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -66,6 +65,7 @@
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.MutableObjectId;
 import org.eclipse.jgit.lib.ObjectId;
@@ -76,6 +76,7 @@
 import org.eclipse.jgit.treewalk.filter.TreeFilter;
 import org.eclipse.jgit.util.QuotedString;
 import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.io.EolStreamTypeUtil;
 
 /**
  * Walks one or more {@link AbstractTreeIterator}s in parallel.
@@ -163,7 +164,44 @@
 	public static TreeWalk forPath(final ObjectReader reader, final String path,
 			final AnyObjectId... trees) throws MissingObjectException,
 			IncorrectObjectTypeException, CorruptObjectException, IOException {
-		TreeWalk tw = new TreeWalk(reader);
+		return forPath(null, reader, path, trees);
+	}
+
+	/**
+	 * Open a tree walk and filter to exactly one path.
+	 * <p>
+	 * The returned tree walk is already positioned on the requested path, so
+	 * the caller should not need to invoke {@link #next()} unless they are
+	 * looking for a possible directory/file name conflict.
+	 *
+	 * @param repo
+	 *            repository to read config data and
+	 *            {@link AttributesNodeProvider} from.
+	 * @param reader
+	 *            the reader the walker will obtain tree data from.
+	 * @param path
+	 *            single path to advance the tree walk instance into.
+	 * @param trees
+	 *            one or more trees to walk through, all with the same root.
+	 * @return a new tree walk configured for exactly this one path; null if no
+	 *         path was found in any of the trees.
+	 * @throws IOException
+	 *             reading a pack file or loose object failed.
+	 * @throws CorruptObjectException
+	 *             an tree object could not be read as its data stream did not
+	 *             appear to be a tree, or could not be inflated.
+	 * @throws IncorrectObjectTypeException
+	 *             an object we expected to be a tree was not a tree.
+	 * @throws MissingObjectException
+	 *             a tree object was not found.
+	 * @since 4.3
+	 */
+	public static TreeWalk forPath(final @Nullable Repository repo,
+			final ObjectReader reader, final String path,
+			final AnyObjectId... trees)
+					throws MissingObjectException, IncorrectObjectTypeException,
+					CorruptObjectException, IOException {
+		TreeWalk tw = new TreeWalk(repo, reader);
 		PathFilter f = PathFilter.create(path);
 		tw.setFilter(f);
 		tw.reset(trees);
@@ -208,7 +246,7 @@
 			final AnyObjectId... trees) throws MissingObjectException,
 			IncorrectObjectTypeException, CorruptObjectException, IOException {
 		try (ObjectReader reader = db.newObjectReader()) {
-			return forPath(reader, path, trees);
+			return forPath(db, reader, path, trees);
 		}
 	}
 
@@ -270,6 +308,9 @@
 	/** Cached attribute for the current entry */
 	private Attributes attrs = null;
 
+	/** Cached attributes handler */
+	private AttributesHandler attributesHandler;
+
 	private Config config;
 
 	/**
@@ -281,9 +322,23 @@
 	 *            when the walker is closed.
 	 */
 	public TreeWalk(final Repository repo) {
-		this(repo.newObjectReader(), true);
-		config = repo.getConfig();
-		attributesNodeProvider = repo.createAttributesNodeProvider();
+		this(repo, repo.newObjectReader(), true);
+	}
+
+	/**
+	 * Create a new tree walker for a given repository.
+	 *
+	 * @param repo
+	 *            the repository the walker will obtain data from. An
+	 *            ObjectReader will be created by the walker, and will be closed
+	 *            when the walker is closed.
+	 * @param or
+	 *            the reader the walker will obtain tree data from. The reader
+	 *            is not closed when the walker is closed.
+	 * @since 4.3
+	 */
+	public TreeWalk(final @Nullable Repository repo, final ObjectReader or) {
+		this(repo, or, false);
 	}
 
 	/**
@@ -294,10 +349,18 @@
 	 *            is not closed when the walker is closed.
 	 */
 	public TreeWalk(final ObjectReader or) {
-		this(or, false);
+		this(null, or, false);
 	}
 
-	private TreeWalk(final ObjectReader or, final boolean closeReader) {
+	private TreeWalk(final @Nullable Repository repo, final ObjectReader or,
+			final boolean closeReader) {
+		if (repo != null) {
+			config = repo.getConfig();
+			attributesNodeProvider = repo.createAttributesNodeProvider();
+		} else {
+			config = null;
+			attributesNodeProvider = null;
+		}
 		reader = or;
 		filter = TreeFilter.ALL;
 		trees = NO_TREES;
@@ -310,6 +373,14 @@
 	}
 
 	/**
+	 * @return the {@link OperationType}
+	 * @since 4.3
+	 */
+	public OperationType getOperationType() {
+		return operationType;
+	}
+
+	/**
 	 * Release any resources used by this walker's reader.
 	 * <p>
 	 * A walker that has been released can be used again, but may need to be
@@ -435,9 +506,110 @@
 		attributesNodeProvider = provider;
 	}
 
+	/**
+	 * @return the {@link AttributesNodeProvider} for this {@link TreeWalk}.
+	 * @since 4.3
+	 */
+	public AttributesNodeProvider getAttributesNodeProvider() {
+		return attributesNodeProvider;
+	}
+
+	/**
+	 * Retrieve the git attributes for the current entry.
+	 *
+	 * <h4>Git attribute computation</h4>
+	 *
+	 * <ul>
+	 * <li>Get the attributes matching the current path entry from the info file
+	 * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
+	 * <li>Completes the list of attributes using the .gitattributes files
+	 * located on the current path (the further the directory that contains
+	 * .gitattributes is from the path in question, the lower its precedence).
+	 * For a checkin operation, it will look first on the working tree (if any).
+	 * If there is no attributes file, it will fallback on the index. For a
+	 * checkout operation, it will first use the index entry and then fallback
+	 * on the working tree if none.</li>
+	 * <li>In the end, completes the list of matching attributes using the
+	 * global attribute file define in the configuration (see
+	 * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
+	 *
+	 * </ul>
+	 *
+	 *
+	 * <h4>Iterator constraints</h4>
+	 *
+	 * <p>
+	 * In order to have a correct list of attributes for the current entry, this
+	 * {@link TreeWalk} requires to have at least one
+	 * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
+	 * {@link AttributesNodeProvider} is used to retrieve the attributes from
+	 * the info attributes file and the global attributes file. The
+	 * {@link DirCacheIterator} is used to retrieve the .gitattributes files
+	 * stored in the index. A {@link WorkingTreeIterator} can also be provided
+	 * to access the local version of the .gitattributes files. If none is
+	 * provided it will fallback on the {@link DirCacheIterator}.
+	 * </p>
+	 *
+	 * @return a {@link Set} of {@link Attribute}s that match the current entry.
+	 * @since 4.2
+	 */
+	public Attributes getAttributes() {
+		if (attrs != null)
+			return attrs;
+
+		if (attributesNodeProvider == null) {
+			// The work tree should have a AttributesNodeProvider to be able to
+			// retrieve the info and global attributes node
+			throw new IllegalStateException(
+					"The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
+		}
+
+		try {
+			// Lazy create the attributesHandler on the first access of
+			// attributes. This requires the info, global and root
+			// attributes nodes
+			if (attributesHandler == null) {
+				attributesHandler = new AttributesHandler(this);
+			}
+			attrs = attributesHandler.getAttributes();
+			return attrs;
+		} catch (IOException e) {
+			throw new JGitInternalException("Error while parsing attributes", //$NON-NLS-1$
+					e);
+		}
+	}
+
+	/**
+	 * @param opType
+	 *            the operationtype (checkin/checkout) which should be used
+	 * @return the EOL stream type of the current entry using the config and
+	 *         {@link #getAttributes()} Note that this method may return null if
+	 *         the {@link TreeWalk} is not based on a working tree
+	 */
+	// TODO(msohn) make this method public in 4.4
+	@Nullable
+	EolStreamType getEolStreamType(OperationType opType) {
+			if (attributesNodeProvider == null || config == null)
+				return null;
+		return EolStreamTypeUtil.detectStreamType(opType,
+					config.get(WorkingTreeOptions.KEY), getAttributes());
+	}
+
+	/**
+	 * @return the EOL stream type of the current entry using the config and
+	 *         {@link #getAttributes()} Note that this method may return null if
+	 *         the {@link TreeWalk} is not based on a working tree
+	 * @since 4.3
+	 */
+	// TODO(msohn) deprecate this method in 4.4
+	public @Nullable EolStreamType getEolStreamType() {
+		return (getEolStreamType(operationType));
+	}
+
 	/** Reset this walker so new tree iterators can be added to it. */
 	public void reset() {
 		attrs = null;
+		attributesHandler = null;
 		trees = NO_TREES;
 		advance = false;
 		depth = 0;
@@ -740,6 +912,16 @@
 	}
 
 	/**
+	 * Obtain the {@link FileMode} for the current entry on the currentHead tree
+	 *
+	 * @return mode for the current entry of the currentHead tree.
+	 * @since 4.3
+	 */
+	public FileMode getFileMode() {
+		return FileMode.fromBits(currentHead.mode);
+	}
+
+	/**
 	 * Obtain the ObjectId for the current entry.
 	 * <p>
 	 * Using this method to compare ObjectId values between trees of this walker
@@ -1019,7 +1201,12 @@
 		for (int i = 0; i < trees.length; i++) {
 			final AbstractTreeIterator t = trees[i];
 			final AbstractTreeIterator n;
-			if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
+			// If we find a GITLINK when attempting to enter a subtree, then the
+			// GITLINK must exist as a TREE in the index, meaning the working tree
+			// entry should be treated as a TREE
+			if (t.matches == ch && !t.eof() &&
+					(FileMode.TREE.equals(t.mode)
+							|| (FileMode.GITLINK.equals(t.mode) && t.isWorkTree())))
 				n = t.createSubtreeIterator(reader, idBuffer);
 			else
 				n = t.createEmptyTreeIterator();
@@ -1109,156 +1296,13 @@
 	}
 
 	/**
-	 * Retrieve the git attributes for the current entry.
-	 *
-	 * <h4>Git attribute computation</h4>
-	 *
-	 * <ul>
-	 * <li>Get the attributes matching the current path entry from the info file
-	 * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
-	 * <li>Completes the list of attributes using the .gitattributes files
-	 * located on the current path (the further the directory that contains
-	 * .gitattributes is from the path in question, the lower its precedence).
-	 * For a checkin operation, it will look first on the working tree (if any).
-	 * If there is no attributes file, it will fallback on the index. For a
-	 * checkout operation, it will first use the index entry and then fallback
-	 * on the working tree if none.</li>
-	 * <li>In the end, completes the list of matching attributes using the
-	 * global attribute file define in the configuration (see
-	 * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
-	 *
-	 * </ul>
-	 *
-	 *
-	 * <h4>Iterator constraints</h4>
-	 *
-	 * <p>
-	 * In order to have a correct list of attributes for the current entry, this
-	 * {@link TreeWalk} requires to have at least one
-	 * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
-	 * {@link AttributesNodeProvider} is used to retrieve the attributes from
-	 * the info attributes file and the global attributes file. The
-	 * {@link DirCacheIterator} is used to retrieve the .gitattributes files
-	 * stored in the index. A {@link WorkingTreeIterator} can also be provided
-	 * to access the local version of the .gitattributes files. If none is
-	 * provided it will fallback on the {@link DirCacheIterator}.
-	 * </p>
-	 *
-	 * @return a {@link Set} of {@link Attribute}s that match the current entry.
-	 * @since 4.2
+	 * @param type
+	 *            of the tree to be queried
+	 * @return the tree of that type or null if none is present
+	 * @since 4.3
 	 */
-	public Attributes getAttributes() {
-		if (attrs != null)
-			return attrs;
-
-		if (attributesNodeProvider == null) {
-			// The work tree should have a AttributesNodeProvider to be able to
-			// retrieve the info and global attributes node
-			throw new IllegalStateException(
-					"The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
-		}
-
-		WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class);
-		DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class);
-		CanonicalTreeParser other = getTree(CanonicalTreeParser.class);
-
-		if (workingTreeIterator == null && dirCacheIterator == null
-				&& other == null) {
-			// Can not retrieve the attributes without at least one of the above
-			// iterators.
-			return new Attributes();
-		}
-
-		String path = currentHead.getEntryPathString();
-		final boolean isDir = FileMode.TREE.equals(currentHead.mode);
-		Attributes attributes = new Attributes();
-		try {
-			// Gets the global attributes node
-			AttributesNode globalNodeAttr = attributesNodeProvider
-					.getGlobalAttributesNode();
-			// Gets the info attributes node
-			AttributesNode infoNodeAttr = attributesNodeProvider
-					.getInfoAttributesNode();
-
-			// Gets the info attributes
-			if (infoNodeAttr != null) {
-				infoNodeAttr.getAttributes(path, isDir, attributes);
-			}
-
-			// Gets the attributes located on the current entry path
-			getPerDirectoryEntryAttributes(path, isDir, operationType,
-					workingTreeIterator, dirCacheIterator, other, attributes);
-
-			// Gets the attributes located in the global attribute file
-			if (globalNodeAttr != null) {
-				globalNodeAttr.getAttributes(path, isDir, attributes);
-			}
-		} catch (IOException e) {
-			throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$
-		}
-		// now after all attributes are collected - in the correct hierarchy
-		// order - remove all unspecified entries (the ! marker)
-		for (Attribute a : attributes.getAll()) {
-			if (a.getState() == State.UNSPECIFIED)
-				attributes.remove(a.getKey());
-		}
-		return attributes;
-	}
-
-	/**
-	 * Get the attributes located on the current entry path.
-	 *
-	 * @param path
-	 *            current entry path
-	 * @param isDir
-	 *            holds true if the current entry is a directory
-	 * @param opType
-	 *            type of operation
-	 * @param workingTreeIterator
-	 *            a {@link WorkingTreeIterator} matching the current entry
-	 * @param dirCacheIterator
-	 *            a {@link DirCacheIterator} matching the current entry
-	 * @param other
-	 *            a {@link CanonicalTreeParser} matching the current entry
-	 * @param attributes
-	 *            Non null map holding the existing attributes. This map will be
-	 *            augmented with new entry. None entry will be overrided.
-	 * @throws IOException
-	 *             It raises an {@link IOException} if a problem appears while
-	 *             parsing one on the attributes file.
-	 */
-	private void getPerDirectoryEntryAttributes(String path, boolean isDir,
-			OperationType opType, WorkingTreeIterator workingTreeIterator,
-			DirCacheIterator dirCacheIterator, CanonicalTreeParser other,
-			Attributes attributes)
-			throws IOException {
-		// Prevents infinite recurrence
-		if (workingTreeIterator != null || dirCacheIterator != null
-				|| other != null) {
-			AttributesNode currentAttributesNode = getCurrentAttributesNode(
-					opType, workingTreeIterator, dirCacheIterator, other);
-			if (currentAttributesNode != null) {
-				currentAttributesNode.getAttributes(path, isDir, attributes);
-			}
-			getPerDirectoryEntryAttributes(path, isDir, opType,
-					getParent(workingTreeIterator, WorkingTreeIterator.class),
-					getParent(dirCacheIterator, DirCacheIterator.class),
-					getParent(other, CanonicalTreeParser.class), attributes);
-		}
-	}
-
-	private static <T extends AbstractTreeIterator> T getParent(T current,
+	public <T extends AbstractTreeIterator> T getTree(
 			Class<T> type) {
-		if (current != null) {
-			AbstractTreeIterator parent = current.parent;
-			if (type.isInstance(parent)) {
-				return type.cast(parent);
-			}
-		}
-		return null;
-	}
-
-	private <T extends AbstractTreeIterator> T getTree(Class<T> type) {
 		for (int i = 0; i < trees.length; i++) {
 			AbstractTreeIterator tree = trees[i];
 			if (type.isInstance(tree)) {
@@ -1269,76 +1313,6 @@
 	}
 
 	/**
-	 * Get the {@link AttributesNode} for the current entry.
-	 * <p>
-	 * This method implements the fallback mechanism between the index and the
-	 * working tree depending on the operation type
-	 * </p>
-	 *
-	 * @param opType
-	 * @param workingTreeIterator
-	 * @param dirCacheIterator
-	 * @param other
-	 * @return a {@link AttributesNode} of the current entry,
-	 *         {@link NullPointerException} otherwise.
-	 * @throws IOException
-	 *             It raises an {@link IOException} if a problem appears while
-	 *             parsing one on the attributes file.
-	 */
-	private AttributesNode getCurrentAttributesNode(OperationType opType,
-			@Nullable WorkingTreeIterator workingTreeIterator,
-			@Nullable DirCacheIterator dirCacheIterator,
-			@Nullable CanonicalTreeParser other)
-					throws IOException {
-		AttributesNode attributesNode = null;
-		switch (opType) {
-		case CHECKIN_OP:
-			if (workingTreeIterator != null) {
-				attributesNode = workingTreeIterator.getEntryAttributesNode();
-			}
-			if (attributesNode == null && dirCacheIterator != null) {
-				attributesNode = getAttributesNode(dirCacheIterator
-						.getEntryAttributesNode(getObjectReader()),
-						attributesNode);
-			}
-			if (attributesNode == null && other != null) {
-				attributesNode = getAttributesNode(
-						other.getEntryAttributesNode(getObjectReader()),
-						attributesNode);
-			}
-			break;
-		case CHECKOUT_OP:
-			if (other != null) {
-				attributesNode = other
-						.getEntryAttributesNode(getObjectReader());
-			}
-			if (dirCacheIterator != null) {
-				attributesNode = getAttributesNode(dirCacheIterator
-						.getEntryAttributesNode(getObjectReader()),
-						attributesNode);
-			}
-			if (attributesNode == null && workingTreeIterator != null) {
-				attributesNode = getAttributesNode(
-						workingTreeIterator.getEntryAttributesNode(),
-						attributesNode);
-			}
-			break;
-		default:
-			throw new IllegalStateException(
-					"The only supported operation types are:" //$NON-NLS-1$
-							+ OperationType.CHECKIN_OP + "," //$NON-NLS-1$
-							+ OperationType.CHECKOUT_OP);
-		}
-
-		return attributesNode;
-	}
-
-	private static AttributesNode getAttributesNode(AttributesNode value,
-			AttributesNode defaultValue) {
-		return (value == null) ? defaultValue : value;
-	}
-
-	/**
 	 * Inspect config and attributes to return a filtercommand applicable for
 	 * the current path
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 0d617ee..c8de3de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -77,8 +77,8 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.CoreConfig;
-import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 import org.eclipse.jgit.lib.CoreConfig.CheckStat;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
@@ -86,12 +86,15 @@
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.Holder;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.Paths;
 import org.eclipse.jgit.util.RawParseUtils;
-import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
+import org.eclipse.jgit.util.io.AutoLFInputStream;
+import org.eclipse.jgit.util.io.EolStreamTypeUtil;
 
 /**
  * Walks a working directory tree as part of a {@link TreeWalk}.
@@ -140,7 +143,17 @@
 	/** If there is a .gitignore file present, the parsed rules from it. */
 	private IgnoreNode ignoreNode;
 
-	private String cleanFilterCommand;
+	/**
+	 * cached clean filter command. Use a Ref in order to distinguish between
+	 * the ref not cached yet and the value null
+	 */
+	private Holder<String> cleanFilterCommandHolder;
+
+	/**
+	 * cached eol stream type. Use a Ref in order to distinguish between the ref
+	 * not cached yet and the value null
+	 */
+	private Holder<EolStreamType> eolStreamTypeHolder;
 
 	/** Repository that is the root level being iterated over */
 	protected Repository repository;
@@ -252,7 +265,7 @@
 			// the cached index information for the path.
 			//
 			DirCacheIterator i = state.walk.getTree(state.dirCacheTree,
-					DirCacheIterator.class);
+							DirCacheIterator.class);
 			if (i != null) {
 				DirCacheEntry ent = i.getDirCacheEntry();
 				if (ent != null && compareMetadata(ent) == MetadataDiff.EQUAL) {
@@ -277,6 +290,11 @@
 		return zeroid;
 	}
 
+	@Override
+	public boolean isWorkTree() {
+		return true;
+	}
+
 	/**
 	 * Get submodule id for given entry.
 	 *
@@ -344,7 +362,8 @@
 				state.initializeDigestAndReadBuffer();
 
 				final long len = e.getLength();
-				InputStream filteredIs = possiblyFilteredInputStream(e, is, len);
+				InputStream filteredIs = possiblyFilteredInputStream(e, is, len,
+						OperationType.CHECKIN_OP);
 				return computeHash(filteredIs, canonLen);
 			} finally {
 				safeClose(is);
@@ -357,8 +376,15 @@
 
 	private InputStream possiblyFilteredInputStream(final Entry e,
 			final InputStream is, final long len) throws IOException {
-		boolean mightNeedCleaning = mightNeedCleaning();
-		if (!mightNeedCleaning) {
+		return possiblyFilteredInputStream(e, is, len, null);
+
+	}
+
+	private InputStream possiblyFilteredInputStream(final Entry e,
+			final InputStream is, final long len, OperationType opType)
+			throws IOException {
+		if (getCleanFilterCommand() == null
+				&& getEolStreamType(opType) == EolStreamType.DIRECT) {
 			canonLen = len;
 			return is;
 		}
@@ -368,7 +394,7 @@
 			byte[] raw = rawbuf.array();
 			int n = rawbuf.limit();
 			if (!isBinary(raw, n)) {
-				rawbuf = filterClean(raw, n);
+				rawbuf = filterClean(raw, n, opType);
 				raw = rawbuf.array();
 				n = rawbuf.limit();
 			}
@@ -376,19 +402,19 @@
 			return new ByteArrayInputStream(raw, 0, n);
 		}
 
-		// TODO: fix autocrlf causing mightneedcleaning
-		if (!mightNeedCleaning && isBinary(e)) {
-			canonLen = len;
-			return is;
-		}
+		if (getCleanFilterCommand() == null && isBinary(e)) {
+				canonLen = len;
+				return is;
+			}
 
-		final InputStream lenIs = filterClean(e.openInputStream());
+		final InputStream lenIs = filterClean(e.openInputStream(),
+				opType);
 		try {
 			canonLen = computeLength(lenIs);
 		} finally {
 			safeClose(lenIs);
 		}
-		return filterClean(is);
+		return filterClean(is, opType);
 	}
 
 	private static void safeClose(final InputStream in) {
@@ -401,20 +427,6 @@
 		}
 	}
 
-	private boolean mightNeedCleaning() throws IOException {
-		switch (getOptions().getAutoCRLF()) {
-		case FALSE:
-		default:
-			if (getCleanFilterCommand() != null)
-				return true;
-			return false;
-
-		case TRUE:
-		case INPUT:
-			return true;
-		}
-	}
-
 	private static boolean isBinary(byte[] content, int sz) {
 		return RawText.isBinary(content, sz);
 	}
@@ -428,17 +440,23 @@
 		}
 	}
 
-	private ByteBuffer filterClean(byte[] src, int n) throws IOException {
+	private ByteBuffer filterClean(byte[] src, int n, OperationType opType)
+			throws IOException {
 		InputStream in = new ByteArrayInputStream(src);
 		try {
-			return IO.readWholeStream(filterClean(in), n);
+			return IO.readWholeStream(filterClean(in, opType), n);
 		} finally {
 			safeClose(in);
 		}
 	}
 
 	private InputStream filterClean(InputStream in) throws IOException {
-		in = handleAutoCRLF(in);
+		return filterClean(in, null);
+	}
+
+	private InputStream filterClean(InputStream in, OperationType opType)
+			throws IOException {
+		in = handleAutoCRLF(in, opType);
 		String filterCommand = getCleanFilterCommand();
 		if (filterCommand != null) {
 			FS fs = repository.getFS();
@@ -467,12 +485,9 @@
 		return in;
 	}
 
-	private InputStream handleAutoCRLF(InputStream in) {
-		AutoCRLF autoCRLF = getOptions().getAutoCRLF();
-		if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) {
-			in = new EolCanonicalizingInputStream(in, true);
-		}
-		return in;
+	private InputStream handleAutoCRLF(InputStream in, OperationType opType)
+			throws IOException {
+		return EolStreamTypeUtil.wrapInputStream(in, getEolStreamType(opType));
 	}
 
 	/**
@@ -531,7 +546,8 @@
 		System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen);
 		pathLen = pathOffset + nameLen;
 		canonLen = -1;
-		cleanFilterCommand = null;
+		cleanFilterCommandHolder = null;
+		eolStreamTypeHolder = null;
 	}
 
 	/**
@@ -594,10 +610,11 @@
 	 */
 	public InputStream openEntryStream() throws IOException {
 		InputStream rawis = current().openInputStream();
-		if (mightNeedCleaning())
-			return filterClean(rawis);
-		else
+		if (getCleanFilterCommand() == null
+				&& getEolStreamType() == EolStreamType.DIRECT)
 			return rawis;
+		else
+			return filterClean(rawis);
 	}
 
 	/**
@@ -921,17 +938,31 @@
 	 */
 	public FileMode getIndexFileMode(final DirCacheIterator indexIter) {
 		final FileMode wtMode = getEntryFileMode();
-		if (indexIter == null)
+		if (indexIter == null) {
 			return wtMode;
-		if (getOptions().isFileMode())
-			return wtMode;
+		}
 		final FileMode iMode = indexIter.getEntryFileMode();
-		if (FileMode.REGULAR_FILE == wtMode
-				&& FileMode.EXECUTABLE_FILE == iMode)
+		if (getOptions().isFileMode() && iMode != FileMode.GITLINK && iMode != FileMode.TREE) {
+			return wtMode;
+		}
+		if (!getOptions().isFileMode()) {
+			if (FileMode.REGULAR_FILE == wtMode
+					&& FileMode.EXECUTABLE_FILE == iMode) {
+				return iMode;
+			}
+			if (FileMode.EXECUTABLE_FILE == wtMode
+					&& FileMode.REGULAR_FILE == iMode) {
+				return iMode;
+			}
+		}
+		if (FileMode.GITLINK == iMode
+				&& FileMode.TREE == wtMode) {
 			return iMode;
-		if (FileMode.EXECUTABLE_FILE == wtMode
-				&& FileMode.REGULAR_FILE == iMode)
+		}
+		if (FileMode.TREE == iMode
+				&& FileMode.GITLINK == wtMode) {
 			return iMode;
+		}
 		return wtMode;
 	}
 
@@ -971,10 +1002,11 @@
 			// Content differs: that's a real change, perhaps
 			if (reader == null) // deprecated use, do no further checks
 				return true;
-			switch (getOptions().getAutoCRLF()) {
-			case INPUT:
-			case TRUE:
-				InputStream dcIn = null;
+
+			switch (getEolStreamType()) {
+			case DIRECT:
+				return true;
+			default:
 				try {
 					ObjectLoader loader = reader.open(entry.getObjectId());
 					if (loader == null)
@@ -982,37 +1014,26 @@
 
 					// We need to compute the length, but only if it is not
 					// a binary stream.
-					dcIn = new EolCanonicalizingInputStream(
-							loader.openStream(), true, true /* abort if binary */);
 					long dcInLen;
-					try {
+					try (InputStream dcIn = new AutoLFInputStream(
+							loader.openStream(), true,
+							true /* abort if binary */)) {
 						dcInLen = computeLength(dcIn);
-					} catch (EolCanonicalizingInputStream.IsBinaryException e) {
+					} catch (AutoLFInputStream.IsBinaryException e) {
 						return true;
-					} finally {
-						dcIn.close();
 					}
 
-					dcIn = new EolCanonicalizingInputStream(
-							loader.openStream(), true);
-					byte[] autoCrLfHash = computeHash(dcIn, dcInLen);
-					boolean changed = getEntryObjectId().compareTo(
-							autoCrLfHash, 0) != 0;
-					return changed;
+					try (InputStream dcIn = new AutoLFInputStream(
+							loader.openStream(), true)) {
+						byte[] autoCrLfHash = computeHash(dcIn, dcInLen);
+						boolean changed = getEntryObjectId()
+								.compareTo(autoCrLfHash, 0) != 0;
+						return changed;
+					}
 				} catch (IOException e) {
 					return true;
-				} finally {
-					if (dcIn != null)
-						try {
-							dcIn.close();
-						} catch (IOException e) {
-							// empty
-						}
 				}
-			case FALSE:
-				break;
 			}
-			return true;
 		}
 	}
 
@@ -1308,10 +1329,61 @@
 	 * @since 4.2
 	 */
 	public String getCleanFilterCommand() throws IOException {
-		if (cleanFilterCommand == null && state.walk != null) {
-			cleanFilterCommand = state.walk
-					.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
+		if (cleanFilterCommandHolder == null) {
+			String cmd = null;
+			if (state.walk != null) {
+				cmd = state.walk
+						.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
+			}
+			cleanFilterCommandHolder = new Holder<String>(cmd);
 		}
-		return cleanFilterCommand;
+		return cleanFilterCommandHolder.get();
+	}
+
+	/**
+	 * @return the eol stream type for the current entry or <code>null</code> if
+	 *         it cannot be determined. When state or state.walk is null or the
+	 *         {@link TreeWalk} is not based on a {@link Repository} then null
+	 *         is returned.
+	 * @throws IOException
+	 * @since 4.3
+	 */
+	public EolStreamType getEolStreamType() throws IOException {
+		return getEolStreamType(null);
+	}
+
+	/**
+	 * @param opType
+	 *            The operationtype (checkin/checkout) which should be used
+	 * @return the eol stream type for the current entry or <code>null</code> if
+	 *         it cannot be determined. When state or state.walk is null or the
+	 *         {@link TreeWalk} is not based on a {@link Repository} then null
+	 *         is returned.
+	 * @throws IOException
+	 */
+	private EolStreamType getEolStreamType(OperationType opType)
+			throws IOException {
+		if (eolStreamTypeHolder == null) {
+			EolStreamType type=null;
+			if (state.walk != null) {
+				if (opType != null) {
+					type = state.walk.getEolStreamType(opType);
+				} else {
+					type=state.walk.getEolStreamType();
+				}
+			} else {
+				switch (getOptions().getAutoCRLF()) {
+				case FALSE:
+					type = EolStreamType.DIRECT;
+					break;
+				case TRUE:
+				case INPUT:
+					type = EolStreamType.AUTO_LF;
+					break;
+				}
+			}
+			eolStreamTypeHolder = new Holder<EolStreamType>(type);
+		}
+		return eolStreamTypeHolder.get();
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
index a6dccce..dea07c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
@@ -48,6 +48,7 @@
 import org.eclipse.jgit.lib.Config.SectionParser;
 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 import org.eclipse.jgit.lib.CoreConfig.CheckStat;
+import org.eclipse.jgit.lib.CoreConfig.EOL;
 import org.eclipse.jgit.lib.CoreConfig.HideDotFiles;
 import org.eclipse.jgit.lib.CoreConfig.SymLinks;
 
@@ -64,17 +65,23 @@
 
 	private final AutoCRLF autoCRLF;
 
+	private final EOL eol;
+
 	private final CheckStat checkStat;
 
 	private final SymLinks symlinks;
 
 	private final HideDotFiles hideDotFiles;
 
+	private final boolean dirNoGitLinks;
+
 	private WorkingTreeOptions(final Config rc) {
 		fileMode = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
 				ConfigConstants.CONFIG_KEY_FILEMODE, true);
 		autoCRLF = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.FALSE);
+		eol = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_EOL, EOL.NATIVE);
 		checkStat = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_CHECKSTAT, CheckStat.DEFAULT);
 		symlinks = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
@@ -82,6 +89,9 @@
 		hideDotFiles = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_HIDEDOTFILES,
 				HideDotFiles.DOTGITONLY);
+		dirNoGitLinks = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_DIRNOGITLINKS,
+				false);
 	}
 
 	/** @return true if the execute bit on working files should be trusted. */
@@ -95,6 +105,15 @@
 	}
 
 	/**
+	 * @return how text line endings should be normalized.
+	 *
+	 * @since 4.3
+	 */
+	public EOL getEOL() {
+		return eol;
+	}
+
+	/**
 	 * @return how stat data is compared
 	 * @since 3.0
 	 */
@@ -117,4 +136,12 @@
 	public HideDotFiles getHideDotFiles() {
 		return hideDotFiles;
 	}
+
+	/**
+	 * @return whether or not we treat nested repos as directories.
+	 * 		   If true, folders containing .git entries will not be
+	 * 		   treated as gitlinks.
+	 * @since 4.3
+	 */
+	public boolean isDirNoGitLinks() { return dirNoGitLinks; }
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java
new file mode 100644
index 0000000..3563e1b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * 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;
+
+/**
+ * Holder of an object.
+ *
+ * @param <T>
+ *            the type of value held by this {@link Holder}
+ *
+ * @since 4.3
+ */
+public class Holder<T> {
+	private T value;
+
+	/**
+	 * @param value
+	 *            is the initial value that is {@link #set(Object)}
+	 */
+	public Holder(T value) {
+		set(value);
+	}
+
+	/**
+	 * @return the value held by this {@link Holder}
+	 */
+	public T get() {
+		return value;
+	}
+
+	/**
+	 * @param value
+	 *            to be set as new value held by this {@link Holder}
+	 */
+	public void set(T value) {
+		this.value = value;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 8b4ad0a..7cb2bf6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -52,8 +52,16 @@
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLEncoder;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
 import java.text.MessageFormat;
 
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.transport.http.HttpConnection;
 
@@ -62,6 +70,14 @@
 	/** The {@code GET} HTTP method. */
 	public static final String METHOD_GET = "GET"; //$NON-NLS-1$
 
+	/** The {@code HEAD} HTTP method.
+	 * @since 4.3 */
+	public static final String METHOD_HEAD = "HEAD"; //$NON-NLS-1$
+
+	/** The {@code POST} HTTP method.
+	 * @since 4.3 */
+	public static final String METHOD_PUT = "PUT"; //$NON-NLS-1$
+
 	/** The {@code POST} HTTP method. */
 	public static final String METHOD_POST = "POST"; //$NON-NLS-1$
 
@@ -173,7 +189,8 @@
 		try {
 			return c.getResponseCode();
 		} catch (ConnectException ce) {
-			final String host = c.getURL().getHost();
+			final URL url = c.getURL();
+			final String host = (url == null) ? "<null>" : url.getHost();
 			// The standard J2SE error message is not very useful.
 			//
 			if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$
@@ -200,7 +217,8 @@
 		try {
 			return c.getResponseCode();
 		} catch (ConnectException ce) {
-			final String host = c.getURL().getHost();
+			final URL url = c.getURL();
+			final String host = (url == null) ? "<null>" : url.getHost();
 			// The standard J2SE error message is not very useful.
 			//
 			if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$
@@ -234,6 +252,50 @@
 		}
 	}
 
+	/**
+	 * Disable SSL and hostname verification for given HTTP connection
+	 *
+	 * @param conn
+	 * @throws IOException
+	 * @since 4.3
+	 */
+	public static void disableSslVerify(HttpConnection conn)
+			throws IOException {
+		final TrustManager[] trustAllCerts = new TrustManager[] {
+				new DummyX509TrustManager() };
+		try {
+			conn.configure(null, trustAllCerts, null);
+			conn.setHostnameVerifier(new DummyHostnameVerifier());
+		} catch (KeyManagementException e) {
+			throw new IOException(e.getMessage());
+		} catch (NoSuchAlgorithmException e) {
+			throw new IOException(e.getMessage());
+		}
+	}
+
+	private static class DummyX509TrustManager implements X509TrustManager {
+		public X509Certificate[] getAcceptedIssuers() {
+			return null;
+		}
+
+		public void checkClientTrusted(X509Certificate[] certs,
+				String authType) {
+			// no check
+		}
+
+		public void checkServerTrusted(X509Certificate[] certs,
+				String authType) {
+			// no check
+		}
+	}
+
+	private static class DummyHostnameVerifier implements HostnameVerifier {
+		public boolean verify(String hostname, SSLSession session) {
+			// always accept
+			return true;
+		}
+	}
+
 	private HttpSupport() {
 		// Utility class only.
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
index 85c1648..8536f1d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, 2015 Shawn O. Pearce <spearce@spearce.org>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -66,6 +66,37 @@
 	}
 
 	/**
+	 * Compare a 64 bit unsigned integer stored in a 64 bit signed integer.
+	 * <p>
+	 * This function performs an unsigned compare operation, even though Java
+	 * does not natively support unsigned integer values. Negative numbers are
+	 * treated as larger than positive ones.
+	 *
+	 * @param a
+	 *            the first value to compare.
+	 * @param b
+	 *            the second value to compare.
+	 * @return &lt; 0 if a &lt; b; 0 if a == b; &gt; 0 if a &gt; b.
+	 * @since 4.3
+	 */
+	public static int compareUInt64(final long a, final long b) {
+		long cmp = (a >>> 1) - (b >>> 1);
+		if (cmp > 0) {
+			return 1;
+		} else if (cmp < 0) {
+			return -1;
+		}
+		cmp = ((a & 1) - (b & 1));
+		if (cmp > 0) {
+			return 1;
+		} else if (cmp < 0) {
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+
+	/**
 	 * Convert sequence of 2 bytes (network byte order) into unsigned value.
 	 *
 	 * @param intbuf
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index f2955f7..86777b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -366,6 +366,72 @@
 	}
 
 	/**
+	 * Parse 16 character base 16 (hex) formatted string to unsigned long.
+	 * <p>
+	 * The number is read in network byte order, that is, most significant
+	 * nibble first.
+	 *
+	 * @param bs
+	 *            buffer to parse digits from; positions {@code [p, p+16)} will
+	 *            be parsed.
+	 * @param p
+	 *            first position within the buffer to parse.
+	 * @return the integer value.
+	 * @throws ArrayIndexOutOfBoundsException
+	 *             if the string is not hex formatted.
+	 * @since 4.3
+	 */
+	public static final long parseHexInt64(final byte[] bs, final int p) {
+		long r = digits16[bs[p]] << 4;
+
+		r |= digits16[bs[p + 1]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 2]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 3]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 4]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 5]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 6]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 7]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 8]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 9]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 10]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 11]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 12]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 13]];
+		r <<= 4;
+
+		r |= digits16[bs[p + 14]];
+
+		final int last = digits16[bs[p + 15]];
+		if (r < 0 || last < 0)
+			throw new ArrayIndexOutOfBoundsException();
+		return (r << 4) | last;
+	}
+
+	/**
 	 * Parse a single hex digit to its numeric value (0-15).
 	 *
 	 * @param digit
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
index 98c5477..30f9ce9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
@@ -50,7 +50,7 @@
 import org.eclipse.jgit.diff.RawText;
 
 /**
- * An OutputStream that expands LF to CRLF.
+ * An InputStream that expands LF to CRLF.
  *
  * Existing CRLF are not expanded to CRCRLF, but retained as is.
  *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
index f05da1c..3a72f7e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
@@ -50,8 +50,11 @@
 
 /**
  * An OutputStream that expands LF to CRLF.
- * <p>
+ *
  * Existing CRLF are not expanded to CRCRLF, but retained as is.
+ *
+ * A binary check on the first 8000 bytes is performed and in case of binary
+ * files, canonicalization is turned off (for the complete file).
  */
 public class AutoCRLFOutputStream extends OutputStream {
 
@@ -67,13 +70,26 @@
 
 	private int binbufcnt = 0;
 
+	private boolean detectBinary;
+
 	private boolean isBinary;
 
 	/**
 	 * @param out
 	 */
 	public AutoCRLFOutputStream(OutputStream out) {
+		this(out, true);
+	}
+
+	/**
+	 * @param out
+	 * @param detectBinary
+	 *            whether binaries should be detected
+	 * @since 4.3
+	 */
+	public AutoCRLFOutputStream(OutputStream out, boolean detectBinary) {
 		this.out = out;
+		this.detectBinary = detectBinary;
 	}
 
 	@Override
@@ -141,7 +157,10 @@
 	}
 
 	private void decideMode() throws IOException {
-		isBinary = RawText.isBinary(binbuf, binbufcnt);
+		if (detectBinary) {
+			isBinary = RawText.isBinary(binbuf, binbufcnt);
+			detectBinary = false;
+		}
 		int cachedLen = binbufcnt;
 		binbufcnt = binbuf.length + 1; // full!
 		write(binbuf, 0, cachedLen);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
new file mode 100644
index 0000000..6e33f99
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com>
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jgit.diff.RawText;
+
+/**
+ * An InputStream that normalizes CRLF to LF.
+ *
+ * Existing single CR are not changed to LF, but retained as is.
+ *
+ * Optionally, a binary check on the first 8000 bytes is performed and in case
+ * of binary files, canonicalization is turned off (for the complete file).
+ * <p>
+ * This is the former EolCanonicalizingInputStream with a new name in order to
+ * have same naming for all LF / CRLF streams
+ *
+ * @since 4.3
+ */
+public class AutoLFInputStream extends InputStream {
+	private final byte[] single = new byte[1];
+
+	private final byte[] buf = new byte[8096];
+
+	private final InputStream in;
+
+	private int cnt;
+
+	private int ptr;
+
+	private boolean isBinary;
+
+	private boolean detectBinary;
+
+	private boolean abortIfBinary;
+
+	/**
+	 * A special exception thrown when {@link AutoLFInputStream} is told to
+	 * throw an exception when attempting to read a binary file. The exception
+	 * may be thrown at any stage during reading.
+	 *
+	 * @since 3.3
+	 */
+	public static class IsBinaryException extends IOException {
+		private static final long serialVersionUID = 1L;
+
+		IsBinaryException() {
+			super();
+		}
+	}
+
+	/**
+	 * Creates a new InputStream, wrapping the specified stream
+	 *
+	 * @param in
+	 *            raw input stream
+	 * @param detectBinary
+	 *            whether binaries should be detected
+	 * @since 2.0
+	 */
+	public AutoLFInputStream(InputStream in, boolean detectBinary) {
+		this(in, detectBinary, false);
+	}
+
+	/**
+	 * Creates a new InputStream, wrapping the specified stream
+	 *
+	 * @param in
+	 *            raw input stream
+	 * @param detectBinary
+	 *            whether binaries should be detected
+	 * @param abortIfBinary
+	 *            throw an IOException if the file is binary
+	 * @since 3.3
+	 */
+	public AutoLFInputStream(InputStream in, boolean detectBinary,
+			boolean abortIfBinary) {
+		this.in = in;
+		this.detectBinary = detectBinary;
+		this.abortIfBinary = abortIfBinary;
+	}
+
+	@Override
+	public int read() throws IOException {
+		final int read = read(single, 0, 1);
+		return read == 1 ? single[0] & 0xff : -1;
+	}
+
+	@Override
+	public int read(byte[] bs, final int off, final int len)
+			throws IOException {
+		if (len == 0)
+			return 0;
+
+		if (cnt == -1)
+			return -1;
+
+		int i = off;
+		final int end = off + len;
+
+		while (i < end) {
+			if (ptr == cnt && !fillBuffer()) {
+				break;
+			}
+
+			byte b = buf[ptr++];
+			if (isBinary || b != '\r') {
+				// Logic for binary files ends here
+				bs[i++] = b;
+				continue;
+			}
+
+			if (ptr == cnt && !fillBuffer()) {
+				bs[i++] = '\r';
+				break;
+			}
+
+			if (buf[ptr] == '\n') {
+				bs[i++] = '\n';
+				ptr++;
+			} else
+				bs[i++] = '\r';
+		}
+
+		return i == off ? -1 : i - off;
+	}
+
+	/**
+	 * @return true if the stream has detected as a binary so far
+	 * @since 3.3
+	 */
+	public boolean isBinary() {
+		return isBinary;
+	}
+
+	@Override
+	public void close() throws IOException {
+		in.close();
+	}
+
+	private boolean fillBuffer() throws IOException {
+		cnt = in.read(buf, 0, buf.length);
+		if (cnt < 1)
+			return false;
+		if (detectBinary) {
+			isBinary = RawText.isBinary(buf, cnt);
+			detectBinary = false;
+			if (isBinary && abortIfBinary)
+				throw new IsBinaryException();
+		}
+		ptr = 0;
+		return true;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java
new file mode 100644
index 0000000..c932b00
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * 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.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.diff.RawText;
+
+/**
+ * An OutputStream that reduces CRLF to LF.
+ *
+ * Existing single CR are not changed to LF, but retained as is.
+ *
+ * A binary check on the first 8000 bytes is performed and in case of binary
+ * files, canonicalization is turned off (for the complete file).
+ *
+ * @since 4.3
+ */
+public class AutoLFOutputStream extends OutputStream {
+
+	static final int BUFFER_SIZE = 8000;
+
+	private final OutputStream out;
+
+	private int buf = -1;
+
+	private byte[] binbuf = new byte[BUFFER_SIZE];
+
+	private byte[] onebytebuf = new byte[1];
+
+	private int binbufcnt = 0;
+
+	private boolean detectBinary;
+
+	private boolean isBinary;
+
+	/**
+	 * @param out
+	 */
+	public AutoLFOutputStream(OutputStream out) {
+		this(out, true);
+	}
+
+	/**
+	 * @param out
+	 * @param detectBinary
+	 *            whether binaries should be detected
+	 */
+	public AutoLFOutputStream(OutputStream out, boolean detectBinary) {
+		this.out = out;
+		this.detectBinary = detectBinary;
+	}
+
+	@Override
+	public void write(int b) throws IOException {
+		onebytebuf[0] = (byte) b;
+		write(onebytebuf, 0, 1);
+	}
+
+	@Override
+	public void write(byte[] b) throws IOException {
+		int overflow = buffer(b, 0, b.length);
+		if (overflow > 0) {
+			write(b, b.length - overflow, overflow);
+		}
+	}
+
+	@Override
+	public void write(byte[] b, final int startOff, final int startLen)
+			throws IOException {
+		final int overflow = buffer(b, startOff, startLen);
+		if (overflow < 0) {
+			return;
+		}
+		final int off = startOff + startLen - overflow;
+		final int len = overflow;
+		if (len == 0) {
+			return;
+		}
+		int lastw = off;
+		if (isBinary) {
+			out.write(b, off, len);
+			return;
+		}
+		for (int i = off; i < off + len; ++i) {
+			final byte c = b[i];
+			if (c == '\r') {
+				// skip write r but backlog r
+				if (lastw < i) {
+					out.write(b, lastw, i - lastw);
+				}
+				lastw = i + 1;
+				buf = '\r';
+			} else if (c == '\n') {
+				if (buf == '\r') {
+					out.write('\n');
+					lastw = i + 1;
+					buf = -1;
+				} else {
+					if (lastw < i + 1) {
+						out.write(b, lastw, i + 1 - lastw);
+					}
+					lastw = i + 1;
+				}
+			} else {
+				if (buf == '\r') {
+					out.write('\r');
+					lastw = i;
+				}
+				buf = -1;
+			}
+		}
+		if (lastw < off + len) {
+			out.write(b, lastw, off + len - lastw);
+		}
+	}
+
+	private int buffer(byte[] b, int off, int len) throws IOException {
+		if (binbufcnt > binbuf.length) {
+			return len;
+		}
+		int copy = Math.min(binbuf.length - binbufcnt, len);
+		System.arraycopy(b, off, binbuf, binbufcnt, copy);
+		binbufcnt += copy;
+		int remaining = len - copy;
+		if (remaining > 0) {
+			decideMode();
+		}
+		return remaining;
+	}
+
+	private void decideMode() throws IOException {
+		if (detectBinary) {
+			isBinary = RawText.isBinary(binbuf, binbufcnt);
+			detectBinary = false;
+		}
+		int cachedLen = binbufcnt;
+		binbufcnt = binbuf.length + 1; // full!
+		write(binbuf, 0, cachedLen);
+	}
+
+	@Override
+	public void flush() throws IOException {
+		if (binbufcnt <= binbuf.length) {
+			decideMode();
+		}
+		out.flush();
+	}
+
+	@Override
+	public void close() throws IOException {
+		flush();
+		if (buf == '\r') {
+			out.write(buf);
+			buf = -1;
+		}
+		out.close();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
index 98485e9..ee729e8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
@@ -46,46 +46,16 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-import org.eclipse.jgit.diff.RawText;
-
 /**
  * An input stream which canonicalizes EOLs bytes on the fly to '\n'.
  *
- * Optionally, a binary check on the first 8000 bytes is performed
- * and in case of binary files, canonicalization is turned off
- * (for the complete file).
+ * Optionally, a binary check on the first 8000 bytes is performed and in case
+ * of binary files, canonicalization is turned off (for the complete file).
+ *
+ * @deprecated use {@link AutoLFInputStream} instead
  */
-public class EolCanonicalizingInputStream extends InputStream {
-	private final byte[] single = new byte[1];
-
-	private final byte[] buf = new byte[8096];
-
-	private final InputStream in;
-
-	private int cnt;
-
-	private int ptr;
-
-	private boolean isBinary;
-
-	private boolean detectBinary;
-
-	private boolean abortIfBinary;
-
-	/**
-	 * A special exception thrown when {@link EolCanonicalizingInputStream} is
-	 * told to throw an exception when attempting to read a binary file. The
-	 * exception may be thrown at any stage during reading.
-	 *
-	 * @since 3.3
-	 */
-	public static class IsBinaryException extends IOException {
-		private static final long serialVersionUID = 1L;
-
-		IsBinaryException() {
-			super();
-		}
-	}
+@Deprecated
+public class EolCanonicalizingInputStream extends AutoLFInputStream {
 
 	/**
 	 * Creates a new InputStream, wrapping the specified stream
@@ -94,10 +64,9 @@
 	 *            raw input stream
 	 * @param detectBinary
 	 *            whether binaries should be detected
-	 * @since 2.0
 	 */
 	public EolCanonicalizingInputStream(InputStream in, boolean detectBinary) {
-		this(in, detectBinary, false);
+		super(in, detectBinary);
 	}
 
 	/**
@@ -109,83 +78,25 @@
 	 *            whether binaries should be detected
 	 * @param abortIfBinary
 	 *            throw an IOException if the file is binary
-	 * @since 3.3
 	 */
 	public EolCanonicalizingInputStream(InputStream in, boolean detectBinary,
 			boolean abortIfBinary) {
-		this.in = in;
-		this.detectBinary = detectBinary;
-		this.abortIfBinary = abortIfBinary;
-	}
-
-	@Override
-	public int read() throws IOException {
-		final int read = read(single, 0, 1);
-		return read == 1 ? single[0] & 0xff : -1;
-	}
-
-	@Override
-	public int read(byte[] bs, final int off, final int len) throws IOException {
-		if (len == 0)
-			return 0;
-
-		if (cnt == -1)
-			return -1;
-
-		int i = off;
-		final int end = off + len;
-
-		while (i < end) {
-			if (ptr == cnt && !fillBuffer()) {
-				break;
-			}
-
-			byte b = buf[ptr++];
-			if (isBinary || b != '\r') {
-				// Logic for binary files ends here
-				bs[i++] = b;
-				continue;
-			}
-
-			if (ptr == cnt && !fillBuffer()) {
-				bs[i++] = '\r';
-				break;
-			}
-
-			if (buf[ptr] == '\n') {
-				bs[i++] = '\n';
-				ptr++;
-			} else
-				bs[i++] = '\r';
-		}
-
-		return i == off ? -1 : i - off;
+		super(in, detectBinary, abortIfBinary);
 	}
 
 	/**
-	 * @return true if the stream has detected as a binary so far
+	 * A special exception thrown when {@link AutoLFInputStream} is told to
+	 * throw an exception when attempting to read a binary file. The exception
+	 * may be thrown at any stage during reading.
+	 *
 	 * @since 3.3
 	 */
-	public boolean isBinary() {
-		return isBinary;
-	}
+	public static class IsBinaryException extends IOException {
+		private static final long serialVersionUID = 1L;
 
-	@Override
-	public void close() throws IOException {
-		in.close();
-	}
-
-	private boolean fillBuffer() throws IOException {
-		cnt = in.read(buf, 0, buf.length);
-		if (cnt < 1)
-			return false;
-		if (detectBinary) {
-			isBinary = RawText.isBinary(buf, cnt);
-			detectBinary = false;
-			if (isBinary && abortIfBinary)
-				throw new IsBinaryException();
+		IsBinaryException() {
+			super();
 		}
-		ptr = 0;
-		return true;
 	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
new file mode 100644
index 0000000..c95992f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * 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.io;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
+import org.eclipse.jgit.treewalk.WorkingTreeOptions;
+
+/**
+ * Utility used to create input and output stream wrappers for
+ * {@link EolStreamType}
+ *
+ * @since 4.3
+ */
+public final class EolStreamTypeUtil {
+	private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false;
+
+	private EolStreamTypeUtil() {
+	}
+
+	/**
+	 * Convenience method used to detect if CRLF conversion has been configured
+	 * using the
+	 * <ul>
+	 * <li>global repo options</li>
+	 * <li>global attributes</li>
+	 * <li>info attributes</li>
+	 * <li>working tree .gitattributes</li>
+	 *
+	 * @param op
+	 *            is the {@link OperationType} of the current traversal
+	 * @param options
+	 *            are the {@link Config} options with key
+	 *            {@link WorkingTreeOptions#KEY}
+	 * @param attrs
+	 *            are the {@link Attributes} of the file for which the
+	 *            {@link EolStreamType} is to be detected
+	 *
+	 * @return the stream conversion {@link EolStreamType} to be performed for
+	 *         the selected {@link OperationType}
+	 */
+	public static EolStreamType detectStreamType(OperationType op,
+			WorkingTreeOptions options, Attributes attrs) {
+		switch (op) {
+		case CHECKIN_OP:
+			return checkInStreamType(options, attrs);
+		case CHECKOUT_OP:
+			return checkOutStreamType(options, attrs);
+		default:
+			throw new IllegalArgumentException("unknown OperationType " + op); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * @param in
+	 *            original stream
+	 * @param conversion
+	 *            to be performed
+	 * @return the converted stream depending on {@link EolStreamType}
+	 */
+	public static InputStream wrapInputStream(InputStream in,
+			EolStreamType conversion) {
+		switch (conversion) {
+		case TEXT_CRLF:
+			return new AutoCRLFInputStream(in, false);
+		case TEXT_LF:
+			return new AutoLFInputStream(in, false);
+		case AUTO_CRLF:
+			return new AutoCRLFInputStream(in, true);
+		case AUTO_LF:
+			return new AutoLFInputStream(in, true);
+		default:
+			return in;
+		}
+	}
+
+	/**
+	 * @param out
+	 *            original stream
+	 * @param conversion
+	 *            to be performed
+	 * @return the converted stream depending on {@link EolStreamType}
+	 */
+	public static OutputStream wrapOutputStream(OutputStream out,
+			EolStreamType conversion) {
+		switch (conversion) {
+		case TEXT_CRLF:
+			return new AutoCRLFOutputStream(out, false);
+		case AUTO_CRLF:
+			return new AutoCRLFOutputStream(out, true);
+		case TEXT_LF:
+			return new AutoLFOutputStream(out, false);
+		case AUTO_LF:
+			return new AutoLFOutputStream(out, true);
+		default:
+			return out;
+		}
+	}
+
+	private static EolStreamType checkInStreamType(WorkingTreeOptions options,
+			Attributes attrs) {
+		// old git system
+		if (attrs.isSet("crlf")) {//$NON-NLS-1$
+			return EolStreamType.TEXT_LF;
+		} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
+			return EolStreamType.DIRECT;
+		} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
+			return EolStreamType.TEXT_LF;
+		}
+
+		// new git system
+		if (attrs.isUnset("text")) {//$NON-NLS-1$
+			return EolStreamType.DIRECT;
+		}
+		String eol = attrs.getValue("eol"); //$NON-NLS-1$
+		if (eol != null)
+			// check-in is always normalized to LF
+			return EolStreamType.TEXT_LF;
+
+		if (attrs.isSet("text")) { //$NON-NLS-1$
+			return EolStreamType.TEXT_LF;
+		}
+
+		if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
+			return EolStreamType.AUTO_LF;
+		}
+
+		switch (options.getAutoCRLF()) {
+		case TRUE:
+		case INPUT:
+			return EolStreamType.AUTO_LF;
+		case FALSE:
+			return EolStreamType.DIRECT;
+		}
+
+		return EolStreamType.DIRECT;
+	}
+
+	private static EolStreamType checkOutStreamType(WorkingTreeOptions options,
+			Attributes attrs) {
+		// old git system
+		if (attrs.isSet("crlf")) {//$NON-NLS-1$
+			return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
+					: EolStreamType.DIRECT;
+		} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
+			return EolStreamType.DIRECT;
+		} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
+			return EolStreamType.DIRECT;
+		}
+
+		// new git system
+		if (attrs.isUnset("text")) {//$NON-NLS-1$
+			return EolStreamType.DIRECT;
+		}
+		String eol = attrs.getValue("eol"); //$NON-NLS-1$
+		if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$
+			return EolStreamType.TEXT_CRLF;
+		if (eol != null && "lf".equals(eol)) //$NON-NLS-1$
+			return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
+					: EolStreamType.DIRECT;
+
+		if (attrs.isSet("text")) { //$NON-NLS-1$
+			switch (options.getAutoCRLF()) {
+			case TRUE:
+				return EolStreamType.TEXT_CRLF;
+			default:
+				// no decision
+			}
+			switch (options.getEOL()) {
+			case CRLF:
+				return EolStreamType.TEXT_CRLF;
+			case LF:
+				return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
+						: EolStreamType.DIRECT;
+			case NATIVE:
+			default:
+				return EolStreamType.DIRECT;
+			}
+		}
+
+		if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
+			switch (options.getAutoCRLF()) {
+			case TRUE:
+				return EolStreamType.AUTO_CRLF;
+			default:
+				// no decision
+			}
+			switch (options.getEOL()) {
+			case CRLF:
+				return EolStreamType.AUTO_CRLF;
+			case LF:
+				return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
+						: EolStreamType.DIRECT;
+			case NATIVE:
+			default:
+				return EolStreamType.DIRECT;
+			}
+		}
+
+		switch (options.getAutoCRLF()) {
+		case TRUE:
+			return EolStreamType.AUTO_CRLF;
+		default:
+			// no decision
+		}
+
+		return EolStreamType.DIRECT;
+	}
+
+}
diff --git a/pom.xml b/pom.xml
index 38060d0..1ff91cd 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.2.1-SNAPSHOT</version>
+  <version>4.3.2-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -192,7 +192,7 @@
     <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>4.1.0.201509280440-r</jgit-last-release-version>
+    <jgit-last-release-version>4.2.0.201601211800-r</jgit-last-release-version>
     <jsch-version>0.1.53</jsch-version>
     <javaewah-version>0.7.9</javaewah-version>
     <junit-version>4.11</junit-version>
@@ -208,6 +208,7 @@
     <log4j-version>1.2.15</log4j-version>
     <maven-javadoc-plugin-version>2.10.1</maven-javadoc-plugin-version>
     <tycho-extras-version>0.23.0</tycho-extras-version>
+    <gson-version>2.2.4</gson-version>
 
     <!-- Properties to enable jacoco code coverage analysis -->
     <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -632,6 +633,12 @@
           </exclusion>
         </exclusions>
       </dependency>
+
+      <dependency>
+        <groupId>com.google.code.gson</groupId>
+        <artifactId>gson</artifactId>
+        <version>${gson-version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 
@@ -767,6 +774,8 @@
     <module>org.eclipse.jgit.http.apache</module>
     <module>org.eclipse.jgit.http.server</module>
     <module>org.eclipse.jgit.pgm</module>
+    <module>org.eclipse.jgit.lfs</module>
+    <module>org.eclipse.jgit.lfs.server</module>
     <module>org.eclipse.jgit.junit</module>
     <module>org.eclipse.jgit.junit.http</module>
 
@@ -774,6 +783,8 @@
     <module>org.eclipse.jgit.ant.test</module>
     <module>org.eclipse.jgit.http.test</module>
     <module>org.eclipse.jgit.pgm.test</module>
+    <module>org.eclipse.jgit.lfs.test</module>
+    <module>org.eclipse.jgit.lfs.server.test</module>
   </modules>
 
 </project>
diff --git a/tools/maven-central/deploy.rb b/tools/maven-central/deploy.rb
index 8e7adc8..2744e77 100755
--- a/tools/maven-central/deploy.rb
+++ b/tools/maven-central/deploy.rb
@@ -55,6 +55,8 @@
              group + '.http.server',
              group + '.junit',
              group + '.junit.http',
+             group + '.lfs',
+             group + '.lfs.server',
              group + '.pgm',
              group + '.ui']
 
diff --git a/tools/maven-central/download.rb b/tools/maven-central/download.rb
index b7ea215..b6c2671 100755
--- a/tools/maven-central/download.rb
+++ b/tools/maven-central/download.rb
@@ -15,6 +15,8 @@
              group + '.http.server',
              group + '.junit',
              group + '.junit.http',
+             group + '.lfs',
+             group + '.lfs.server',
              group + '.pgm',
              group + '.ui']