Merge branch 'stable-4.2'
* stable-4.2:
RepoProject: Fix warnings about variable hiding
RepoTest: Open Git in try-with-resources
RepositoryResolveTest: Open Git in try-with-resource
RepositoryTestCase: Open autocloseable types in try-with-resource
ResetCommandTest: Use Git member in testHardResetAfterSquashMerge
ResolveMergerTest: Open Git in try-with-resource
RevCommitListTest: Open Git and RevWalk in try-with-resource
RevCommitParseTest: Open ObjectInserter.Formatter in try-with-resource
RevObjectTest: Open RevWalk in try-with-resource
RevTagParseTest: Open ObjectInserter.Formatter in try-with-resource
RevertCommandTest: Open Git in try-with-resource
SquashMessageFormatterTest: Open git in try-with-resource
StatusCommandTest: Open Git in try-with-resource
SubmoduleAddTest: Open Git in try-with-resource
SymlinksTest: Open git and TreeWalk in try-with-resource
T0003_BasicTest: Open autocloseable types in try-with-resource
TextHashFunctions: Fix warnings about variable hiding
TreeFilterTest: Open TreeWalk in try-with-resource
TreeWalkJava7Test: Open TreeWalk in try-with-resource
Fix diff for added and removed submodule
Change-Id: If3ecc63f6dfac55474d3c1dd2f4105371f3d24fb
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/BUCK b/BUCK
index f19b7bd..22ab592 100644
--- a/BUCK
+++ b/BUCK
@@ -30,6 +30,39 @@
)
java_library(
+ name = 'jgit-lfs',
+ exported_deps = [
+ ':jgit',
+ '//org.eclipse.jgit.lfs:jgit-lfs'
+ ],
+ visibility = ['PUBLIC'],
+)
+
+genrule(
+ name = 'jgit-lfs_src',
+ cmd = 'ln -s $(location //org.eclipse.jgit.lfs:jgit-lfs_src) $OUT',
+ out = 'jgit-lfs_src.zip',
+ visibility = ['PUBLIC'],
+)
+
+java_library(
+ name = 'jgit-lfs-server',
+ exported_deps = [
+ ':jgit',
+ ':jgit-lfs',
+ '//org.eclipse.jgit.lfs.server:jgit-lfs-server'
+ ],
+ visibility = ['PUBLIC'],
+)
+
+genrule(
+ name = 'jgit-lfs-server_src',
+ cmd = 'ln -s $(location //org.eclipse.jgit.lfs.server:jgit-lfs-server_src) $OUT',
+ out = 'jgit-lfs-server_src.zip',
+ visibility = ['PUBLIC'],
+)
+
+java_library(
name = 'junit',
exported_deps = [
':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..d43dcee 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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..8e702a2 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.0-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..6e2ad19 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.0.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.0,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.0";
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..4bfaba0 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.0-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..e6b7191 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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.0";
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..c3d04f6 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.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index b9c509f..d8371bc 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.0-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..2616526 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.3.0";
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..ae5bceb 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.0-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 setRequestProperty(String name, String value) {
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..28835f6 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.0.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.0",
+ org.eclipse.jgit.http.server.glue;version="4.3.0";
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.0";
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.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index e13e9fa..3d351a7 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.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 05e4f81..056ef39 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.http.server;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.junit.http;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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..e01367f 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.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index bec4178..f05cb30 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.http.server;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.0,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.0";
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..d0a7be1 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.0-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 @@ public class AppServer {
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 @@ public AppServer() {
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..8f50782 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.0,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.0";
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..fbf55b9 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.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit</artifactId>
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..47ed9c3
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/BUCK
@@ -0,0 +1,34 @@
+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',
+ ],
+ 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..d263c3a
--- /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.0.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.0,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.test;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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..15461aa
--- /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.0-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..35bf09b
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 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));
+ }
+}
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..b0ff740
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -0,0 +1,28 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.lfs.server
+Bundle-Version: 4.3.0.qualifier
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Export-Package: org.eclipse.jgit.lfs.server;version="4.3.0";
+ uses:="javax.servlet.http,
+ org.eclipse.jgit.lfs.lib",
+ org.eclipse.jgit.lfs.server.fs;version="4.3.0";
+ 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.0";x-internal:=true
+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.eclipse.jgit.annotations;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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..8cfdf3b
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -0,0 +1,158 @@
+<?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.0-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>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..752a774
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties
@@ -0,0 +1,4 @@
+corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted.
+invalidPathInfo=Invalid pathInfo ''{0}'' does not match ''/'{'SHA-256'}'''
+objectNotFound=Object ''{0}'' not found
+unsupportedOperation=Operation ''{0}'' 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..12da271
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java
@@ -0,0 +1,185 @@
+/*
+ * 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.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+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;
+ private AtomicObjectOutputStream out;
+
+ /**
+ * @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);
+ }
+
+ WritableByteChannel getWriteChannel(AnyLongObjectId id)
+ throws IOException {
+ Path path = getPath(id);
+ Files.createDirectories(path.getParent());
+ out = new AtomicObjectOutputStream(path, id);
+ return Channels.newChannel(out);
+ }
+
+ /**
+ * Abort the output stream
+ */
+ void abortWrite() {
+ if (out != null) {
+ out.abort();
+ }
+ }
+
+ 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..05970ad
--- /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 FileLfsRepository repository;
+
+ private final ServletInputStream in;
+
+ private final ReadableByteChannel inChannel;
+
+ private WritableByteChannel out;
+
+ 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.repository = repository;
+ this.context = context;
+ this.response = response;
+ this.in = request.getInputStream();
+ this.inChannel = Channels.newChannel(in);
+ this.out = repository.getWriteChannel(id);
+ 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();
+ out.write(buffer);
+ buffer.compact();
+ } else {
+ buffer.flip();
+ while (buffer.hasRemaining()) {
+ out.write(buffer);
+ }
+ close();
+ return;
+ }
+ }
+ }
+
+ /**
+ * @throws IOException
+ */
+ @Override
+ public void onAllDataRead() throws IOException {
+ close();
+ }
+
+ protected void close() throws IOException {
+ try {
+ inChannel.close();
+ out.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 {
+ repository.abortWrite();
+ inChannel.close();
+ out.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..4e1c4c6
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.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.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 invalidPathInfo;
+ /***/ public String objectNotFound;
+ /***/ public String unsupportedOperation;
+}
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..61456f1
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.jgit.lfs.test
+Bundle-Version: 4.3.0.qualifier
+Bundle-Vendor: %provider_name
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.junit;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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.lib;version="4.3.0";x-internal:=true,
+ org.eclipse.jgit.lfs.test;version="4.3.0";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..ae72a80
--- /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.0-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..9a1fd7b
--- /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.0.qualifier
+Bundle-Localization: plugin
+Bundle-Vendor: %provider_name
+Export-Package: org.eclipse.jgit.lfs.errors;version="4.3.0",
+ org.eclipse.jgit.lfs.internal;version="4.3.0";x-friends:="org.eclipse.jgit.lfs.test",
+ org.eclipse.jgit.lfs.lib;version="4.3.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,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..bfee4ca
--- /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.0-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 <= 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 <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>;
+ * >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 <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>;
+ * >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 <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>;
+ * >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 >= 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 < 0 if this id comes before other; 0 if this id is equal to
+ * other; > 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..ba40ef8 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.0.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..9ce89e8 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.0-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..dcf0988 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.0.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..284d125 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.0-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..2a91700 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.0.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..a96852e 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.0-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..bb52857
--- /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.0.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..9d35816
--- /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.0-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..03c9fed 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.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -27,7 +27,7 @@
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"/>
</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..8bfec26 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.0-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..0ec4ed2 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.0.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..1cec2f2 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.0-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..15d10e6 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,9 @@
<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>
<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..9d842ef 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.0-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..0d82775 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.0.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..8ffeb14 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.0-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..8265e23 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.0.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..ce71222 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
@@ -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"/>
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/R20150821153341-Mars.tpd
index cb63807..5cacd71 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
@@ -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..66305f8 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.0-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..68efc46 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.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JGit Tycho Parent</name>
@@ -78,6 +78,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>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index b64f5f0..3ae7a80 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.diff;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.3.0",
+ org.eclipse.jgit.junit;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.pgm;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.0,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..1d24533 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.0-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..b654fc9 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -2,48 +2,68 @@
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 4.2.1.qualifier
+Bundle-Version: 4.3.0.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.0,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.archive;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.awtui;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.blame;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.diff;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.gitrepo;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.ketch;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.server;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.notes;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revplot;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.pack;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.0,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.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="4.2.1";
+ org.eclipse.jgit.pgm;version="4.3.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.pgm.opt,
@@ -54,11 +74,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.0";
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.0";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.3.0";
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..44c5a88 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.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.3.0.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..8d0bcf9 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.0-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..544e234 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
@@ -125,6 +125,7 @@
metaVar_revision=REVISION
metaVar_seconds=SECONDS
metaVar_service=SERVICE
+metaVar_lfsStorage=STORAGE
metaVar_treeish=tree-ish
metaVar_uriish=uri-ish
metaVar_url=URL
@@ -218,6 +219,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=fs: store lfs objects in file system
+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
@@ -347,6 +352,7 @@
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_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 @@ class Daemon extends TextBuiltin {
@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 @@ protected void run() throws Exception {
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 @@ private static DaemonService service(
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..18398f6
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
@@ -0,0 +1,249 @@
+/*
+ * 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.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 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.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.pgm.Command;
+import org.eclipse.jgit.pgm.TextBuiltin;
+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;
+ }
+
+ 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;
+
+ @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;
+
+ default:
+ throw new IllegalArgumentException(
+ "Unknown store type: " + storeType); //$NON-NLS-1$
+ }
+
+ 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("LFS protocol URL: " + getProtocolUrl(baseURI)); //$NON-NLS-1$
+ if (storeType == StoreType.FS) {
+ outw.println("LFS objects located in: " + directory); //$NON-NLS-1$
+ outw.println("LFS store URL: " + getStoreUrl(baseURI)); //$NON-NLS-1$
+ }
+ }
+
+ 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..fbd4672 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 = "set extensions.refsStorage = reftree")
+ boolean enable;
+
private String txnNamespace;
private String txnCommitted;
@@ -103,7 +110,7 @@ protected void run() throws Exception {
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,15 +129,31 @@ protected void run() throws Exception {
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()) {
+ 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().startsWith(txnNamespace)) {
continue;
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index abb86e8..dfe9a63 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.0.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.0,4.4.0)",
+ org.eclipse.jgit.api.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.attributes;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.awtui;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.blame;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.diff;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.dircache;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.events;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.fnmatch;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.gitrepo;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.hooks;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.ignore;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.junit;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.merge;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.notes;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.patch;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.pgm;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revplot;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.file;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.storage.pack;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.submodule;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.http;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util.io;version="[4.3.0,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..0bc22ac 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.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.test</artifactId>
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 524d0b8..164c4ba 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
@@ -747,6 +747,59 @@ public void testRecordRemoteBranch() throws Exception {
}
}
+ @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/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 @@ public void testGetRefs_EmptyDatabase() throws IOException {
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 @@ public void testUpdate_RefusesDotLockInRefName() throws IOException {
}
@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/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 @@ public void testCompareUInt32() {
}
@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.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index f9e1a39..93418e7 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.0.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.0"
+Import-Package: org.eclipse.jgit.errors;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.lib;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.nls;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revplot;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.revwalk;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.transport;version="[4.3.0,4.4.0)",
+ org.eclipse.jgit.util;version="[4.3.0,4.4.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index c80f31f..ccb0c95 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.0-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
deleted file mode 100644
index 36041f8..0000000
--- a/org.eclipse.jgit/.settings/.api_filters
+++ /dev/null
@@ -1,116 +0,0 @@
-<?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">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.attributes.AttributesNode"/>
- <message_argument value="getAttributes(String, boolean, Map<String,Attribute>)"/>
- </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">
- <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_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">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.lib.Repository"/>
- <message_argument value="createAttributesNodeProvider()"/>
- </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">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.PushCertificate"/>
- <message_argument value="PushCertificate()"/>
- </message_arguments>
- </filter>
- <filter comment="PushCertificate wasn't really usable in 4.0" id="338792546">
- <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_arguments>
- </filter>
- </resource>
-</component>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index d0f03ff..3e155a7 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.0.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.0",
+ org.eclipse.jgit.api;version="4.3.0";
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.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.3.0",
+ org.eclipse.jgit.blame;version="4.3.0";
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.0";
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.0";
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.0";
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.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.3.0",
+ org.eclipse.jgit.gitrepo;version="4.3.0";
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.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.3.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.3.0",
+ org.eclipse.jgit.ignore.internal;version="4.3.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.3.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.ketch;version="4.3.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.3.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
+ org.eclipse.jgit.internal.storage.file;version="4.3.0";
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.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.3.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.3.0";
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.0";
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.0",
+ org.eclipse.jgit.notes;version="4.3.0";
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.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.3.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.3.0";
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.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.3.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.3.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.3.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.3.0";
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.0";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.3.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.3.0";
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.0";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.3.0";
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.0"
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..6cdf122 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.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index e931990..a65ec96 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.0-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/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 @@ public class ManifestParser extends DefaultHandler {
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 @@ public ManifestParser(IncludedFileReader includedReader, String filename,
}
}
- remotes = new HashMap<String, String>();
+ remotes = new HashMap<String, Remote>();
projects = new ArrayList<RepoProject>();
filteredProjects = new ArrayList<RepoProject>();
}
@@ -195,14 +195,14 @@ public void startElement(
} 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 @@ public void endDocument() throws SAXException {
} 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 @@ public void endDocument() throws SAXException {
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 @@ private boolean isNestedCopyfile(CopyFile copyfile) {
}
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/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 @@ private DfsBlockCache(final DfsBlockCacheConfig cfg) {
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 @@ public boolean pack(ProgressMonitor pm) throws IOException {
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 @@ else if (RefTreeNames.isRefTree(refdb, ref.getName()))
}
}
+ 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..49f9335 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
@@ -629,15 +629,16 @@ private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOExceptio
}
/**
- * 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);
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 @@ public void close() {
@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 @@ private static ObjectId idOf(@Nullable Ref src) {
@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 @@ public BatchRefUpdate newBatchUpdate() {
@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 @@ boolean conflictsWithBootstrap(String name) {
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 @@ public static boolean isRefTree(RefDatabase refdb, String ref) {
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/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 class ChainingCredentialsProvider extends CredentialsProvider {
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 @@ public void setUploadPackFactory(UploadPackFactory<DaemonClient> factory) {
}
/**
+ * 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 @@ protected HttpConnection httpOpen(String method, URL u)
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 @@ protected HttpConnection httpOpen(String method, URL u)
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 @@ void execute() throws IOException {
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/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 8b4ad0a..6a43c0f 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 @@ public class HttpSupport {
/** 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$
@@ -234,6 +250,50 @@ public static Proxy proxyFor(final ProxySelector proxySelector, final URL u)
}
}
+ /**
+ * 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 @@ public static int compareUInt32(final int a, final int b) {
}
/**
+ * 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 < 0 if a < b; 0 if a == b; > 0 if a > 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 @@ public static final int parseHexInt32(final byte[] bs, final int p) {
}
/**
+ * 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/pom.xml b/pom.xml
index 38060d0..6bb30fe 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.0-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>