diff options
296 files changed, 20626 insertions, 1271 deletions
diff --git a/.buckconfig b/.buckconfig index b2e07acccf..8d2572bfda 100644 --- a/.buckconfig +++ b/.buckconfig @@ -1,3 +1,12 @@ +[alias] + all = //:all + jgit = //org.eclipse.jgit:jgit + jgit-archive = //org.eclipse.jgit.archive:jgit-archive + jgit_bin = //:jgit_bin + jgit-lfs = //org.eclipse.jgit.lfs:jgit-lfs + jgit-lfs-server = //org.eclipse.jgit.lfs.server:jgit-lfs-server + jgit-servlet = //org.eclipse.jgit.http.server:jgit-servlet + [buildfile] includes = //tools/default.defs diff --git a/.buckversion b/.buckversion index 9daac2cea3..561a769f10 100644 --- a/.buckversion +++ b/.buckversion @@ -1 +1 @@ -1b03b4313b91b634bd604fc3487a05f877e59dee +ca8d6cbac373a690f543c5159eec0116e76187a9 @@ -1,45 +1,18 @@ -java_library( - name = 'jgit', - exported_deps = ['//org.eclipse.jgit:jgit'], - visibility = ['PUBLIC'], -) - -genrule( - name = 'jgit_src', - cmd = 'ln -s $(location //org.eclipse.jgit:jgit_src) $OUT', - out = 'jgit_src.zip', - visibility = ['PUBLIC'], -) - -java_library( - name = 'jgit-servlet', - exported_deps = [ - ':jgit', - '//org.eclipse.jgit.http.server:jgit-servlet' - ], - visibility = ['PUBLIC'], -) - -java_library( - name = 'jgit-archive', - exported_deps = [ - ':jgit', - '//org.eclipse.jgit.archive:jgit-archive' - ], - visibility = ['PUBLIC'], -) +DEPS = [ + '//org.eclipse.jgit:jgit', + '//org.eclipse.jgit.archive:jgit-archive', + '//org.eclipse.jgit.http.server:jgit-servlet', + '//org.eclipse.jgit.lfs:jgit-lfs', + '//org.eclipse.jgit.lfs.server:jgit-lfs-server', + '//org.eclipse.jgit.pgm:jgit', +] -java_library( - name = 'junit', - exported_deps = [ - ':jgit', - '//org.eclipse.jgit.junit:junit' - ], - visibility = ['PUBLIC'], +zip_file( + name = 'all', + srcs = DEPS, ) -genrule( +sh_binary( name = 'jgit_bin', - cmd = 'ln -s $(location //org.eclipse.jgit.pgm:jgit) $OUT', - out = 'jgit_bin', + main = '//org.eclipse.jgit.pgm:jgit', ) @@ -123,3 +123,12 @@ maven_jar( 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 cfaefcde03..afa0f5c24b 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 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.ant.test -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.junit;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", + org.eclipse.jgit.ant.tasks;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.junit;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", org.hamcrest;version="[1.1.0,2.0.0)", org.junit;version="[4.0.0,5.0.0)" diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml index f1e7e36b8f..fc1257d644 100644 --- a/org.eclipse.jgit.ant.test/pom.xml +++ b/org.eclipse.jgit.ant.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant.test</artifactId> diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF index 74720fe034..98da2c0525 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)" + org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)" Bundle-Localization: plugin Bundle-Vendor: %Provider-Name -Export-Package: org.eclipse.jgit.ant.tasks;version="4.2.1"; +Export-Package: org.eclipse.jgit.ant.tasks;version="4.3.2"; uses:="org.apache.tools.ant.types,org.apache.tools.ant" diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index 0e694574f5..6fc806f132 100644 --- a/org.eclipse.jgit.ant/pom.xml +++ b/org.eclipse.jgit.ant/pom.xml @@ -48,7 +48,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant</artifactId> diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF index 4f3a61f393..a185d1f782 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 @@ -12,14 +12,14 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)", org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)", org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)", org.apache.commons.compress.compressors.xz;version="[1.4,2.0)", - org.eclipse.jgit.api;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", + org.eclipse.jgit.api;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", org.osgi.framework;version="[1.3.0,2.0.0)" Bundle-ActivationPolicy: lazy Bundle-Activator: org.eclipse.jgit.archive.FormatActivator -Export-Package: org.eclipse.jgit.archive;version="4.2.1"; +Export-Package: org.eclipse.jgit.archive;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.api, org.apache.commons.compress.archivers, diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF index 61fbb7e65e..cf164a9c29 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-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.archive - Sources Bundle-SymbolicName: org.eclipse.jgit.archive.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.2.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.2.1.qualifier";roots="." +Bundle-Version: 4.3.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.3.2.qualifier";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index b9c509fe47..7b9c6ac893 100644 --- a/org.eclipse.jgit.archive/pom.xml +++ b/org.eclipse.jgit.archive/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.archive</artifactId> diff --git a/org.eclipse.jgit.http.apache/BUCK b/org.eclipse.jgit.http.apache/BUCK index f48f33a1ae..3b7a8234f7 100644 --- a/org.eclipse.jgit.http.apache/BUCK +++ b/org.eclipse.jgit.http.apache/BUCK @@ -4,7 +4,6 @@ java_library( 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 b775549650..0872d4983a 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 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-Localization: plugin Bundle-Vendor: %Provider-Name Bundle-ActivationPolicy: lazy -Import-Package: org.apache.commons.logging;version="[1.1.1,2.0.0)", - org.apache.http;version="[4.1.0,5.0.0)", +Import-Package: org.apache.http;version="[4.1.0,5.0.0)", org.apache.http.client;version="[4.1.0,5.0.0)", org.apache.http.client.methods;version="[4.1.0,5.0.0)", org.apache.http.client.params;version="[4.1.0,5.0.0)", @@ -20,10 +19,10 @@ Import-Package: org.apache.commons.logging;version="[1.1.1,2.0.0)", org.apache.http.impl.client;version="[4.1.0,5.0.0)", org.apache.http.impl.client.cache;version="[4.1.0,5.0.0)", org.apache.http.params;version="[4.1.0,5.0.0)", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.http;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="4.2.1"; + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)" +Export-Package: org.eclipse.jgit.transport.http.apache;version="4.3.2"; uses:="org.eclipse.jgit.transport.http, javax.net.ssl, org.apache.http.client, diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index e2df65b63c..2b0b58d5f9 100644 --- a/org.eclipse.jgit.http.apache/pom.xml +++ b/org.eclipse.jgit.http.apache/pom.xml @@ -48,7 +48,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.apache</artifactId> diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java index de81bf82bf..a0eeef89fc 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.HttpResponse; 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 class HttpClientConnection implements HttpConnection { 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 9f87aab943..848b55e427 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 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.http.server;version="4.2.1", - org.eclipse.jgit.http.server.glue;version="4.2.1"; +Export-Package: org.eclipse.jgit.http.server;version="4.3.2", + org.eclipse.jgit.http.server.glue;version="4.3.2"; uses:="javax.servlet,javax.servlet.http", - org.eclipse.jgit.http.server.resolver;version="4.2.1"; + org.eclipse.jgit.http.server.resolver;version="4.3.2"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.lib, org.eclipse.jgit.transport, @@ -17,12 +17,12 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: javax.servlet;version="[2.5.0,3.2.0)", javax.servlet.http;version="[2.5.0,3.2.0)", - org.eclipse.jgit.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)" + org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)" diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index e13e9fa3aa..c6de937212 100644 --- a/org.eclipse.jgit.http.server/pom.xml +++ b/org.eclipse.jgit.http.server/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.server</artifactId> diff --git a/org.eclipse.jgit.http.test/BUCK b/org.eclipse.jgit.http.test/BUCK index d2ced7a247..d65cdadfc2 100644 --- a/org.eclipse.jgit.http.test/BUCK +++ b/org.eclipse.jgit.http.test/BUCK @@ -23,6 +23,7 @@ for t in TESTS: '//lib/jetty:servlet', '//lib/jetty:security', '//lib/jetty:util', + '//lib:commons-logging', ], source_under_test = ['//org.eclipse.jgit.http.server:jgit-servlet'], ) diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index 05e4f81315..fefdb66db3 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 @@ -22,23 +22,23 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.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.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.http.server;version="[4.2.1,4.3.0)", - org.eclipse.jgit.http.server.glue;version="[4.2.1,4.3.0)", - org.eclipse.jgit.http.server.resolver;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.junit;version="[4.2.1,4.3.0)", - org.eclipse.jgit.junit.http;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.http;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.http.apache;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", + org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.http.server;version="[4.3.2,4.4.0)", + org.eclipse.jgit.http.server.glue;version="[4.3.2,4.4.0)", + org.eclipse.jgit.http.server.resolver;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.junit;version="[4.3.2,4.4.0)", + org.eclipse.jgit.junit.http;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http.apache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.0.0,5.0.0)", org.junit.runner;version="[4.0.0,5.0.0)", diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index 44f7a38a89..8fc34f7417 100644 --- a/org.eclipse.jgit.http.test/pom.xml +++ b/org.eclipse.jgit.http.test/pom.xml @@ -51,7 +51,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.test</artifactId> diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index 82861ed9b7..0f3d3c6cf5 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -56,6 +56,7 @@ import static org.junit.Assert.fail; import java.io.IOException; import java.io.PrintWriter; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -89,6 +90,8 @@ import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdRef; +import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.lib.ReflogReader; @@ -487,6 +490,25 @@ public class SmartClientSmartServerTest extends HttpTestCase { } @Test + public void testInvalidWant() throws Exception { + @SuppressWarnings("resource") + ObjectId id = new ObjectInserter.Formatter().idFor(Constants.OBJ_BLOB, + "testInvalidWant".getBytes(StandardCharsets.UTF_8)); + + Repository dst = createBareRepository(); + try (Transport t = Transport.open(dst, remoteURI); + FetchConnection c = t.openFetch()) { + Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(), + id); + c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want), + Collections.<ObjectId> emptySet()); + fail("Server accepted want " + id.name()); + } catch (TransportException err) { + assertEquals("want " + id.name() + " not valid", err.getMessage()); + } + } + + @Test public void testPush_NotAuthorized() throws Exception { final TestRepository src = createTestRepository(); final RevBlob Q_txt = src.blob("new text"); diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index bec4178db9..bd26ac098d 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 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy @@ -20,16 +20,16 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.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.jgit.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.http.server;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.junit;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)", + org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.http.server;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.junit;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)", org.junit;version="[4.0.0,5.0.0)" -Export-Package: org.eclipse.jgit.junit.http;version="4.2.1"; +Export-Package: org.eclipse.jgit.junit.http;version="4.3.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.junit, javax.servlet.http, diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index 47be76bda2..43fdb8ac07 100644 --- a/org.eclipse.jgit.junit.http/pom.xml +++ b/org.eclipse.jgit.junit.http/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit.http</artifactId> diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java index ce04bdf2c6..c36c297623 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 class 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 794e3d32e2..89171bcc8a 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -2,27 +2,27 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.eclipse.jgit.api;version="[4.2.1,4.3.0)", - org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)", - org.eclipse.jgit.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.merge;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk.filter;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)", +Import-Package: org.eclipse.jgit.api;version="[4.3.2,4.4.0)", + org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.merge;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk.filter;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)", org.junit;version="[4.0.0,5.0.0)" -Export-Package: org.eclipse.jgit.junit;version="4.2.1"; +Export-Package: org.eclipse.jgit.junit;version="4.3.2"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index 4c7a85b1d2..bb2b6771b5 100644 --- a/org.eclipse.jgit.junit/pom.xml +++ b/org.eclipse.jgit.junit/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit</artifactId> diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java index 0db71f7cbc..86b87b4075 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java @@ -144,10 +144,10 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase { protected static void checkFile(File f, final String checkData) throws IOException { - Reader r = new InputStreamReader(new FileInputStream(f), "ISO-8859-1"); + Reader r = new InputStreamReader(new FileInputStream(f), "UTF-8"); try { - char[] data = new char[(int) f.length()]; - if (f.length() != r.read(data)) + char[] data = new char[checkData.length()]; + if (checkData.length() != r.read(data)) throw new IOException("Internal error reading file data from "+f); assertEquals(checkData, new String(data)); } finally { diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java index e259156c31..1119e63824 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java @@ -112,23 +112,18 @@ import org.eclipse.jgit.util.io.SafeBufferedOutputStream; * type of Repository the test data is stored on. */ public class TestRepository<R extends Repository> { - private static final PersonIdent defaultAuthor; - private static final PersonIdent defaultCommitter; + public static final String AUTHOR = "J. Author"; - static { - final MockSystemReader m = new MockSystemReader(); - final long now = m.getCurrentTime(); - final int tz = m.getTimezone(now); + public static final String AUTHOR_EMAIL = "jauthor@example.com"; - final String an = "J. Author"; - final String ae = "jauthor@example.com"; - defaultAuthor = new PersonIdent(an, ae, now, tz); + public static final String COMMITTER = "J. Committer"; - final String cn = "J. Committer"; - final String ce = "jcommitter@example.com"; - defaultCommitter = new PersonIdent(cn, ce, now, tz); - } + public static final String COMMITTER_EMAIL = "jcommitter@example.com"; + + private final PersonIdent defaultAuthor; + + private final PersonIdent defaultCommitter; private final R db; @@ -184,6 +179,10 @@ public class TestRepository<R extends Repository> { this.pool = rw; this.inserter = db.newObjectInserter(); this.mockSystemReader = reader; + long now = mockSystemReader.getCurrentTime(); + int tz = mockSystemReader.getTimezone(now); + defaultAuthor = new PersonIdent(AUTHOR, AUTHOR_EMAIL, now, tz); + defaultCommitter = new PersonIdent(COMMITTER, COMMITTER_EMAIL, now, tz); } /** @return the repository this helper class operates against. */ diff --git a/org.eclipse.jgit.lfs.server.test/.classpath b/org.eclipse.jgit.lfs.server.test/.classpath new file mode 100644 index 0000000000..2fdcc94f18 --- /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 0000000000..4dc009173e --- /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 0000000000..e3a4748e91 --- /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 0000000000..4824b80263 --- /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 0000000000..5a0ad22d2a --- /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 0000000000..dcc0d3a0fe --- /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 0000000000..c336cce6ed --- /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 0000000000..3dec4d97c7 --- /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 0000000000..ce7a0f0478 --- /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 0000000000..d585687d5c --- /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 0000000000..923c37fb8d --- /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 0000000000..9d6e72fcf5 --- /dev/null +++ b/org.eclipse.jgit.lfs.server.test/BUCK @@ -0,0 +1,35 @@ +TEST_BASE = ['tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java'] +TESTS = glob(['tst/**/*.java'], + excludes = TEST_BASE +) + +for t in TESTS: + n = t[len('tst/'):len(t)-len('.java')].replace('/', '.') + java_test( + name = n, + labels = ['lfs-server'], + srcs = [t] + TEST_BASE, + deps = [ + '//org.eclipse.jgit.lfs.test:helpers', + '//org.eclipse.jgit:jgit', + '//org.eclipse.jgit.junit:junit', + '//org.eclipse.jgit.junit.http:junit-http', + '//org.eclipse.jgit.lfs:jgit-lfs', + '//org.eclipse.jgit.lfs.server:jgit-lfs-server', + '//lib:hamcrest-core', + '//lib:hamcrest-library', + '//lib:httpcore', + '//lib:httpcomponents', + '//lib:junit', + '//lib/jetty:http', + '//lib/jetty:io', + '//lib/jetty:server', + '//lib/jetty:servlet', + '//lib/jetty:security', + '//lib/jetty:util', + '//lib:servlet-api', + '//lib:commons-logging', + ], + source_under_test = ['//org.eclipse.jgit.lfs.server:jgit-lfs-server'], + vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'], + ) diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..53d74cfdbe --- /dev/null +++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF @@ -0,0 +1,38 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %plugin_name +Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test +Bundle-Version: 4.3.2.qualifier +Bundle-Vendor: %provider_name +Bundle-Localization: plugin +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Import-Package: javax.servlet;version="[3.1.0,4.0.0)", + javax.servlet.http;version="[3.1.0,4.0.0)", + org.apache.http;version="[4.3.0,5.0.0)", + org.apache.http.client;version="[4.3.0,5.0.0)", + org.apache.http.client.methods;version="[4.3.0,5.0.0)", + org.apache.http.entity;version="[4.3.0,5.0.0)", + org.apache.http.impl.client;version="[4.3.0,5.0.0)", + org.eclipse.jetty.continuation;version="[9.0.0,10.0.0)", + org.eclipse.jetty.http;version="[9.0.0,10.0.0)", + org.eclipse.jetty.io;version="[9.0.0,10.0.0)", + org.eclipse.jetty.security;version="[9.0.0,10.0.0)", + org.eclipse.jetty.security.authentication;version="[9.0.0,10.0.0)", + org.eclipse.jetty.server;version="[9.0.0,10.0.0)", + org.eclipse.jetty.server.handler;version="[9.0.0,10.0.0)", + org.eclipse.jetty.server.nio;version="[9.0.0,10.0.0)", + org.eclipse.jetty.servlet;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)", + org.eclipse.jgit.junit.http;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.server.fs;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.test;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", + org.hamcrest.core;version="[1.1.0,2.0.0)", + org.junit;version="[4.0.0,5.0.0)", + org.junit.runner;version="[4.0.0,5.0.0)", + org.junit.runners;version="[4.0.0,5.0.0)" diff --git a/org.eclipse.jgit.lfs.server.test/build.properties b/org.eclipse.jgit.lfs.server.test/build.properties new file mode 100644 index 0000000000..9ffa0caf78 --- /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 0000000000..4a8e6425f3 --- /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 0000000000..05ee6f2eff --- /dev/null +++ b/org.eclipse.jgit.lfs.server.test/pom.xml @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2015 Matthias Sohn <matthias.sohn@sap.com> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>4.3.2-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> + <name>JGit - LFS Server Tests</name> + + <description> + Tests for the LFS server. + </description> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs.server</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs.test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit.http</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <version>4.3.6</version> + </dependency> + </dependencies> + + <build> + <testSourceDirectory>tst/</testSourceDirectory> + + <testResources> + <testResource> + <directory>tst-rsrc/</directory> + </testResource> + </testResources> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/DownloadTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/DownloadTest.java new file mode 100644 index 0000000000..6c4f3cbb99 --- /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 0000000000..8c266d4830 --- /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 0000000000..1fb91bd29d --- /dev/null +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/UploadTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.fs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.LongObjectId; +import org.eclipse.jgit.lfs.test.LongObjectIdTestUtils; +import org.junit.Test; + +public class UploadTest extends LfsServerTest { + + @Test + public void testUpload() throws Exception { + String TEXT = "test"; + AnyLongObjectId id = putContent(TEXT); + assertTrue("expect object " + id.name() + " to exist", + repository.getSize(id) >= 0); + assertEquals("expected object length " + TEXT.length(), TEXT.length(), + repository.getSize(id)); + } + + @Test + public void testCorruptUpload() throws Exception { + String TEXT = "test"; + AnyLongObjectId id = LongObjectIdTestUtils.hash("wrongHash"); + try { + putContent(id, TEXT); + fail("expected RuntimeException(\"Status 400\")"); + } catch (RuntimeException e) { + assertEquals("Status: 400. Bad Request", e.getMessage()); + } + assertFalse("expect object " + id.name() + " not to exist", + repository.getSize(id) >= 0); + } + + @SuppressWarnings("boxing") + @Test + public void testLargeFileUpload() throws Exception { + Path f = Paths.get(getTempDirectory().toString(), "largeRandomFile"); + createPseudoRandomContentFile(f, 5 * MiB); + long start = System.nanoTime(); + LongObjectId id = putContent(f); + System.out.println( + MessageFormat.format("uploaded 10 MiB random data in {0}ms", + (System.nanoTime() - start) / 1e6)); + assertTrue("expect object " + id.name() + " to exist", + repository.getSize(id) >= 0); + assertEquals("expected object length " + Files.size(f), Files.size(f), + repository.getSize(id)); + } + + @Test + public void testParallelUploads() throws Exception { + int count = 10; + List<Path> paths = new ArrayList<>(count); + + for (int i = 0; i < count; i++) { + Path f = Paths.get(getTempDirectory().toString(), + "largeRandomFile_" + i); + createPseudoRandomContentFile(f, 1 * MiB); + paths.add(f); + } + + final CyclicBarrier barrier = new CyclicBarrier(count); + + ExecutorService e = Executors.newFixedThreadPool(count); + try { + for (final Path p : paths) { + e.submit(new Callable<Void>() { + @Override + public Void call() throws Exception { + barrier.await(); + putContent(p); + return null; + } + }); + } + } finally { + e.shutdown(); + e.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); + } + } +} diff --git a/org.eclipse.jgit.lfs.server/.classpath b/org.eclipse.jgit.lfs.server/.classpath new file mode 100644 index 0000000000..04a2be7bdb --- /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 0000000000..81a0767ff6 --- /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 0000000000..934e0e06ff --- /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 0000000000..8379fea677 --- /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 0000000000..99f26c0203 --- /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 0000000000..5a0ad22d2a --- /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 0000000000..ff39d16ab7 --- /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 0000000000..c336cce6ed --- /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 0000000000..3dec4d97c7 --- /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 0000000000..ce7a0f0478 --- /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 0000000000..3294d4f799 --- /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 0000000000..923c37fb8d --- /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 0000000000..6b40b7c127 --- /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 0000000000..d15690056a --- /dev/null +++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF @@ -0,0 +1,35 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %plugin_name +Bundle-SymbolicName: org.eclipse.jgit.lfs.server +Bundle-Version: 4.3.2.qualifier +Bundle-Localization: plugin +Bundle-Vendor: %provider_name +Export-Package: org.eclipse.jgit.lfs.server;version="4.3.2"; + uses:="javax.servlet.http, + org.eclipse.jgit.lfs.lib", + org.eclipse.jgit.lfs.server.fs;version="4.3.2"; + uses:="javax.servlet, + javax.servlet.http, + org.eclipse.jgit.lfs.server, + org.eclipse.jgit.lfs.lib", + org.eclipse.jgit.lfs.server.internal;version="4.3.2";x-internal:=true, + org.eclipse.jgit.lfs.server.s3;version="4.3.2"; + uses:="org.eclipse.jgit.lfs.server, + org.eclipse.jgit.lfs.lib" +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Import-Package: com.google.gson;version="[2.2.4,3.0.0)", + javax.servlet;version="[3.1.0,4.0.0)", + javax.servlet.annotation;version="[3.1.0,4.0.0)", + javax.servlet.http;version="[3.1.0,4.0.0)", + org.apache.http;version="[4.3.0,5.0.0)", + org.apache.http.client;version="[4.3.0,5.0.0)", + org.eclipse.jgit.annotations;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http.apache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)" diff --git a/org.eclipse.jgit.lfs.server/about.html b/org.eclipse.jgit.lfs.server/about.html new file mode 100644 index 0000000000..01a2671875 --- /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 0000000000..8148271ef3 --- /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 0000000000..59f0139b84 --- /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 0000000000..0a81e33c88 --- /dev/null +++ b/org.eclipse.jgit.lfs.server/pom.xml @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>4.3.2-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.lfs.server</artifactId> + <name>JGit - Large File Storage Server</name> + + <description> + JGit Large File Storage Server implementation. + </description> + + <properties> + <translate-qualifier/> + </properties> + + <dependencies> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.http.apache</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> + </dependencies> + + <build> + <sourceDirectory>src/</sourceDirectory> + + <resources> + <resource> + <directory>.</directory> + <includes> + <include>plugin.properties</include> + <include>about.html</include> + </includes> + </resource> + <resource> + <directory>resources/</directory> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <inherited>true</inherited> + <executions> + <execution> + <id>attach-sources</id> + <phase>process-classes</phase> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <archive> + <manifestFile>${source-bundle-manifest}</manifestFile> + </archive> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${bundle-manifest}</manifestFile> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>clirr-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>clirr-maven-plugin</artifactId> + <version>${clirr-version}</version> + <configuration> + <comparisonVersion>${jgit-last-release-version}</comparisonVersion> + <minSeverity>info</minSeverity> + </configuration> + </plugin> + </plugins> + </reporting> +</project> diff --git a/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties b/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties new file mode 100644 index 0000000000..451064d0db --- /dev/null +++ b/org.eclipse.jgit.lfs.server/resources/org/eclipse/jgit/lfs/server/internal/LfsServerText.properties @@ -0,0 +1,12 @@ +corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted. +failedToCalcSignature=Failed to calculate a request signature: {0} +invalidPathInfo=Invalid pathInfo ''{0}'' does not match ''/'{'SHA-256'}''' +objectNotFound=Object ''{0}'' not found +undefinedS3AccessKey=S3 configuration: ''accessKey'' is undefined +undefinedS3Bucket=S3 configuration: ''bucket'' is undefined +undefinedS3Region=S3 configuration: ''region'' is undefined +undefinedS3SecretKey=S3 configuration: ''secretKey'' is undefined +undefinedS3StorageClass=S3 configuration: ''storageClass'' is undefined +unparsableEndpoint=Unable to parse service endpoint: {0} +unsupportedOperation=Operation ''{0}'' is not supported +unsupportedUtf8=UTF-8 encoding is not supported. diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java new file mode 100644 index 0000000000..3bdf8d08b2 --- /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 0000000000..30ba22e885 --- /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 0000000000..394137cf68 --- /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 0000000000..dc972e04ee --- /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 0000000000..bf5b61cc6e --- /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 0000000000..ecc7c1f36c --- /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 0000000000..2e71c0407e --- /dev/null +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.fs; + +import static org.eclipse.jgit.util.HttpSupport.HDR_AUTHORIZATION; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Collections; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.Constants; +import org.eclipse.jgit.lfs.server.LargeFileRepository; +import org.eclipse.jgit.lfs.server.Response; +import org.eclipse.jgit.lfs.server.Response.Action; + +/** + * Repository storing large objects in the file system + * + * @since 4.3 + */ +public class FileLfsRepository implements LargeFileRepository { + + private final String url; + private final Path dir; + + /** + * @param url + * external URL of this repository + * @param dir + * storage directory + * @throws IOException + */ + public FileLfsRepository(String url, Path dir) throws IOException { + this.url = url; + this.dir = dir; + Files.createDirectories(dir); + } + + @Override + public Response.Action getDownloadAction(AnyLongObjectId id) { + return getAction(id); + } + + @Override + public Action getUploadAction(AnyLongObjectId id, long size) { + return getAction(id); + } + + @Override + public @Nullable Action getVerifyAction(AnyLongObjectId id) { + return null; + } + + @Override + public long getSize(AnyLongObjectId id) throws IOException { + Path p = getPath(id); + if (Files.exists(p)) { + return Files.size(p); + } else { + return -1; + } + } + + /** + * Get the storage directory + * + * @return the path of the storage directory + */ + public Path getDir() { + return dir; + } + + /** + * Get the path where the given object is stored + * + * @param id + * id of a large object + * @return path the object's storage path + */ + protected Path getPath(AnyLongObjectId id) { + StringBuilder s = new StringBuilder( + Constants.LONG_OBJECT_ID_STRING_LENGTH + 6); + s.append(toHexCharArray(id.getFirstByte())).append('/'); + s.append(toHexCharArray(id.getSecondByte())).append('/'); + s.append(id.name()); + return dir.resolve(s.toString()); + } + + private Response.Action getAction(AnyLongObjectId id) { + Response.Action a = new Response.Action(); + a.href = url + id.getName(); + a.header = Collections.singletonMap(HDR_AUTHORIZATION, "not:required"); //$NON-NLS-1$ + return a; + } + + ReadableByteChannel getReadChannel(AnyLongObjectId id) + throws IOException { + return FileChannel.open(getPath(id), StandardOpenOption.READ); + } + + AtomicObjectOutputStream getOutputStream(AnyLongObjectId id) + throws IOException { + Path path = getPath(id); + Files.createDirectories(path.getParent()); + return new AtomicObjectOutputStream(path, id); + } + + private static char[] toHexCharArray(int b) { + final char[] dst = new char[2]; + formatHexChar(dst, 0, b); + return dst; + } + + private static final char[] hexchar = { '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private static void formatHexChar(final char[] dst, final int p, int b) { + int o = p + 1; + while (o >= p && b != 0) { + dst[o--] = hexchar[b & 0xf]; + b >>>= 4; + } + while (o >= p) + dst[o--] = '0'; + } +} diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsServlet.java new file mode 100644 index 0000000000..8864af87ab --- /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 0000000000..bfdea4fb1d --- /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 0000000000..e524ac643c --- /dev/null +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohnk@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.fs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.AsyncContext; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.HttpStatus; +import org.eclipse.jgit.lfs.errors.CorruptLongObjectException; +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.lib.Constants; + +/** + * Handle asynchronous object upload + */ +class ObjectUploadListener implements ReadListener { + + private static Logger LOG = Logger + .getLogger(ObjectUploadListener.class.getName()); + + private final AsyncContext context; + + private final HttpServletResponse response; + + private final ServletInputStream in; + + private final ReadableByteChannel inChannel; + + private final AtomicObjectOutputStream out; + + private WritableByteChannel channel; + + private final ByteBuffer buffer = ByteBuffer.allocateDirect(8192); + + /** + * @param repository + * the repository storing large objects + * @param context + * @param request + * @param response + * @param id + * @throws FileNotFoundException + * @throws IOException + */ + public ObjectUploadListener(FileLfsRepository repository, + AsyncContext context, HttpServletRequest request, + HttpServletResponse response, AnyLongObjectId id) + throws FileNotFoundException, IOException { + this.context = context; + this.response = response; + this.in = request.getInputStream(); + this.inChannel = Channels.newChannel(in); + this.out = repository.getOutputStream(id); + this.channel = Channels.newChannel(out); + response.setContentType(Constants.CONTENT_TYPE_GIT_LFS_JSON); + } + + /** + * Writes all the received data to the output channel + * + * @throws IOException + */ + @Override + public void onDataAvailable() throws IOException { + while (in.isReady()) { + if (inChannel.read(buffer) > 0) { + buffer.flip(); + channel.write(buffer); + buffer.compact(); + } else { + buffer.flip(); + while (buffer.hasRemaining()) { + channel.write(buffer); + } + close(); + return; + } + } + } + + /** + * @throws IOException + */ + @Override + public void onAllDataRead() throws IOException { + close(); + } + + protected void close() throws IOException { + try { + inChannel.close(); + channel.close(); + // TODO check if status 200 is ok for PUT request, HTTP foresees 204 + // for successful PUT without response body + response.setStatus(HttpServletResponse.SC_OK); + } finally { + context.complete(); + } + } + + /** + * @param e + * the exception that caused the problem + */ + @Override + public void onError(Throwable e) { + try { + out.abort(); + inChannel.close(); + channel.close(); + int status; + if (e instanceof CorruptLongObjectException) { + status = HttpStatus.SC_BAD_REQUEST; + LOG.log(Level.WARNING, e.getMessage(), e); + } else { + status = HttpStatus.SC_INTERNAL_SERVER_ERROR; + LOG.log(Level.SEVERE, e.getMessage(), e); + } + FileLfsServlet.sendError(response, status, e.getMessage()); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getMessage(), ex); + } + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.java new file mode 100644 index 0000000000..c5ad53a541 --- /dev/null +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/internal/LfsServerText.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.internal; + +import org.eclipse.jgit.nls.NLS; +import org.eclipse.jgit.nls.TranslationBundle; + +/** + * Translation bundle for JGit LFS server + */ +public class LfsServerText extends TranslationBundle { + + /** + * @return an instance of this translation bundle + */ + public static LfsServerText get() { + return NLS.getBundleFor(LfsServerText.class); + } + + // @formatter:off + /***/ public String corruptLongObject; + /***/ public String failedToCalcSignature; + /***/ public String invalidPathInfo; + /***/ public String objectNotFound; + /***/ public String undefinedS3AccessKey; + /***/ public String undefinedS3Bucket; + /***/ public String undefinedS3Region; + /***/ public String undefinedS3SecretKey; + /***/ public String undefinedS3StorageClass; + /***/ public String unparsableEndpoint; + /***/ public String unsupportedOperation; + /***/ public String unsupportedUtf8; +} diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Config.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Config.java new file mode 100644 index 0000000000..4876fb8cb5 --- /dev/null +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Config.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.s3; + +/** + * Configuration for an Amazon AWS S3 bucket + * + * @since 4.3 + */ +public class S3Config { + private final String region; + private final String bucket; + private final String storageClass; + private final String accessKey; + private final String secretKey; + private final int expirationSeconds; + private final boolean disableSslVerify; + + /** + * @param region + * AWS region + * @param bucket + * S3 storage bucket + * @param storageClass + * S3 storage class + * @param accessKey + * access key for authenticating to AWS + * @param secretKey + * secret key for authenticating to AWS + * @param expirationSeconds + * period in seconds after which requests signed for this bucket + * will expire + * @param disableSslVerify + * if {@code true} disable Amazon server certificate and hostname + * verification + */ + public S3Config(String region, String bucket, String storageClass, + String accessKey, String secretKey, int expirationSeconds, + boolean disableSslVerify) { + this.region = region; + this.bucket = bucket; + this.storageClass = storageClass; + this.accessKey = accessKey; + this.secretKey = secretKey; + this.expirationSeconds = expirationSeconds; + this.disableSslVerify = disableSslVerify; + } + + /** + * @return Get name of AWS region this bucket resides in + */ + public String getRegion() { + return region; + } + + /** + * @return Get S3 storage bucket name + */ + public String getBucket() { + return bucket; + } + + /** + * @return S3 storage class to use for objects stored in this bucket + */ + public String getStorageClass() { + return storageClass; + } + + /** + * @return access key for authenticating to AWS + */ + public String getAccessKey() { + return accessKey; + } + + /** + * @return secret key for authenticating to AWS + */ + public String getSecretKey() { + return secretKey; + } + + /** + * @return period in seconds after which requests signed for this bucket + * will expire + */ + public int getExpirationSeconds() { + return expirationSeconds; + } + + /** + * @return {@code true} if Amazon server certificate and hostname + * verification is disabled + */ + boolean isDisableSslVerify() { + return disableSslVerify; + } + +} diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java new file mode 100644 index 0000000000..c229758d43 --- /dev/null +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.s3; + +import static javax.servlet.http.HttpServletResponse.SC_OK; +import static org.eclipse.jgit.lfs.server.s3.SignerV4.UNSIGNED_PAYLOAD; +import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_CONTENT_SHA256; +import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_EXPIRES; +import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_STORAGE_CLASS; +import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH; +import static org.eclipse.jgit.util.HttpSupport.METHOD_GET; +import static org.eclipse.jgit.util.HttpSupport.METHOD_HEAD; +import static org.eclipse.jgit.util.HttpSupport.METHOD_PUT; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URL; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jgit.lfs.lib.AnyLongObjectId; +import org.eclipse.jgit.lfs.server.LargeFileRepository; +import org.eclipse.jgit.lfs.server.Response; +import org.eclipse.jgit.lfs.server.Response.Action; +import org.eclipse.jgit.lfs.server.internal.LfsServerText; +import org.eclipse.jgit.transport.http.HttpConnection; +import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; +import org.eclipse.jgit.util.HttpSupport; + +/** + * Repository storing LFS objects in Amazon S3 + * + * @since 4.3 + */ +public class S3Repository implements LargeFileRepository { + + private S3Config s3Config; + + /** + * Construct a LFS repository storing large objects in Amazon S3 + * + * @param config + * AWS S3 storage bucket configuration + */ + public S3Repository(S3Config config) { + validateConfig(config); + this.s3Config = config; + } + + @Override + public Response.Action getDownloadAction(AnyLongObjectId oid) { + URL endpointUrl = getObjectUrl(oid); + Map<String, String> queryParams = new HashMap<String, String>(); + queryParams.put(X_AMZ_EXPIRES, + Integer.toString(s3Config.getExpirationSeconds())); + Map<String, String> headers = new HashMap<String, String>(); + String authorizationQueryParameters = SignerV4.createAuthorizationQuery( + s3Config, endpointUrl, METHOD_GET, headers, queryParams, + UNSIGNED_PAYLOAD); + + Response.Action a = new Response.Action(); + a.href = endpointUrl.toString() + "?" + authorizationQueryParameters; //$NON-NLS-1$ + return a; + } + + @Override + public Response.Action getUploadAction(AnyLongObjectId oid, long size) { + cacheObjectMetaData(oid, size); + URL objectUrl = getObjectUrl(oid); + Map<String, String> headers = new HashMap<String, String>(); + headers.put(X_AMZ_CONTENT_SHA256, oid.getName()); + headers.put(HDR_CONTENT_LENGTH, Long.toString(size)); + headers.put(X_AMZ_STORAGE_CLASS, s3Config.getStorageClass()); + headers.put(HttpSupport.HDR_CONTENT_TYPE, "application/octet-stream"); //$NON-NLS-1$ + headers = SignerV4.createHeaderAuthorization(s3Config, objectUrl, + METHOD_PUT, headers, oid.getName()); + + Response.Action a = new Response.Action(); + a.href = objectUrl.toString(); + a.header = new HashMap<>(); + a.header.putAll(headers); + return a; + } + + @Override + public Action getVerifyAction(AnyLongObjectId id) { + return null; // TODO(ms) implement this + } + + @Override + public long getSize(AnyLongObjectId oid) throws IOException { + URL endpointUrl = getObjectUrl(oid); + Map<String, String> queryParams = new HashMap<String, String>(); + queryParams.put(X_AMZ_EXPIRES, + Integer.toString(s3Config.getExpirationSeconds())); + Map<String, String> headers = new HashMap<String, String>(); + + String authorizationQueryParameters = SignerV4.createAuthorizationQuery( + s3Config, endpointUrl, METHOD_HEAD, headers, queryParams, + UNSIGNED_PAYLOAD); + String href = endpointUrl.toString() + "?" //$NON-NLS-1$ + + authorizationQueryParameters; + + Proxy proxy = HttpSupport.proxyFor(ProxySelector.getDefault(), + endpointUrl); + HttpClientConnectionFactory f = new HttpClientConnectionFactory(); + HttpConnection conn = f.create(new URL(href), proxy); + if (s3Config.isDisableSslVerify()) { + HttpSupport.disableSslVerify(conn); + } + conn.setRequestMethod(METHOD_HEAD); + conn.connect(); + int status = conn.getResponseCode(); + if (status == SC_OK) { + String contentLengthHeader = conn + .getHeaderField(HDR_CONTENT_LENGTH); + if (contentLengthHeader != null) { + return Integer.parseInt(contentLengthHeader); + } + } + return -1; + } + + /** + * Cache metadata (size) for an object to avoid extra roundtrip to S3 in + * order to retrieve this metadata for a given object. Subclasses can + * implement a local cache and override {{@link #getSize(AnyLongObjectId)} + * to retrieve the object size from the local cache to eliminate the need + * for another roundtrip to S3 + * + * @param oid + * the object id identifying the object to be cached + * @param size + * the object's size (in bytes) + */ + protected void cacheObjectMetaData(AnyLongObjectId oid, long size) { + // no caching + } + + private void validateConfig(S3Config config) { + assertNotEmpty(LfsServerText.get().undefinedS3AccessKey, + config.getAccessKey()); + assertNotEmpty(LfsServerText.get().undefinedS3Bucket, + config.getBucket()); + assertNotEmpty(LfsServerText.get().undefinedS3Region, + config.getRegion()); + assertNotEmpty(LfsServerText.get().undefinedS3SecretKey, + config.getSecretKey()); + assertNotEmpty(LfsServerText.get().undefinedS3StorageClass, + config.getStorageClass()); + } + + private void assertNotEmpty(String message, String value) { + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException(message); + } + } + + private URL getObjectUrl(AnyLongObjectId oid) { + try { + return new URL(String.format("https://s3-%s.amazonaws.com/%s/%s", //$NON-NLS-1$ + s3Config.getRegion(), s3Config.getBucket(), + getPath(oid))); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(MessageFormat.format( + LfsServerText.get().unparsableEndpoint, e.getMessage())); + } + } + + private String getPath(AnyLongObjectId oid) { + return oid.getName(); + } +} diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java new file mode 100644 index 0000000000..08bb4b96fd --- /dev/null +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.lfs.server.s3; + +import static org.eclipse.jgit.util.HttpSupport.HDR_AUTHORIZATION; + +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SimpleTimeZone; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.eclipse.jgit.lfs.lib.Constants; +import org.eclipse.jgit.lfs.server.internal.LfsServerText; + +/** + * Signing support for Amazon AWS signing V4 + * <p> + * See + * http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html + */ +class SignerV4 { + static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD"; //$NON-NLS-1$ + + private static final String ALGORITHM = "HMAC-SHA256"; //$NON-NLS-1$ + private static final String DATE_STRING_FORMAT = "yyyyMMdd"; //$NON-NLS-1$ + private static final String HEX = "0123456789abcdef"; //$NON-NLS-1$ + private static final String HMACSHA256 = "HmacSHA256"; //$NON-NLS-1$ + private static final String ISO8601_BASIC_FORMAT = "yyyyMMdd'T'HHmmss'Z'"; //$NON-NLS-1$ + private static final String S3 = "s3"; //$NON-NLS-1$ + private static final String SCHEME = "AWS4"; //$NON-NLS-1$ + private static final String TERMINATOR = "aws4_request"; //$NON-NLS-1$ + private static final String UTC = "UTC"; //$NON-NLS-1$ + private static final String X_AMZ_ALGORITHM = "X-Amz-Algorithm"; //$NON-NLS-1$ + private static final String X_AMZ_CREDENTIAL = "X-Amz-Credential"; //$NON-NLS-1$ + private static final String X_AMZ_DATE = "X-Amz-Date"; //$NON-NLS-1$ + private static final String X_AMZ_SIGNATURE = "X-Amz-Signature"; //$NON-NLS-1$ + private static final String X_AMZ_SIGNED_HEADERS = "X-Amz-SignedHeaders"; //$NON-NLS-1$ + + static final String X_AMZ_CONTENT_SHA256 = "x-amz-content-sha256"; //$NON-NLS-1$ + static final String X_AMZ_EXPIRES = "X-Amz-Expires"; //$NON-NLS-1$ + static final String X_AMZ_STORAGE_CLASS = "x-amz-storage-class"; //$NON-NLS-1$ + + /** + * Create an AWSV4 authorization for a request, suitable for embedding in + * query parameters. + * + * @param bucketConfig + * configuration of S3 storage bucket this request should be + * signed for + * @param url + * HTTP request URL + * @param httpMethod + * HTTP method + * @param headers + * The HTTP request headers; 'Host' and 'X-Amz-Date' will be + * added to this set. + * @param queryParameters + * Any query parameters that will be added to the endpoint. The + * parameters should be specified in canonical format. + * @param bodyHash + * Pre-computed SHA256 hash of the request body content; this + * value should also be set as the header 'X-Amz-Content-SHA256' + * for non-streaming uploads. + * @return The computed authorization string for the request. This value + * needs to be set as the header 'Authorization' on the subsequent + * HTTP request. + */ + static String createAuthorizationQuery(S3Config bucketConfig, URL url, + String httpMethod, Map<String, String> headers, + Map<String, String> queryParameters, String bodyHash) { + addHostHeader(url, headers); + + queryParameters.put(X_AMZ_ALGORITHM, SCHEME + "-" + ALGORITHM); //$NON-NLS-1$ + + Date now = new Date(); + String dateStamp = dateStamp(now); + String scope = scope(bucketConfig.getRegion(), dateStamp); + queryParameters.put(X_AMZ_CREDENTIAL, + bucketConfig.getAccessKey() + "/" + scope); //$NON-NLS-1$ + + String dateTimeStampISO8601 = dateTimeStampISO8601(now); + queryParameters.put(X_AMZ_DATE, dateTimeStampISO8601); + + String canonicalizedHeaderNames = canonicalizeHeaderNames(headers); + queryParameters.put(X_AMZ_SIGNED_HEADERS, canonicalizedHeaderNames); + + String canonicalizedQueryParameters = canonicalizeQueryString( + queryParameters); + String canonicalizedHeaders = canonicalizeHeaderString(headers); + String canonicalRequest = canonicalRequest(url, httpMethod, + canonicalizedQueryParameters, canonicalizedHeaderNames, + canonicalizedHeaders, bodyHash); + byte[] signature = createSignature(bucketConfig, dateTimeStampISO8601, + dateStamp, scope, canonicalRequest); + queryParameters.put(X_AMZ_SIGNATURE, toHex(signature)); + + return formatAuthorizationQuery(queryParameters); + } + + private static String formatAuthorizationQuery( + Map<String, String> queryParameters) { + StringBuilder s = new StringBuilder(); + for (String key : queryParameters.keySet()) { + appendQuery(s, key, queryParameters.get(key)); + } + return s.toString(); + } + + private static void appendQuery(StringBuilder s, String key, + String value) { + if (s.length() != 0) { + s.append("&"); //$NON-NLS-1$ + } + s.append(key).append("=").append(value); //$NON-NLS-1$ + } + + /** + * Sign headers for given bucket, url and HTTP method and add signature in + * Authorization header. + * + * @param bucketConfig + * configuration of S3 storage bucket this request should be + * signed for + * @param url + * HTTP request URL + * @param httpMethod + * HTTP method + * @param headers + * HTTP headers to sign + * @param bodyHash + * Pre-computed SHA256 hash of the request body content; this + * value should also be set as the header 'X-Amz-Content-SHA256' + * for non-streaming uploads. + * @return HTTP headers signd by an Authorization header added to the + * headers + */ + static Map<String, String> createHeaderAuthorization( + S3Config bucketConfig, URL url, String httpMethod, + Map<String, String> headers, String bodyHash) { + addHostHeader(url, headers); + + Date now = new Date(); + String dateTimeStamp = dateTimeStampISO8601(now); + headers.put(X_AMZ_DATE, dateTimeStamp); + + String canonicalizedHeaderNames = canonicalizeHeaderNames(headers); + String canonicalizedHeaders = canonicalizeHeaderString(headers); + String canonicalRequest = canonicalRequest(url, httpMethod, "", //$NON-NLS-1$ + canonicalizedHeaderNames, canonicalizedHeaders, bodyHash); + String dateStamp = dateStamp(now); + String scope = scope(bucketConfig.getRegion(), dateStamp); + + byte[] signature = createSignature(bucketConfig, dateTimeStamp, + dateStamp, scope, canonicalRequest); + + headers.put(HDR_AUTHORIZATION, formatAuthorizationHeader(bucketConfig, + canonicalizedHeaderNames, scope, signature)); // $NON-NLS-1$ + + return headers; + } + + private static String formatAuthorizationHeader( + S3Config bucketConfig, String canonicalizedHeaderNames, + String scope, byte[] signature) { + StringBuilder s = new StringBuilder(); + s.append(SCHEME).append("-").append(ALGORITHM).append(" "); //$NON-NLS-1$ //$NON-NLS-2$ + s.append("Credential=").append(bucketConfig.getAccessKey()).append("/") //$NON-NLS-1$//$NON-NLS-2$ + .append(scope).append(","); //$NON-NLS-1$ + s.append("SignedHeaders=").append(canonicalizedHeaderNames).append(","); //$NON-NLS-1$ //$NON-NLS-2$ + s.append("Signature=").append(toHex(signature)); //$NON-NLS-1$ + return s.toString(); + } + + private static void addHostHeader(URL url, + Map<String, String> headers) { + String hostHeader = url.getHost(); + int port = url.getPort(); + if (port > -1) { + hostHeader.concat(":" + Integer.toString(port)); //$NON-NLS-1$ + } + headers.put("Host", hostHeader); //$NON-NLS-1$ + } + + private static String canonicalizeHeaderNames( + Map<String, String> headers) { + List<String> sortedHeaders = new ArrayList<String>(); + sortedHeaders.addAll(headers.keySet()); + Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER); + + StringBuilder buffer = new StringBuilder(); + for (String header : sortedHeaders) { + if (buffer.length() > 0) + buffer.append(";"); //$NON-NLS-1$ + buffer.append(header.toLowerCase()); + } + + return buffer.toString(); + } + + private static String canonicalizeHeaderString( + Map<String, String> headers) { + if (headers == null || headers.isEmpty()) { + return ""; //$NON-NLS-1$ + } + + List<String> sortedHeaders = new ArrayList<String>(); + sortedHeaders.addAll(headers.keySet()); + Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER); + + StringBuilder buffer = new StringBuilder(); + for (String key : sortedHeaders) { + buffer.append(key.toLowerCase().replaceAll("\\s+", " ") + ":" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + headers.get(key).replaceAll("\\s+", " ")); //$NON-NLS-1$//$NON-NLS-2$ + buffer.append("\n"); //$NON-NLS-1$ + } + + return buffer.toString(); + } + + private static String dateStamp(Date now) { + // TODO(ms) cache and reuse DateFormat instances + SimpleDateFormat dateStampFormat = new SimpleDateFormat( + DATE_STRING_FORMAT); + dateStampFormat.setTimeZone(new SimpleTimeZone(0, UTC)); + String dateStamp = dateStampFormat.format(now); + return dateStamp; + } + + private static String dateTimeStampISO8601(Date now) { + // TODO(ms) cache and reuse DateFormat instances + SimpleDateFormat dateTimeFormat = new SimpleDateFormat( + ISO8601_BASIC_FORMAT); + dateTimeFormat.setTimeZone(new SimpleTimeZone(0, UTC)); + String dateTimeStamp = dateTimeFormat.format(now); + return dateTimeStamp; + } + + private static String scope(String region, String dateStamp) { + String scope = String.format("%s/%s/%s/%s", dateStamp, region, S3, //$NON-NLS-1$ + TERMINATOR); + return scope; + } + + private static String canonicalizeQueryString( + Map<String, String> parameters) { + if (parameters == null || parameters.isEmpty()) { + return ""; //$NON-NLS-1$ + } + + SortedMap<String, String> sorted = new TreeMap<String, String>(); + + Iterator<Map.Entry<String, String>> pairs = parameters.entrySet() + .iterator(); + while (pairs.hasNext()) { + Map.Entry<String, String> pair = pairs.next(); + String key = pair.getKey(); + String value = pair.getValue(); + sorted.put(urlEncode(key, false), urlEncode(value, false)); + } + + StringBuilder builder = new StringBuilder(); + pairs = sorted.entrySet().iterator(); + while (pairs.hasNext()) { + Map.Entry<String, String> pair = pairs.next(); + builder.append(pair.getKey()); + builder.append("="); //$NON-NLS-1$ + builder.append(pair.getValue()); + if (pairs.hasNext()) { + builder.append("&"); //$NON-NLS-1$ + } + } + + return builder.toString(); + } + + private static String canonicalRequest(URL endpoint, String httpMethod, + String queryParameters, String canonicalizedHeaderNames, + String canonicalizedHeaders, String bodyHash) { + return String.format("%s\n%s\n%s\n%s\n%s\n%s", //$NON-NLS-1$ + httpMethod, canonicalizeResourcePath(endpoint), + queryParameters, canonicalizedHeaders, canonicalizedHeaderNames, + bodyHash); + } + + private static String canonicalizeResourcePath(URL endpoint) { + if (endpoint == null) { + return "/"; //$NON-NLS-1$ + } + String path = endpoint.getPath(); + if (path == null || path.isEmpty()) { + return "/"; //$NON-NLS-1$ + } + + String encodedPath = urlEncode(path, true); + if (encodedPath.startsWith("/")) { //$NON-NLS-1$ + return encodedPath; + } else { + return "/" + encodedPath; //$NON-NLS-1$ + } + } + + private static byte[] hash(String s) { + MessageDigest md = Constants.newMessageDigest(); + md.update(s.getBytes(StandardCharsets.UTF_8)); + return md.digest(); + } + + private static byte[] sign(String stringData, byte[] key) { + try { + byte[] data = stringData.getBytes("UTF-8"); //$NON-NLS-1$ + Mac mac = Mac.getInstance(HMACSHA256); + mac.init(new SecretKeySpec(key, HMACSHA256)); + return mac.doFinal(data); + } catch (Exception e) { + throw new RuntimeException(MessageFormat.format( + LfsServerText.get().failedToCalcSignature, e.getMessage()), + e); + } + } + + private static String stringToSign(String scheme, String algorithm, + String dateTime, String scope, String canonicalRequest) { + return String.format("%s-%s\n%s\n%s\n%s", //$NON-NLS-1$ + scheme, algorithm, dateTime, scope, + toHex(hash(canonicalRequest))); + } + + private static String toHex(byte[] bytes) { + StringBuilder builder = new StringBuilder(2 * bytes.length); + for (byte b : bytes) { + builder.append(HEX.charAt((b & 0xF0) >> 4)); + builder.append(HEX.charAt(b & 0xF)); + } + return builder.toString(); + } + + private static String urlEncode(String url, boolean keepPathSlash) { + String encoded; + try { + encoded = URLEncoder.encode(url, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(LfsServerText.get().unsupportedUtf8, e); + } + if (keepPathSlash) { + encoded = encoded.replace("%2F", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + } + return encoded; + } + + private static byte[] createSignature(S3Config bucketConfig, + String dateTimeStamp, String dateStamp, + String scope, String canonicalRequest) { + String stringToSign = stringToSign(SCHEME, ALGORITHM, dateTimeStamp, + scope, canonicalRequest); + + byte[] signature = (SCHEME + bucketConfig.getSecretKey()).getBytes(); + signature = sign(dateStamp, signature); + signature = sign(bucketConfig.getRegion(), signature); + signature = sign(S3, signature); + signature = sign(TERMINATOR, signature); + signature = sign(stringToSign, signature); + return signature; + } +} diff --git a/org.eclipse.jgit.lfs.test/.classpath b/org.eclipse.jgit.lfs.test/.classpath new file mode 100644 index 0000000000..efc781df01 --- /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 0000000000..4dc009173e --- /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 0000000000..c5dddb0e94 --- /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 0000000000..4824b80263 --- /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 0000000000..5a0ad22d2a --- /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 0000000000..dcc0d3a0fe --- /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 0000000000..c336cce6ed --- /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 0000000000..3dec4d97c7 --- /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 0000000000..ce7a0f0478 --- /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 0000000000..d585687d5c --- /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 0000000000..923c37fb8d --- /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 0000000000..d0fb96f9cd --- /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 0000000000..173b0eb998 --- /dev/null +++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %plugin_name +Bundle-SymbolicName: org.eclipse.jgit.lfs.test +Bundle-Version: 4.3.2.qualifier +Bundle-Vendor: %provider_name +Bundle-Localization: plugin +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Import-Package: org.eclipse.jgit.junit;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", + org.hamcrest.core;version="[1.1.0,2.0.0)", + org.junit;version="[4.0.0,5.0.0)", + org.junit.runner;version="[4.0.0,5.0.0)", + org.junit.runners;version="[4.0.0,5.0.0)" +Export-Package: org.eclipse.jgit.lfs.test;version="4.3.2";x-friends:="org.eclipse.jgit.lfs.server.test" + diff --git a/org.eclipse.jgit.lfs.test/build.properties b/org.eclipse.jgit.lfs.test/build.properties new file mode 100644 index 0000000000..47ae0916a2 --- /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 0000000000..f9007802f0 --- /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 0000000000..8e5d28b5d8 --- /dev/null +++ b/org.eclipse.jgit.lfs.test/pom.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2015 Matthias Sohn <matthias.sohn@sap.com> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>4.3.2-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.lfs.test</artifactId> + <name>JGit - Large File Storage Tests</name> + + <description> + Tests for the Large File Extension (LFS). + </description> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <testSourceDirectory>tst/</testSourceDirectory> + <sourceDirectory>src/</sourceDirectory> + + <testResources> + <testResource> + <directory>tst-rsrc/</directory> + </testResource> + </testResources> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/org.eclipse.jgit.lfs.test/src/org/eclipse/jgit/lfs/test/LongObjectIdTestUtils.java b/org.eclipse.jgit.lfs.test/src/org/eclipse/jgit/lfs/test/LongObjectIdTestUtils.java new file mode 100644 index 0000000000..6c6400f293 --- /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 0000000000..8938a7875d --- /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 0000000000..435a2a3c39 --- /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 0000000000..9bdc3ab0c0 --- /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 0000000000..04a2be7bdb --- /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 0000000000..81a0767ff6 --- /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 0000000000..934e0e06ff --- /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 0000000000..d9beec1979 --- /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 0000000000..99f26c0203 --- /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 0000000000..5a0ad22d2a --- /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 0000000000..ff39d16ab7 --- /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 0000000000..c336cce6ed --- /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 0000000000..3dec4d97c7 --- /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 0000000000..ce7a0f0478 --- /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 0000000000..3294d4f799 --- /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 0000000000..923c37fb8d --- /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 0000000000..ddb3a10803 --- /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 0000000000..30baf05fac --- /dev/null +++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %plugin_name +Bundle-SymbolicName: org.eclipse.jgit.lfs +Bundle-Version: 4.3.2.qualifier +Bundle-Localization: plugin +Bundle-Vendor: %provider_name +Export-Package: org.eclipse.jgit.lfs.errors;version="4.3.2", + org.eclipse.jgit.lfs.internal;version="4.3.2";x-friends:="org.eclipse.jgit.lfs.test", + org.eclipse.jgit.lfs.lib;version="4.3.2" +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Import-Package: org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)" diff --git a/org.eclipse.jgit.lfs/about.html b/org.eclipse.jgit.lfs/about.html new file mode 100644 index 0000000000..721e62d66e --- /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 0000000000..8148271ef3 --- /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 0000000000..7c22ddb571 --- /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 0000000000..b0f543d9b1 --- /dev/null +++ b/org.eclipse.jgit.lfs/pom.xml @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>4.3.2-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.lfs</artifactId> + <name>JGit - Large File Storage</name> + + <description> + JGit Large File Storage (LFS) implementation. + </description> + + <properties> + <translate-qualifier/> + </properties> + + <dependencies> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + <build> + <sourceDirectory>src/</sourceDirectory> + + <resources> + <resource> + <directory>.</directory> + <includes> + <include>plugin.properties</include> + <include>about.html</include> + </includes> + </resource> + <resource> + <directory>resources/</directory> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <inherited>true</inherited> + <executions> + <execution> + <id>attach-sources</id> + <phase>process-classes</phase> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <archive> + <manifestFile>${source-bundle-manifest}</manifestFile> + </archive> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${bundle-manifest}</manifestFile> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>clirr-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>clirr-maven-plugin</artifactId> + <version>${clirr-version}</version> + <configuration> + <comparisonVersion>${jgit-last-release-version}</comparisonVersion> + <minSeverity>info</minSeverity> + </configuration> + </plugin> + </plugins> + </reporting> +</project> diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties new file mode 100644 index 0000000000..700e2d50d0 --- /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 0000000000..fea148b884 --- /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 0000000000..1f6e2d174f --- /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 0000000000..eaffcc9047 --- /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 0000000000..e9eb0e3850 --- /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 0000000000..1f0df882d2 --- /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 0000000000..3cadbf73a1 --- /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 0000000000..c4a4e43b12 --- /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 0000000000..130e94ed0b --- /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 c22b3bc8f8..3d13c7139e 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit" label="%featureName" - version="4.2.1.qualifier" + version="4.3.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml index cbfa6b69ad..81a8c3b455 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml index 73b6eb6f40..f2597a46cc 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.http.apache" label="%featureName" - version="4.2.1.qualifier" + version="4.3.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml index 82dfb1f0de..ca0fdca5bf 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml index 186b2631c3..829ef0a506 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.junit" label="%featureName" - version="4.2.1.qualifier" + version="4.3.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml index 4cee17ed55..6b900d433c 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.gitignore new file mode 100644 index 0000000000..2f7896d1d1 --- /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 0000000000..c2061b9a53 --- /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 0000000000..14bdc2c705 --- /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 0000000000..898252b4d6 --- /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 0000000000..823c0f56ae --- /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 0000000000..0cba949fb7 --- /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 0000000000..b4a8dde9e5 --- /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 0000000000..1826b47af8 --- /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 0000000000..c5bffa8f26 --- /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 0000000000..829c05623a --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feature + id="org.eclipse.jgit.lfs" + label="%featureName" + version="4.3.2.qualifier" + provider-name="%providerName"> + + <description url="http://www.eclipse.org/jgit/"> + %description + </description> + + <copyright> + %copyright + </copyright> + + <license url="%licenseURL"> + %license + </license> + + <url> + <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/> + <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/> + </url> + + <requires> + <import plugin="org.eclipse.jgit"/> + </requires> + + <plugin + id="org.eclipse.jgit.lfs" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="org.eclipse.jgit.lfs.server" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.google.gson" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + +</feature> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/license.html new file mode 100644 index 0000000000..95ad95e77d --- /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 0000000000..bf96616a5f --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>jgit.tycho.parent</artifactId> + <version>4.3.2-SNAPSHOT</version> + </parent> + + <groupId>org.eclipse.jgit.feature</groupId> + <artifactId>org.eclipse.jgit.lfs</artifactId> + <packaging>eclipse-feature</packaging> + + <name>JGit - Optional LFS support</name> + <dependencies> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs.server</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + +</project> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml index 67ff3cfade..73a9a13823 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.pgm" label="%featureName" - version="4.2.1.qualifier" + version="4.3.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -26,8 +26,13 @@ id="org.eclipse.jgit" version="0.0.0"/> + <includes + id="org.eclipse.jgit.lfs" + version="0.0.0"/> + <requires> - <import feature="org.eclipse.jgit" version="4.2.1" match="equivalent"/> + <import feature="org.eclipse.jgit" version="4.3.0" match="equivalent"/> + <import feature="org.eclipse.jgit.lfs" version="4.3.0" match="equivalent"/> </requires> <plugin diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml index 8cbbdc5b4e..262872166b 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml index d53df31452..4cb5cb7b3b 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.pgm.source" label="%featureName" - version="4.2.1.qualifier" + version="4.3.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml index 8dd726d101..c5015c6b6c 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml index 2186ad9825..c2bfc92c3c 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml @@ -21,6 +21,12 @@ <feature url="features/org.eclipse.jgit.http.apache_0.0.0.qualifier.jar" id="org.eclipse.jgit.http.apache" version="0.0.0" patch="true"> <category name="JGit"/> </feature> + <feature url="features/org.eclipse.jgit.lfs_0.0.0.qualifier.jar" id="org.eclipse.jgit.lfs" version="0.0.0"> + <category name="JGit"/> + </feature> + <feature url="features/org.eclipse.jgit.lfs_0.0.0.qualifier.jar" id="org.eclipse.jgit.lfs" version="0.0.0"> + <category name="JGit"/> + </feature> <category-def name="JGit" label="JGit"> <description> JGit diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml index bf8c7cb097..1410d68e88 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.repository</artifactId> @@ -70,6 +70,16 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs.server</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.pgm</artifactId> <version>${project.version}</version> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml index 38db4f6ad9..4d49aae0a0 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.source" label="%featureName" - version="4.2.1.qualifier" + version="4.3.2.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml index ef6de2b33d..204026b63d 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF index 09e7c944f9..4e1f66f7ae 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 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: JGit Target Platform Bundle Bundle-SymbolicName: org.eclipse.jgit.target -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target index e8670e7cfb..19edf51437 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 0b85d75b02..3451b42653 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 20674827b1..08116ebf18 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.5" sequenceNumber="1440022750"> +<target name="jgit-4.5" sequenceNumber="1456522731"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/> @@ -31,8 +31,8 @@ <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/> <unit id="org.apache.httpcomponents.httpcore" version="4.3.3.v201411290715"/> <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/> - <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715"/> - <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715B"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715B"/> <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> <unit id="org.kohsuke.args4j" version="2.0.21.v201301150030"/> @@ -45,6 +45,7 @@ <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> <unit id="org.mockito" version="1.8.4.v201303031500"/> <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="com.google.gson" version="2.2.4.v201311231704"/> <unit id="com.jcraft.jsch" version="0.1.53.v201508180515"/> <unit id="com.jcraft.jsch.source" version="0.1.53.v201508180515"/> <unit id="org.junit" version="4.11.0.v201303080030"/> @@ -57,7 +58,7 @@ <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20160221192158/repository/"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd index cdf24b551c..5cd1037d16 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd @@ -1,7 +1,7 @@ target "jgit-4.5" with source configurePhase include "projects/jetty-9.2.13.tpd" -include "orbit/R20150821153341-Mars.tpd" +include "orbit/R20160221192158-Mars.tpd" location "http://download.eclipse.org/releases/mars/" { org.eclipse.osgi lazy diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target new file mode 100644 index 0000000000..e506aec37d --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?pde?> +<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> +<target name="jgit-4.5" sequenceNumber="1455835295"> + <locations> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.client.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.continuation" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.continuation.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.http" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.http.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.io" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.io.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.security" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.security.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.server" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.server.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.servlet" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.servlet.source" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.util" version="9.2.13.v20150730"/> + <unit id="org.eclipse.jetty.util.source" version="9.2.13.v20150730"/> + <repository id="jetty-9.2.13" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.2.13.v20150730/"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.apache.ant" version="1.9.4.v201504302020"/> + <unit id="org.apache.ant.source" version="1.9.4.v201504302020"/> + <unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/> + <unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/> + <unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/> + <unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/> + <unit id="org.apache.httpcomponents.httpcore" version="4.3.3.v201411290715"/> + <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201511171540"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201511171540"/> + <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> + <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> + <unit id="org.kohsuke.args4j" version="2.0.21.v201301150030"/> + <unit id="org.kohsuke.args4j.source" version="2.0.21.v201301150030"/> + <unit id="org.hamcrest.core" version="1.3.0.v201303031735"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/> + <unit id="javaewah" version="0.7.9.v201401101600"/> + <unit id="javaewah.source" version="0.7.9.v201401101600"/> + <unit id="org.objenesis" version="1.0.0.v201505121915"/> + <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> + <unit id="org.mockito" version="1.8.4.v201303031500"/> + <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="com.google.gson" version="2.2.4.v201311231704"/> + <unit id="com.jcraft.jsch" version="0.1.53.v201508180515"/> + <unit id="com.jcraft.jsch.source" version="0.1.53.v201508180515"/> + <unit id="org.junit" version="4.11.0.v201303080030"/> + <unit id="org.junit.source" version="4.11.0.v201303080030"/> + <unit id="javax.servlet" version="3.1.0.v201410161800"/> + <unit id="javax.servlet.source" version="3.1.0.v201410161800"/> + <unit id="org.tukaani.xz" version="1.3.0.v201308270617"/> + <unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/> + <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/> + <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> + <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> + <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20151204220443/repository/"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.osgi" version="0.0.0"/> + <repository location="http://download.eclipse.org/releases/mars/"/> + </location> + </locations> +</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd new file mode 100644 index 0000000000..3963361f6c --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd @@ -0,0 +1,8 @@ +target "jgit-4.5" with source configurePhase + +include "projects/jetty-9.2.13.tpd" +include "orbit/S20151204220443-Neon.tpd" + +location "http://download.eclipse.org/releases/mars/" { + org.eclipse.osgi lazy +}
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd index 38c9297cdd..d9e072fffe 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 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2015012407374 org.objenesis.source [1.0.0.v201105211943,1.0.0.v201105211943] org.mockito [1.8.4.v201303031500,1.8.4.v201303031500] org.mockito.source [1.8.4.v201303031500,1.8.4.v201303031500] + com.google.gson [2.2.4.v201311231704,2.2.4.v201311231704] com.jcraft.jsch [0.1.51.v201410302000,0.1.51.v201410302000] com.jcraft.jsch.source [0.1.51.v201410302000,0.1.51.v201410302000] org.junit [4.11.0.v201303080030,4.11.0.v201303080030] diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20160221192158-Mars.tpd index cb638074af..5e47f6854f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20160221192158-Mars.tpd @@ -1,7 +1,7 @@ -target "R20150821153341-Mars" with source configurePhase +target "R20160221192158-Mars" with source configurePhase // see http://download.eclipse.org/tools/orbit/downloads/ -location "http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/" { +location "http://download.eclipse.org/tools/orbit/downloads/drops/R20160221192158/repository/" { org.apache.ant [1.9.4.v201504302020,1.9.4.v201504302020] org.apache.ant.source [1.9.4.v201504302020,1.9.4.v201504302020] org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400] @@ -10,8 +10,8 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2015082115334 org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721] org.apache.httpcomponents.httpcore [4.3.3.v201411290715,4.3.3.v201411290715] org.apache.httpcomponents.httpcore.source [4.3.3.v201411290715,4.3.3.v201411290715] - org.apache.httpcomponents.httpclient [4.3.6.v201411290715,4.3.6.v201411290715] - org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715,4.3.6.v201411290715] + org.apache.httpcomponents.httpclient [4.3.6.v201411290715B,4.3.6.v201411290715B] + org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715B,4.3.6.v201411290715B] org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815] org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815] org.kohsuke.args4j [2.0.21.v201301150030,2.0.21.v201301150030] @@ -24,6 +24,7 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2015082115334 org.objenesis.source [1.0.0.v201505121915,1.0.0.v201505121915] org.mockito [1.8.4.v201303031500,1.8.4.v201303031500] org.mockito.source [1.8.4.v201303031500,1.8.4.v201303031500] + com.google.gson [2.2.4.v201311231704,2.2.4.v201311231704] com.jcraft.jsch [0.1.53.v201508180515,0.1.53.v201508180515] com.jcraft.jsch.source [0.1.53.v201508180515,0.1.53.v201508180515] org.junit [4.11.0.v201303080030,4.11.0.v201303080030] diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20151204220443-Neon.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20151204220443-Neon.tpd new file mode 100644 index 0000000000..3afdbaa3d3 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20151204220443-Neon.tpd @@ -0,0 +1,40 @@ +target "S20151204220443-Neon" with source configurePhase +// see http://download.eclipse.org/tools/orbit/downloads/ + +location "http://download.eclipse.org/tools/orbit/downloads/drops/S20151204220443/repository/" { + org.apache.ant [1.9.4.v201504302020,1.9.4.v201504302020] + org.apache.ant.source [1.9.4.v201504302020,1.9.4.v201504302020] + org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400] + org.apache.commons.compress.source [1.6.0.v201310281400,1.6.0.v201310281400] + org.apache.commons.logging [1.1.1.v201101211721,1.1.1.v201101211721] + org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721] + org.apache.httpcomponents.httpcore [4.3.3.v201411290715,4.3.3.v201411290715] + org.apache.httpcomponents.httpcore.source [4.3.3.v201411290715,4.3.3.v201411290715] + org.apache.httpcomponents.httpclient [4.3.6.v201511171540,4.3.6.v201511171540] + org.apache.httpcomponents.httpclient.source [4.3.6.v201511171540,4.3.6.v201511171540] + org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815] + org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815] + org.kohsuke.args4j [2.0.21.v201301150030,2.0.21.v201301150030] + org.kohsuke.args4j.source [2.0.21.v201301150030,2.0.21.v201301150030] + org.hamcrest.core [1.3.0.v201303031735,1.3.0.v201303031735] + org.hamcrest.core.source [1.3.0.v201303031735,1.3.0.v201303031735] + javaewah [0.7.9.v201401101600,0.7.9.v201401101600] + javaewah.source [0.7.9.v201401101600,0.7.9.v201401101600] + org.objenesis [1.0.0.v201505121915,1.0.0.v201505121915] + 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] + org.junit.source [4.11.0.v201303080030,4.11.0.v201303080030] + javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800] + javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800] + org.tukaani.xz [1.3.0.v201308270617,1.3.0.v201308270617] + org.tukaani.xz.source [1.3.0.v201308270617,1.3.0.v201308270617] + org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250] + org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250] + org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200] + org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200] +}
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index a4fcefaafd..4696b82719 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -49,7 +49,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.target</artifactId> diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index 7231c9ca7b..39b4be9043 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (C) 2009, 2013, Matthias Sohn <matthias.sohn@sap.com> + Copyright (C) 2009, 2015, Matthias Sohn <matthias.sohn@sap.com> and other copyright owners as documented in the project's IP log. This program and the accompanying materials are made available @@ -53,7 +53,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> <packaging>pom</packaging> <name>JGit Tycho Parent</name> @@ -61,6 +61,7 @@ <properties> <tycho-version>0.23.0</tycho-version> <tycho-extras-version>${tycho-version}</tycho-extras-version> + <target-platform>jgit-4.5</target-platform> </properties> <pluginRepositories> @@ -78,6 +79,7 @@ <module>org.eclipse.jgit.target</module> <module>org.eclipse.jgit.feature</module> <module>org.eclipse.jgit.http.apache.feature</module> + <module>org.eclipse.jgit.lfs.feature</module> <module>org.eclipse.jgit.pgm.feature</module> <module>org.eclipse.jgit.source.feature</module> <module>org.eclipse.jgit.pgm.source.feature</module> @@ -227,43 +229,6 @@ <profiles> <profile> - <id>platform-kepler</id> - <activation> - <property> - <name>platform-version-name</name> - <value>kepler</value> - </property> - </activation> - <properties> - <target-platform>jgit-4.3</target-platform> - </properties> - </profile> - <profile> - <id>platform-luna</id> - <activation> - <property> - <name>platform-version-name</name> - <value>luna</value> - </property> - </activation> - <properties> - <target-platform>jgit-4.4</target-platform> - </properties> - </profile> - <profile> - <id>platform-mars</id> - <activation> - <activeByDefault>true</activeByDefault> - <property> - <name>platform-version-name</name> - <value>mars</value> - </property> - </activation> - <properties> - <target-platform>jgit-4.5</target-platform> - </properties> - </profile> - <profile> <id>eclipse-sign</id> <build> <plugins> diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index b64f5f07f8..097319d8c8 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 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.eclipse.jgit.api;version="[4.2.1,4.3.0)", - org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.diff;version="[4.2.1,4.3.0)", - org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="4.2.1", - org.eclipse.jgit.junit;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.merge;version="[4.2.1,4.3.0)", - org.eclipse.jgit.pgm;version="[4.2.1,4.3.0)", - org.eclipse.jgit.pgm.internal;version="[4.2.1,4.3.0)", - org.eclipse.jgit.pgm.opt;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)", +Import-Package: org.eclipse.jgit.api;version="[4.3.2,4.4.0)", + org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.diff;version="[4.3.2,4.4.0)", + org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="4.3.2", + org.eclipse.jgit.junit;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.merge;version="[4.3.2,4.4.0)", + org.eclipse.jgit.pgm;version="[4.3.2,4.4.0)", + org.eclipse.jgit.pgm.internal;version="[4.3.2,4.4.0)", + org.eclipse.jgit.pgm.opt;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)", org.hamcrest.core;bundle-version="[1.1.0,2.0.0)", org.junit;version="[4.4.0,5.0.0)", org.kohsuke.args4j;version="[2.0.12,2.1.0)" diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index 135bf6592b..422532371e 100644 --- a/org.eclipse.jgit.pgm.test/pom.xml +++ b/org.eclipse.jgit.pgm.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm.test</artifactId> diff --git a/org.eclipse.jgit.pgm/BUCK b/org.eclipse.jgit.pgm/BUCK index edcf2fc28f..47711b5c6b 100644 --- a/org.eclipse.jgit.pgm/BUCK +++ b/org.eclipse.jgit.pgm/BUCK @@ -9,8 +9,18 @@ java_library( '//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 5c6a16b4ec..ab0c11bd0f 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -2,48 +2,69 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.apache.commons.compress.archivers;version="[1.3,2.0)", +Import-Package: javax.servlet;version="[3.1.0,4.0.0)", + javax.servlet.http;version="[3.1.0,4.0.0)", + org.apache.commons.compress.archivers;version="[1.3,2.0)", org.apache.commons.compress.archivers.tar;version="[1.3,2.0)", org.apache.commons.compress.archivers.zip;version="[1.3,2.0)", - org.eclipse.jgit.api;version="[4.2.1,4.3.0)", - org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.archive;version="[4.2.1,4.3.0)", - org.eclipse.jgit.awtui;version="[4.2.1,4.3.0)", - org.eclipse.jgit.blame;version="[4.2.1,4.3.0)", - org.eclipse.jgit.diff;version="[4.2.1,4.3.0)", - org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)", - org.eclipse.jgit.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.gitrepo;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.reftree;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.merge;version="4.2.1", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.notes;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revplot;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk.filter;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.pack;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.http.apache;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk.filter;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)", + org.eclipse.jetty.continuation;version="[9.0.0,10.0.0)", + org.eclipse.jetty.http;version="[9.0.0,10.0.0)", + org.eclipse.jetty.io;version="[9.0.0,10.0.0)", + org.eclipse.jetty.security;version="[9.0.0,10.0.0)", + org.eclipse.jetty.security.authentication;version="[9.0.0,10.0.0)", + org.eclipse.jetty.server;version="[9.0.0,10.0.0)", + org.eclipse.jetty.server.handler;version="[9.0.0,10.0.0)", + org.eclipse.jetty.server.nio;version="[9.0.0,10.0.0)", + org.eclipse.jetty.servlet;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)", + org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)", + org.eclipse.jgit.api;version="[4.3.2,4.4.0)", + org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.archive;version="[4.3.2,4.4.0)", + org.eclipse.jgit.awtui;version="[4.3.2,4.4.0)", + org.eclipse.jgit.blame;version="[4.3.2,4.4.0)", + org.eclipse.jgit.diff;version="[4.3.2,4.4.0)", + org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.gitrepo;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.ketch;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.reftree;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.server;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.server.fs;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lfs.server.s3;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.merge;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.notes;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revplot;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk.filter;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.pack;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http.apache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk.filter;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)", org.kohsuke.args4j;version="[2.0.12,2.1.0)", org.kohsuke.args4j.spi;version="[2.0.15,2.1.0)" -Export-Package: org.eclipse.jgit.console;version="4.2.1"; +Export-Package: org.eclipse.jgit.console;version="4.3.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="4.2.1"; + org.eclipse.jgit.pgm;version="4.3.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.pgm.opt, @@ -54,11 +75,11 @@ Export-Package: org.eclipse.jgit.console;version="4.2.1"; org.eclipse.jgit.treewalk, javax.swing, org.eclipse.jgit.transport", - org.eclipse.jgit.pgm.debug;version="4.2.1"; + org.eclipse.jgit.pgm.debug;version="4.3.2"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm", - org.eclipse.jgit.pgm.internal;version="4.2.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="4.2.1"; + org.eclipse.jgit.pgm.internal;version="4.3.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", + org.eclipse.jgit.pgm.opt;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.kohsuke.args4j.spi, diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF index 5df8f53d86..3bbff12e19 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-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.pgm - Sources Bundle-SymbolicName: org.eclipse.jgit.pgm.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.2.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.2.1.qualifier";roots="." +Bundle-Version: 4.3.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.3.2.qualifier";roots="." diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin index 6aa20041b5..c159f7d609 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.UploadPack 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 b9ad706071..8edb94a4ce 100644 --- a/org.eclipse.jgit.pgm/pom.xml +++ b/org.eclipse.jgit.pgm/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm</artifactId> @@ -122,6 +122,34 @@ <artifactId>log4j</artifactId> <version>${log4j-version}</version> </dependency> + + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit.http</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.lfs.server</artifactId> + <version>${project.version}</version> + </dependency> </dependencies> <build> diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index b4b1261b37..fc412ba56f 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -74,6 +74,12 @@ invalidHttpProxyOnlyHttpSupported=Invalid http_proxy: {0}: Only http supported. jgitVersion=jgit version {0} lineFormat={0} listeningOn=Listening on {0} +lfsNoAccessKey=No accessKey in {0} +lfsNoSecretKey=No secretKey in {0} +lfsProtocolUrl=LFS protocol URL: {0} +lfsStoreDirectory=LFS objects stored in: {0} +lfsStoreUrl=LFS store URL: {0} +lfsUnknownStoreType="Unknown LFS store type: {0}" mergeConflict=CONFLICT(content): Merge conflict in {0} mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge: mergeFailed=Automatic merge failed; fix conflicts and then commit the result @@ -107,6 +113,7 @@ metaVar_file=FILE metaVar_filepattern=filepattern metaVar_gitDir=GIT_DIR metaVar_hostName=HOSTNAME +metaVar_lfsStorage=STORAGE metaVar_linesOfContext=lines metaVar_message=message metaVar_n=n @@ -123,6 +130,9 @@ metaVar_refs=REFS metaVar_refspec=refspec metaVar_remoteName=name metaVar_revision=REVISION +metaVar_s3Bucket=BUCKET +metaVar_s3Region=REGION +metaVar_s3StorageClass=STORAGE-CLASS metaVar_seconds=SECONDS metaVar_service=SERVICE metaVar_treeish=tree-ish @@ -168,6 +178,7 @@ remoteMessage=remote: {0} remoteRefObjectChangedIsNotExpectedOne=remote ref object changed - is not expected one {0} remoteSideDoesNotSupportDeletingRefs=remote side does not support deleting refs repaint=Repaint +s3InvalidBucket=Invalid S3 bucket ''{0}'' serviceNotSupported=Service ''{0}'' not supported skippingObject=skipping {0} {1} statusFileListFormat=\t%1$s @@ -218,6 +229,10 @@ usage_DisplayTheVersionOfJgit=Display the version of jgit usage_Gc=Cleanup unnecessary files and optimize the local repository usage_Glog=View commit history as a graph usage_IndexPack=Build pack index file for an existing packed archive +usage_LFSDirectory=Directory to store large objects +usage_LFSPort=Server http port +usage_LFSRunStore=Store (fs | s3), store lfs objects in file system or Amazon S3 +usage_LFSStoreUrl=URL of the LFS store usage_LongFormat=Always output the long format usage_LsRemote=List references in a remote repository usage_lsRemoteHeads=Show only refs starting with refs/heads @@ -229,12 +244,19 @@ usage_MergesTwoDevelopmentHistories=Merges two development histories usage_ReadDirCache= Read the DirCache 100 times usage_RebuildCommitGraph=Recreate a repository from another one's commit graph usage_RebuildRefTree=Copy references into a RefTree +usage_RebuildRefTreeEnable=set extensions.refsStorage = reftree usage_Remote=Manage set of tracked repositories usage_RepositoryToReadFrom=Repository to read from usage_RepositoryToReceiveInto=Repository to receive into usage_RevList=List commit objects in reverse chronological order usage_RevParse=Pick out and massage parameters usage_RevParseAll=Show all refs found in refs/ +usage_S3Bucket=S3 bucket name +usage_S3Expiration=Authorization validity in seconds, default 60 sec +usage_S3Region=S3 region (us-east-1 | us-west-1 | us-west-2 | eu-west-1 |\ + eu-central-1 | ap-southeast-1 | ap- southeast-2 | ap-northeast-1 |\ + ap-northeast-2 | sa-east-1), see http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region +usage_S3StorageClass=S3 storage class (REDUCED_REDUNDANCY | STANDARD), default REDUCED_REDUNDANCY usage_ServerSideBackendForJgitFetch=Server side backend for 'jgit fetch' usage_ServerSideBackendForJgitPush=Server side backend for 'jgit push' usage_ShowCacheTree=Show cache tree @@ -347,6 +369,8 @@ usage_resetReference=Reset to given reference name usage_resetHard=Resets the index and working tree usage_resetSoft=Resets without touching the index file nor the working tree usage_resetMixed=Resets the index but not the working tree +usage_runLfsStore=Run LFS Store in a given directory +usage_S3NoSslVerify=Skip verification of Amazon server certificate and hostname usage_setTheGitRepositoryToOperateOn=set the git repository to operate on usage_show=display one commit usage_showRefNamesMatchingCommits=Show ref names matching commits diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java index 04182d6dbe..03f3fac0b6 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 @@ package org.eclipse.jgit.pgm; 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 @@ class Daemon extends TextBuiltin { 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 @@ class Daemon extends TextBuiltin { 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 0000000000..00bea86c41 --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.pgm.debug; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lfs.server.LargeFileRepository; +import org.eclipse.jgit.lfs.server.LfsProtocolServlet; +import org.eclipse.jgit.lfs.server.fs.FileLfsServlet; +import org.eclipse.jgit.lfs.server.fs.FileLfsRepository; +import org.eclipse.jgit.lfs.server.s3.S3Config; +import org.eclipse.jgit.lfs.server.s3.S3Repository; +import org.eclipse.jgit.pgm.Command; +import org.eclipse.jgit.pgm.TextBuiltin; +import org.eclipse.jgit.pgm.internal.CLIText; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@Command(common = true, usage = "usage_runLfsStore") +class LfsStore extends TextBuiltin { + + /** + * Tiny web application server for testing + */ + class AppServer { + + private final Server server; + + private final ServerConnector connector; + + private final ContextHandlerCollection contexts; + + private URI uri; + + AppServer(int port) { + server = new Server(); + + HttpConfiguration http_config = new HttpConfiguration(); + http_config.setOutputBufferSize(32768); + + connector = new ServerConnector(server, + new HttpConnectionFactory(http_config)); + connector.setPort(port); + try { + String host = InetAddress.getByName("localhost") //$NON-NLS-1$ + .getHostAddress(); + connector.setHost(host); + if (host.contains(":") && !host.startsWith("[")) //$NON-NLS-1$ //$NON-NLS-2$ + host = "[" + host + "]"; //$NON-NLS-1$//$NON-NLS-2$ + uri = new URI("http://" + host + ":" + port); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (UnknownHostException e) { + throw new RuntimeException("Cannot find localhost", e); //$NON-NLS-1$ + } catch (URISyntaxException e) { + throw new RuntimeException("Unexpected URI error on " + uri, e); //$NON-NLS-1$ + } + + contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + server.setConnectors(new Connector[] { connector }); + } + + /** + * Create a new servlet context within the server. + * <p> + * This method should be invoked before the server is started, once for + * each context the caller wants to register. + * + * @param path + * path of the context; use "/" for the root context if + * binding to the root is desired. + * @return the context to add servlets into. + */ + ServletContextHandler addContext(String path) { + assertNotRunning(); + if ("".equals(path)) //$NON-NLS-1$ + path = "/"; //$NON-NLS-1$ + + ServletContextHandler ctx = new ServletContextHandler(); + ctx.setContextPath(path); + contexts.addHandler(ctx); + + return ctx; + } + + void start() throws Exception { + server.start(); + } + + void stop() throws Exception { + server.stop(); + } + + URI getURI() { + return uri; + } + + private void assertNotRunning() { + if (server.isRunning()) { + throw new IllegalStateException("server is running"); //$NON-NLS-1$ + } + } + } + + private static enum StoreType { + FS, S3; + } + + private static enum StorageClass { + REDUCED_REDUNDANCY, STANDARD + } + + private static final String OBJECTS = "objects/"; //$NON-NLS-1$ + + private static final String STORE_PATH = "/" + OBJECTS + "*"; //$NON-NLS-1$//$NON-NLS-2$ + + private static final String PROTOCOL_PATH = "/lfs/objects/batch"; //$NON-NLS-1$ + + @Option(name = "--port", aliases = {"-p" }, + metaVar = "metaVar_port", usage = "usage_LFSPort") + int port; + + @Option(name = "--store", metaVar = "metaVar_lfsStorage", usage = "usage_LFSRunStore") + StoreType storeType; + + @Option(name = "--store-url", aliases = {"-u" }, metaVar = "metaVar_url", + usage = "usage_LFSStoreUrl") + String storeUrl; + + @Option(name = "--region", aliases = {"-r" }, + metaVar = "metaVar_s3Region", usage = "usage_S3Region") + String region; // $NON-NLS-1$ + + @Option(name = "--bucket", aliases = {"-b" }, + metaVar = "metaVar_s3Bucket", usage = "usage_S3Bucket") + String bucket; // $NON-NLS-1$ + + @Option(name = "--storage-class", aliases = {"-c" }, + metaVar = "metaVar_s3StorageClass", usage = "usage_S3StorageClass") + StorageClass storageClass = StorageClass.REDUCED_REDUNDANCY; + + @Option(name = "--expire", aliases = {"-e" }, + metaVar = "metaVar_seconds", usage = "usage_S3Expiration") + int expirationSeconds = 600; + + @Option(name = "--no-ssl-verify", usage = "usage_S3NoSslVerify") + boolean disableSslVerify = false; + + @Argument(required = false, metaVar = "metaVar_directory", usage = "usage_LFSDirectory") + String directory; + + String protocolUrl; + + String accessKey; + + String secretKey; + + @Override + protected boolean requiresRepository() { + return false; + } + + protected void run() throws Exception { + AppServer server = new AppServer(port); + URI baseURI = server.getURI(); + ServletContextHandler app = server.addContext("/"); //$NON-NLS-1$ + + final LargeFileRepository repository; + switch (storeType) { + case FS: + Path dir = Paths.get(directory); + FileLfsRepository fsRepo = new FileLfsRepository( + getStoreUrl(baseURI), dir); + FileLfsServlet content = new FileLfsServlet(fsRepo, 30000); + app.addServlet(new ServletHolder(content), STORE_PATH); + repository = fsRepo; + break; + + case S3: + readAWSKeys(); + checkOptions(); + S3Config config = new S3Config(region.toString(), bucket, + storageClass.toString(), accessKey, secretKey, + expirationSeconds, disableSslVerify); + repository = new S3Repository(config); + break; + default: + throw new IllegalArgumentException(MessageFormat + .format(CLIText.get().lfsUnknownStoreType, storeType)); + } + + LfsProtocolServlet protocol = new LfsProtocolServlet() { + + private static final long serialVersionUID = 1L; + + @Override + protected LargeFileRepository getLargeFileRepository() { + return repository; + } + + }; + app.addServlet(new ServletHolder(protocol), PROTOCOL_PATH); + + server.start(); + + outw.println(MessageFormat.format(CLIText.get().lfsProtocolUrl, + getProtocolUrl(baseURI))); + if (storeType == StoreType.FS) { + outw.println(MessageFormat.format(CLIText.get().lfsStoreDirectory, + directory)); + outw.println(MessageFormat.format(CLIText.get().lfsStoreUrl, + getStoreUrl(baseURI))); + } + } + + private void checkOptions() { + if (bucket == null || bucket.length() == 0) { + throw die(MessageFormat.format(CLIText.get().s3InvalidBucket, + bucket)); + } + } + + private void readAWSKeys() throws IOException, ConfigInvalidException { + String credentialsPath = System.getProperty("user.home") //$NON-NLS-1$ + + "/.aws/credentials"; //$NON-NLS-1$ + FileBasedConfig c = new FileBasedConfig(new File(credentialsPath), + FS.DETECTED); + c.load(); + accessKey = c.getString("default", null, "accessKey"); //$NON-NLS-1$//$NON-NLS-2$ + secretKey = c.getString("default", null, "secretKey"); //$NON-NLS-1$ //$NON-NLS-2$ + if (accessKey == null || accessKey.isEmpty()) { + throw die(MessageFormat.format(CLIText.get().lfsNoAccessKey, + credentialsPath)); + } + if (secretKey == null || secretKey.isEmpty()) { + throw die(MessageFormat.format(CLIText.get().lfsNoSecretKey, + credentialsPath)); + } + } + + private String getStoreUrl(URI baseURI) { + if (storeUrl == null) { + if (storeType == StoreType.FS) { + storeUrl = baseURI + "/" + OBJECTS; //$NON-NLS-1$ + } else { + die("Local store not running and no --store-url specified"); //$NON-NLS-1$ + } + } + return storeUrl; + } + + private String getProtocolUrl(URI baseURI) { + if (protocolUrl == null) { + protocolUrl = baseURI + PROTOCOL_PATH; + } + return protocolUrl; + } +} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java index 78ca1a7128..57345e20dc 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.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.pgm.Command; import org.eclipse.jgit.pgm.TextBuiltin; import org.eclipse.jgit.revwalk.RevWalk; +import org.kohsuke.args4j.Option; @Command(usage = "usage_RebuildRefTree") class RebuildRefTree extends TextBuiltin { + @Option(name = "--enable", usage = "usage_RebuildRefTreeEnable") + boolean enable; + private String txnNamespace; private String txnCommitted; @@ -103,7 +110,7 @@ class RebuildRefTree extends TextBuiltin { oldTreeId = ObjectId.zeroId(); } - RefTree tree = rebuild(refDb.getRefs(RefDatabase.ALL)); + RefTree tree = rebuild(refDb); b.setTreeId(tree.writeTree(inserter)); b.setAuthor(new PersonIdent(db)); b.setCommitter(b.getAuthor()); @@ -122,16 +129,32 @@ class RebuildRefTree extends TextBuiltin { default: throw die(String.format("%s: %s", update.getName(), result)); //$NON-NLS-1$ } + + if (enable && !(db.getRefDatabase() instanceof RefTreeDatabase)) { + StoredConfig cfg = db.getConfig(); + cfg.setInt("core", null, "repositoryformatversion", 1); //$NON-NLS-1$ //$NON-NLS-2$ + cfg.setString("extensions", null, "refsStorage", "reftree"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + cfg.save(); + errw.println("Enabled reftree."); //$NON-NLS-1$ + errw.flush(); + } } } - private RefTree rebuild(Map<String, Ref> refMap) { + private RefTree rebuild(RefDatabase refdb) throws IOException { RefTree tree = RefTree.newEmptyTree(); List<org.eclipse.jgit.internal.storage.reftree.Command> cmds = new ArrayList<>(); - for (Ref r : refMap.values()) { - if (r.getName().equals(txnCommitted) + Ref head = refdb.exactRef(HEAD); + if (head != null) { + cmds.add(new org.eclipse.jgit.internal.storage.reftree.Command( + null, + head)); + } + + for (Ref r : refdb.getRefs(RefDatabase.ALL).values()) { + if (r.getName().equals(txnCommitted) || r.getName().equals(HEAD) || r.getName().startsWith(txnNamespace)) { continue; } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java index 2812137266..e99fe007d9 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java @@ -150,6 +150,12 @@ public class CLIText extends TranslationBundle { /***/ public String initializedEmptyGitRepositoryIn; /***/ public String invalidHttpProxyOnlyHttpSupported; /***/ public String jgitVersion; + /***/ public String lfsNoAccessKey; + /***/ public String lfsNoSecretKey; + /***/ public String lfsProtocolUrl; + /***/ public String lfsStoreDirectory; + /***/ public String lfsStoreUrl; + /***/ public String lfsUnknownStoreType; /***/ public String lineFormat; /***/ public String listeningOn; /***/ public String mergeCheckoutConflict; @@ -178,6 +184,7 @@ public class CLIText extends TranslationBundle { /***/ public String metaVar_filepattern; /***/ public String metaVar_gitDir; /***/ public String metaVar_hostName; + /***/ public String metaVar_lfsStorage; /***/ public String metaVar_linesOfContext; /***/ public String metaVar_message; /***/ public String metaVar_n; @@ -192,6 +199,9 @@ public class CLIText extends TranslationBundle { /***/ public String metaVar_refs; /***/ public String metaVar_refspec; /***/ public String metaVar_remoteName; + /***/ public String metaVar_s3Bucket; + /***/ public String metaVar_s3Region; + /***/ public String metaVar_s3StorageClass; /***/ public String metaVar_seconds; /***/ public String metaVar_service; /***/ public String metaVar_treeish; @@ -237,6 +247,7 @@ public class CLIText extends TranslationBundle { /***/ public String remoteRefObjectChangedIsNotExpectedOne; /***/ public String remoteSideDoesNotSupportDeletingRefs; /***/ public String repaint; + /***/ public String s3InvalidBucket; /***/ public String serviceNotSupported; /***/ public String skippingObject; /***/ public String statusFileListFormat; diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index abb86e8281..4c47ce5b58 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -2,52 +2,52 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)", - org.eclipse.jgit.api;version="[4.2.1,4.3.0)", - org.eclipse.jgit.api.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.attributes;version="[4.2.1,4.3.0)", - org.eclipse.jgit.awtui;version="[4.2.1,4.3.0)", - org.eclipse.jgit.blame;version="[4.2.1,4.3.0)", - org.eclipse.jgit.diff;version="[4.2.1,4.3.0)", - org.eclipse.jgit.dircache;version="[4.2.1,4.3.0)", - org.eclipse.jgit.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.events;version="[4.2.1,4.3.0)", - org.eclipse.jgit.fnmatch;version="[4.2.1,4.3.0)", - org.eclipse.jgit.gitrepo;version="[4.2.1,4.3.0)", - org.eclipse.jgit.hooks;version="[4.2.1,4.3.0)", - org.eclipse.jgit.ignore;version="[4.2.1,4.3.0)", - org.eclipse.jgit.ignore.internal;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.2.1,4.3.0)", - org.eclipse.jgit.internal.storage.reftree;version="[4.2.1,4.3.0)", - org.eclipse.jgit.junit;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.merge;version="[4.2.1,4.3.0)", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.notes;version="[4.2.1,4.3.0)", - org.eclipse.jgit.patch;version="[4.2.1,4.3.0)", - org.eclipse.jgit.pgm;version="[4.2.1,4.3.0)", - org.eclipse.jgit.pgm.internal;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revplot;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk.filter;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.file;version="[4.2.1,4.3.0)", - org.eclipse.jgit.storage.pack;version="[4.2.1,4.3.0)", - org.eclipse.jgit.submodule;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.http;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport.resolver;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.treewalk.filter;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util.io;version="[4.2.1,4.3.0)", + org.eclipse.jgit.api;version="[4.3.2,4.4.0)", + org.eclipse.jgit.api.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.attributes;version="[4.3.2,4.4.0)", + org.eclipse.jgit.awtui;version="[4.3.2,4.4.0)", + org.eclipse.jgit.blame;version="[4.3.2,4.4.0)", + org.eclipse.jgit.diff;version="[4.3.2,4.4.0)", + org.eclipse.jgit.dircache;version="[4.3.2,4.4.0)", + org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.events;version="[4.3.2,4.4.0)", + org.eclipse.jgit.fnmatch;version="[4.3.2,4.4.0)", + org.eclipse.jgit.gitrepo;version="[4.3.2,4.4.0)", + org.eclipse.jgit.hooks;version="[4.3.2,4.4.0)", + org.eclipse.jgit.ignore;version="[4.3.2,4.4.0)", + org.eclipse.jgit.ignore.internal;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.3.2,4.4.0)", + org.eclipse.jgit.internal.storage.reftree;version="[4.3.2,4.4.0)", + org.eclipse.jgit.junit;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.merge;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.notes;version="[4.3.2,4.4.0)", + org.eclipse.jgit.patch;version="[4.3.2,4.4.0)", + org.eclipse.jgit.pgm;version="[4.3.2,4.4.0)", + org.eclipse.jgit.pgm.internal;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revplot;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk.filter;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.file;version="[4.3.2,4.4.0)", + org.eclipse.jgit.storage.pack;version="[4.3.2,4.4.0)", + org.eclipse.jgit.submodule;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.http;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport.resolver;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.treewalk.filter;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util.io;version="[4.3.2,4.4.0)", org.hamcrest;version="[1.1.0,2.0.0)", org.junit;version="[4.4.0,5.0.0)", org.junit.experimental.theories;version="[4.4.0,5.0.0)", diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml index 0985c3df03..a52ac4c075 100644 --- a/org.eclipse.jgit.test/pom.xml +++ b/org.eclipse.jgit.test/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.test</artifactId> diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII.patch new file mode 100644 index 0000000000..d6408c99ff --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII.patch @@ -0,0 +1,8 @@ +diff --git a/NonASCII b/NonASCII +index 2e65efe..7898192 100644 +--- a/NonASCII ++++ b/NonASCII +@@ -1 +1 @@ +-ã‚ +\ No newline at end of file ++ã‚ diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2.patch new file mode 100644 index 0000000000..cea6cfd156 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2.patch @@ -0,0 +1,9 @@ +diff --git a/NonASCII2 b/NonASCII2 +index 2e65efe..7898192 100644 +--- a/NonASCII2 ++++ b/NonASCII2 +@@ -1 +1 @@ +-ä½ å¥½ï¼Œ ä¸–ç•Œï¼ +\ No newline at end of file ++å†è§ +\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PostImage new file mode 100644 index 0000000000..5c8e34dbdb --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PostImage @@ -0,0 +1 @@ +å†è§
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PreImage new file mode 100644 index 0000000000..43696610a6 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII2_PreImage @@ -0,0 +1 @@ +ä½ å¥½ï¼Œ 世界ï¼
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd.patch new file mode 100644 index 0000000000..de31a9eee3 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd.patch @@ -0,0 +1,7 @@ +diff --git a/NonASCIIAdd b/NonASCIIAdd +index 2e65efe..7898192 100644 +--- a/NonASCIIAdd ++++ b/NonASCIIAdd +@@ -0,0 +1 @@ ++ã‚ +\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2.patch new file mode 100644 index 0000000000..5b41e9c26a --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2.patch @@ -0,0 +1,8 @@ +diff --git a/NonASCIIAdd2 b/NonASCIIAdd2 +new file mode 100644 +index 2e65efe..7898192 100644 +--- /dev/null ++++ b/NonASCIIAdd2 +@@ -0,0 +1 @@ ++ã‚ +\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2_PostImage new file mode 100644 index 0000000000..0575c798f0 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd2_PostImage @@ -0,0 +1 @@ +ã‚
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PostImage new file mode 100644 index 0000000000..0575c798f0 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PostImage @@ -0,0 +1 @@ +ã‚
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PreImage new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIAdd_PreImage diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel.patch new file mode 100644 index 0000000000..80e8f82cce --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel.patch @@ -0,0 +1,9 @@ +diff --git a/NonASCIIDel b/NonASCIIDel +deleted file mode 100644 +new file mode 100644 +index 2e65efe..7898192 100644 +--- a/NonASCIIDel ++++ /dev/null +@@ -1 +0,0 @@ +-ã‚ +\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel_PreImage new file mode 100644 index 0000000000..0575c798f0 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCIIDel_PreImage @@ -0,0 +1 @@ +ã‚
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PostImage new file mode 100644 index 0000000000..f2435a2989 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PostImage @@ -0,0 +1 @@ +ã‚ diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PreImage new file mode 100644 index 0000000000..0575c798f0 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/NonASCII_PreImage @@ -0,0 +1 @@ +ã‚
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W.patch new file mode 100644 index 0000000000..cfecb8c5c4 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W.patch @@ -0,0 +1,7 @@ +diff --git a/W b/W +index a3648a1..2d44096 100644 +--- a/W ++++ b/W +@@ -1 +0,0 @@ +-a +\ No newline at end of file
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PostImage new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PostImage diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PreImage new file mode 100644 index 0000000000..2e65efe2a1 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/W_PreImage @@ -0,0 +1 @@ +a
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java index 4fefdfddab..126ca5cdd1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java @@ -53,6 +53,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; +import java.util.Set; import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.api.errors.GitAPIException; @@ -62,14 +63,11 @@ import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; -import org.eclipse.jgit.lib.ConfigConstants; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.FileMode; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.lib.*; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeOptions; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; import org.junit.Test; @@ -1002,6 +1000,91 @@ public class AddCommandTest extends RepositoryTestCase { assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0)); } + @Test + public void testAddGitlink() throws Exception { + createNestedRepo("git-link-dir"); + try (Git git = new Git(db)) { + git.add().addFilepattern("git-link-dir").call(); + + assertEquals( + "[git-link-dir, mode:160000]", + indexState(0)); + Set<String> untrackedFiles = git.status().call().getUntracked(); + assert (untrackedFiles.isEmpty()); + } + + } + + @Test + public void testAddSubrepoWithDirNoGitlinks() throws Exception { + createNestedRepo("nested-repo"); + + // Set DIR_NO_GITLINKS + StoredConfig config = db.getConfig(); + config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true); + config.save(); + + assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks()); + + try (Git git = new Git(db)) { + git.add().addFilepattern("nested-repo").call(); + + assertEquals( + "[nested-repo/README1.md, mode:100644]" + + "[nested-repo/README2.md, mode:100644]", + indexState(0)); + } + + // Turn off DIR_NO_GITLINKS, ensure nested-repo is still treated as + // a normal directory + // Set DIR_NO_GITLINKS + config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, false); + config.save(); + + writeTrashFile("nested-repo", "README3.md", "content"); + + try (Git git = new Git(db)) { + git.add().addFilepattern("nested-repo").call(); + + assertEquals( + "[nested-repo/README1.md, mode:100644]" + + "[nested-repo/README2.md, mode:100644]" + + "[nested-repo/README3.md, mode:100644]", + indexState(0)); + } + } + + @Test + public void testAddGitlinkDoesNotChange() throws Exception { + createNestedRepo("nested-repo"); + + try (Git git = new Git(db)) { + git.add().addFilepattern("nested-repo").call(); + + assertEquals( + "[nested-repo, mode:160000]", + indexState(0)); + } + + // Set DIR_NO_GITLINKS + StoredConfig config = db.getConfig(); + config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true); + config.save(); + + assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks()); + + try (Git git = new Git(db)) { + git.add().addFilepattern("nested-repo").call(); + + assertEquals( + "[nested-repo, mode:160000]", + indexState(0)); + } + } + private static DirCacheEntry addEntryToBuilder(String path, File file, ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage) throws IOException { @@ -1029,4 +1112,25 @@ public class AddCommandTest extends RepositoryTestCase { throw new IOException("could not commit"); } + private void createNestedRepo(String path) throws IOException { + File gitLinkDir = new File(db.getWorkTree(), path); + FileUtils.mkdir(gitLinkDir); + + FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder(); + nestedBuilder.setWorkTree(gitLinkDir); + + Repository nestedRepo = nestedBuilder.build(); + nestedRepo.create(); + + writeTrashFile(path, "README1.md", "content"); + writeTrashFile(path, "README2.md", "content"); + + // Commit these changes in the subrepo + try (Git git = new Git(nestedRepo)) { + git.add().addFilepattern(".").call(); + git.commit().setMessage("subrepo commit").call(); + } catch (GitAPIException e) { + throw new RuntimeException(e); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java index d842046bb7..f2b5b3ba95 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java @@ -147,6 +147,16 @@ public class ApplyCommandTest extends RepositoryTestCase { } @Test + public void testModifyW() throws Exception { + ApplyResult result = init("W"); + assertEquals(1, result.getUpdatedFiles().size()); + assertEquals(new File(db.getWorkTree(), "W"), + result.getUpdatedFiles().get(0)); + checkFile(new File(db.getWorkTree(), "W"), + b.getString(0, b.size(), false)); + } + + @Test public void testModifyX() throws Exception { ApplyResult result = init("X"); assertEquals(1, result.getUpdatedFiles().size()); @@ -186,6 +196,55 @@ public class ApplyCommandTest extends RepositoryTestCase { b.getString(0, b.size(), false)); } + @Test + public void testNonASCII() throws Exception { + ApplyResult result = init("NonASCII"); + assertEquals(1, result.getUpdatedFiles().size()); + assertEquals(new File(db.getWorkTree(), "NonASCII"), + result.getUpdatedFiles().get(0)); + checkFile(new File(db.getWorkTree(), "NonASCII"), + b.getString(0, b.size(), false)); + } + + @Test + public void testNonASCII2() throws Exception { + ApplyResult result = init("NonASCII2"); + assertEquals(1, result.getUpdatedFiles().size()); + assertEquals(new File(db.getWorkTree(), "NonASCII2"), + result.getUpdatedFiles().get(0)); + checkFile(new File(db.getWorkTree(), "NonASCII2"), + b.getString(0, b.size(), false)); + } + + @Test + public void testNonASCIIAdd() throws Exception { + ApplyResult result = init("NonASCIIAdd"); + assertEquals(1, result.getUpdatedFiles().size()); + assertEquals(new File(db.getWorkTree(), "NonASCIIAdd"), + result.getUpdatedFiles().get(0)); + checkFile(new File(db.getWorkTree(), "NonASCIIAdd"), + b.getString(0, b.size(), false)); + } + + @Test + public void testNonASCIIAdd2() throws Exception { + ApplyResult result = init("NonASCIIAdd2", false, true); + assertEquals(1, result.getUpdatedFiles().size()); + assertEquals(new File(db.getWorkTree(), "NonASCIIAdd2"), + result.getUpdatedFiles().get(0)); + checkFile(new File(db.getWorkTree(), "NonASCIIAdd2"), + b.getString(0, b.size(), false)); + } + + @Test + public void testNonASCIIDel() throws Exception { + ApplyResult result = init("NonASCIIDel", true, false); + assertEquals(1, result.getUpdatedFiles().size()); + assertEquals(new File(db.getWorkTree(), "NonASCIIDel"), + result.getUpdatedFiles().get(0)); + assertFalse(new File(db.getWorkTree(), "NonASCIIDel").exists()); + } + private static byte[] readFile(final String patchFile) throws IOException { final InputStream in = getTestResource(patchFile); if (in == null) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index 362d7ac9c9..0bb6610a24 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -88,7 +88,6 @@ import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.FileUtils; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; public class CheckoutCommandTest extends RepositoryTestCase { @@ -633,11 +632,116 @@ public class CheckoutCommandTest extends RepositoryTestCase { } @Test - @Ignore - public void testSmudgeAndClean() throws IOException, GitAPIException { - // @TODO: fix this test - File clean_filter = writeTempFile("sed s/V1/@version/g -"); - File smudge_filter = writeTempFile("sed s/@version/V1/g -"); + public void testSmudgeFilter_deleteFileAndRestoreFromCommit() + throws IOException, GitAPIException { + File script = writeTempFile("sed s/o/e/g"); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "smudge", + "sh " + slashify(script.getPath())); + config.save(); + + writeTrashFile("foo", "foo"); + git.add().addFilepattern("foo").call(); + git.commit().setMessage("initial").call(); + + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("add filter").call(); + + writeTrashFile("src/a.tmp", "foo"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.txt", "foo\n"); + git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") + .call(); + RevCommit content = git.commit().setMessage("added content").call(); + + deleteTrashFile("src/a.txt"); + git.checkout().setStartPoint(content.getName()).addPath("src/a.txt") + .call(); + + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", + indexState(CONTENT)); + assertEquals("foo", read("src/a.tmp")); + assertEquals("fee\n", read("src/a.txt")); + } + + @Test + public void testSmudgeFilter_deleteFileAndRestoreFromIndex() + throws IOException, GitAPIException { + File script = writeTempFile("sed s/o/e/g"); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "smudge", + "sh " + slashify(script.getPath())); + config.save(); + + writeTrashFile("foo", "foo"); + git.add().addFilepattern("foo").call(); + git.commit().setMessage("initial").call(); + + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("add filter").call(); + + writeTrashFile("src/a.tmp", "foo"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.txt", "foo\n"); + git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") + .call(); + git.commit().setMessage("added content").call(); + + deleteTrashFile("src/a.txt"); + git.checkout().addPath("src/a.txt").call(); + + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", + indexState(CONTENT)); + assertEquals("foo", read("src/a.tmp")); + assertEquals("fee\n", read("src/a.txt")); + } + + @Test + public void testSmudgeFilter_deleteFileAndCreateBranchAndRestoreFromCommit() + throws IOException, GitAPIException { + File script = writeTempFile("sed s/o/e/g"); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "smudge", + "sh " + slashify(script.getPath())); + config.save(); + + writeTrashFile("foo", "foo"); + git.add().addFilepattern("foo").call(); + git.commit().setMessage("initial").call(); + + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("add filter").call(); + + writeTrashFile("src/a.tmp", "foo"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.txt", "foo\n"); + git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") + .call(); + RevCommit content = git.commit().setMessage("added content").call(); + + deleteTrashFile("src/a.txt"); + git.checkout().setName("newBranch").setCreateBranch(true) + .setStartPoint(content).addPath("src/a.txt").call(); + + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", + indexState(CONTENT)); + assertEquals("foo", read("src/a.tmp")); + assertEquals("fee\n", read("src/a.txt")); + } + + @Test + public void testSmudgeAndClean() throws Exception { + File clean_filter = writeTempFile("sed s/V1/@version/g"); + File smudge_filter = writeTempFile("sed s/@version/V1/g"); try (Git git2 = new Git(db)) { StoredConfig config = git.getRepository().getConfig(); @@ -646,33 +750,39 @@ public class CheckoutCommandTest extends RepositoryTestCase { config.setString("filter", "tstFilter", "clean", "sh " + slashify(clean_filter.getPath())); config.save(); - writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + writeTrashFile(".gitattributes", "filterTest.txt filter=tstFilter"); git2.add().addFilepattern(".gitattributes").call(); git2.commit().setMessage("add attributes").call(); - writeTrashFile("filterTest.txt", "hello world, V1"); + fsTick(writeTrashFile("filterTest.txt", "hello world, V1\n")); git2.add().addFilepattern("filterTest.txt").call(); - git2.commit().setMessage("add filterText.txt").call(); + RevCommit one = git2.commit().setMessage("add filterText.txt").call(); assertEquals( - "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]", + "[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:hello world, @version\n]", indexState(CONTENT)); - git2.checkout().setCreateBranch(true).setName("test2").call(); - writeTrashFile("filterTest.txt", "bon giorno world, V1"); + fsTick(writeTrashFile("filterTest.txt", "bon giorno world, V1\n")); git2.add().addFilepattern("filterTest.txt").call(); - git2.commit().setMessage("modified filterText.txt").call(); + RevCommit two = git2.commit().setMessage("modified filterTest.txt").call(); + + assertTrue(git2.status().call().isClean()); + assertEquals( + "[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:bon giorno world, @version\n]", + indexState(CONTENT)); + git2.checkout().setName(one.getName()).call(); assertTrue(git2.status().call().isClean()); assertEquals( - "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:bon giorno world, @version]", + "[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:hello world, @version\n]", indexState(CONTENT)); + assertEquals("hello world, V1\n", read("filterTest.txt")); - git2.checkout().setName("refs/heads/test").call(); + git2.checkout().setName(two.getName()).call(); assertTrue(git2.status().call().isClean()); assertEquals( - "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]", + "[.gitattributes, mode:100644, content:filterTest.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:bon giorno world, @version\n]", indexState(CONTENT)); - assertEquals("hello world, V1", read("filterTest.txt")); + assertEquals("bon giorno world, V1\n", read("filterTest.txt")); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java new file mode 100644 index 0000000000..5f10131750 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.api; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.api.errors.CheckoutConflictException; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.NoFilepatternException; +import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.RevisionSyntaxException; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; +import org.eclipse.jgit.lib.CoreConfig.EOL; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.IO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.runner.RunWith; + +/** + * Unit tests for end-of-line conversion and settings using core.autocrlf, * + * core.eol and the .gitattributes eol, text, binary (macro for -diff -merge + * -text) + */ +@RunWith(Theories.class) +public class EolRepositoryTest extends RepositoryTestCase { + private static final FileMode D = FileMode.TREE; + + private static final FileMode F = FileMode.REGULAR_FILE; + + @DataPoint + public static boolean doSmudgeEntries = true; + + @DataPoint + public static boolean dontSmudgeEntries = false; + + private boolean smudge; + + @DataPoint + public static String smallContents[] = { + generateTestData(3, 1, true, false), + generateTestData(3, 1, false, true), + generateTestData(3, 1, true, true) }; + + @DataPoint + public static String hugeContents[] = { + generateTestData(1000000, 17, true, false), + generateTestData(1000000, 17, false, true), + generateTestData(1000000, 17, true, true) }; + + static String generateTestData(int size, int lineSize, boolean withCRLF, + boolean withLF) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < size; i++) { + if (i > 0 && i % lineSize == 0) { + // newline + if (withCRLF && withLF) { + // mixed + if (i % 2 == 0) + sb.append("\r\n"); + else + sb.append("\n"); + } else if (withCRLF) { + sb.append("\r\n"); + } else if (withLF) { + sb.append("\n"); + } + } + sb.append("A"); + } + return sb.toString(); + } + + public EolRepositoryTest(String[] testContent, boolean smudgeEntries) { + CONTENT_CRLF = testContent[0]; + CONTENT_LF = testContent[1]; + CONTENT_MIXED = testContent[2]; + this.smudge = smudgeEntries; + } + + protected String CONTENT_CRLF; + + protected String CONTENT_LF; + + protected String CONTENT_MIXED; + + private TreeWalk walk; + + /** work tree root .gitattributes */ + private File dotGitattributes; + + /** file containing CRLF */ + private File fileCRLF; + + /** file containing LF */ + private File fileLF; + + /** file containing mixed CRLF and LF */ + private File fileMixed; + + /** this values are set in {@link #collectRepositoryState()} */ + private static class ActualEntry { + private String attrs; + + private String file; + + private String index; + + private int indexContentLength; + } + + private ActualEntry entryCRLF = new ActualEntry(); + + private ActualEntry entryLF = new ActualEntry(); + + private ActualEntry entryMixed = new ActualEntry(); + + private DirCache dirCache; + + @Test + public void testDefaultSetup() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, null, null, null, "* text=auto"); + collectRepositoryState(); + assertEquals("text=auto", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + public void checkEntryContent(ActualEntry entry, String fileContent, + String indexContent) { + assertEquals(fileContent, entry.file); + assertEquals(indexContent, entry.index); + if (entry.indexContentLength != 0) { + assertEquals(fileContent.length(), entry.indexContentLength); + } + } + + @Test + public void test_ConfigAutoCRLF_false() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.FALSE, null, null, null, "* text=auto"); + collectRepositoryState(); + assertEquals("text=auto", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_true() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.TRUE, null, null, null, "* text=auto"); + collectRepositoryState(); + assertEquals("text=auto", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_input() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.INPUT, null, null, null, "* text=auto"); + collectRepositoryState(); + assertEquals("text=auto", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigEOL_lf() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, EOL.LF, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigEOL_crlf() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, EOL.CRLF, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_ConfigEOL_native_windows() throws Exception { + String origLineSeparator = System.getProperty("line.separator", "\n"); + System.setProperty("line.separator", "\r\n"); + try { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + } finally { + System.setProperty("line.separator", origLineSeparator); + } + } + + @Test + public void test_ConfigEOL_native_xnix() throws Exception { + String origLineSeparator = System.getProperty("line.separator", "\n"); + System.setProperty("line.separator", "\n"); + try { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + } finally { + System.setProperty("line.separator", origLineSeparator); + } + } + + @Test + public void test_ConfigAutoCRLF_false_ConfigEOL_lf() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_false_ConfigEOL_native() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.NATIVE, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_true_ConfigEOL_lf() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_switchToBranchWithTextAttributes() + throws Exception { + Git git = Git.wrap(db); + + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.CRLF, null, null, + "file1.txt text\nfile2.txt text\nfile3.txt text"); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + + // switch to binary for file1 + dotGitattributes = createAndAddFile(git, Constants.DOT_GIT_ATTRIBUTES, + "file1.txt binary\nfile2.txt text\nfile3.txt text"); + gitCommit(git, "switchedToBinaryFor1"); + recreateWorktree(git); + collectRepositoryState(); + assertEquals("binary -diff -merge -text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + assertEquals("text", entryLF.attrs); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + assertEquals("text", entryMixed.attrs); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + + // checkout the commit which has text for file1 + gitCheckout(git, "HEAD^"); + recreateWorktree(git); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_switchToBranchWithBinaryAttributes() throws Exception { + Git git = Git.wrap(db); + + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, null, null, + "file1.txt binary\nfile2.txt binary\nfile3.txt binary"); + collectRepositoryState(); + assertEquals("binary -diff -merge -text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED); + + // switch to text for file1 + dotGitattributes = createAndAddFile(git, Constants.DOT_GIT_ATTRIBUTES, + "file1.txt text\nfile2.txt binary\nfile3.txt binary"); + gitCommit(git, "switchedToTextFor1"); + recreateWorktree(git); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + assertEquals("binary -diff -merge -text", entryLF.attrs); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + assertEquals("binary -diff -merge -text", entryMixed.attrs); + checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED); + + // checkout the commit which has text for file1 + gitCheckout(git, "HEAD^"); + recreateWorktree(git); + collectRepositoryState(); + assertEquals("binary -diff -merge -text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED); + } + + @Test + public void test_ConfigAutoCRLF_input_ConfigEOL_lf() throws Exception { + // for EOL to work, the text attribute must be set + setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt text", null, null); + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_true_GlobalEOL_lf() throws Exception { + setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt eol=lf", null, null); + collectRepositoryState(); + assertEquals("eol=lf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_false_GlobalEOL_lf() throws Exception { + setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt eol=lf", null, null); + collectRepositoryState(); + assertEquals("eol=lf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_input_GlobalEOL_lf() throws Exception { + setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt eol=lf", null, null); + collectRepositoryState(); + assertEquals("eol=lf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_true_GlobalEOL_crlf() throws Exception { + setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt eol=crlf", null, null); + collectRepositoryState(); + assertEquals("eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_false_GlobalEOL_crlf() throws Exception { + setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt eol=crlf", null, null); + collectRepositoryState(); + assertEquals("eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_input_GlobalEOL_crlf() throws Exception { + setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt eol=crlf", null, null); + collectRepositoryState(); + assertEquals("eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_true_GlobalEOL_lf_InfoEOL_crlf() + throws Exception { + setupGitAndDoHardReset(AutoCRLF.TRUE, null, "*.txt eol=lf", "*.txt eol=crlf", null); + // info decides + collectRepositoryState(); + assertEquals("eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_ConfigAutoCRLF_false_GlobalEOL_crlf_InfoEOL_lf() + throws Exception { + setupGitAndDoHardReset(AutoCRLF.FALSE, null, "*.txt eol=crlf", "*.txt eol=lf", null); + // info decides + collectRepositoryState(); + assertEquals("eol=lf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void test_GlobalEOL_lf_RootEOL_crlf() throws Exception { + setupGitAndDoHardReset(null, null, "*.txt eol=lf", null, "*.txt eol=crlf"); + // root over global + collectRepositoryState(); + assertEquals("eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_GlobalEOL_lf_InfoEOL_crlf_RootEOL_lf() throws Exception { + setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt eol=crlf", "*.txt eol=lf"); + // info overrides all + collectRepositoryState(); + assertEquals("eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_GlobalEOL_lf_InfoEOL_crlf_RootEOL_unspec() + throws Exception { + setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt eol=crlf", + "*.txt text !eol"); + // info overrides all + collectRepositoryState(); + assertEquals("eol=crlf text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF); + } + + @Test + public void test_GlobalEOL_lf_InfoEOL_unspec_RootEOL_crlf() + throws Exception { + setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt !eol", + "*.txt text eol=crlf"); + // info overrides all + collectRepositoryState(); + assertEquals("text", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF); + } + + @Test + public void testBinary1() throws Exception { + setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.CRLF, "*.txt text", "*.txt binary", + "*.txt eol=crlf"); + // info overrides all + collectRepositoryState(); + assertEquals("binary -diff -merge -text eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED); + } + + @Test + public void testBinary2() throws Exception { + setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.CRLF, "*.txt text eol=crlf", null, + "*.txt binary"); + // root over global + collectRepositoryState(); + assertEquals("binary -diff -merge -text eol=crlf", entryCRLF.attrs); + checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF); + checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF); + checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED); + } + + // create new repo with + // global .gitattributes + // info .git/config/info/.gitattributes + // workdir root .gitattributes + // text file lf.txt CONTENT_LF + // text file crlf.txt CONTENT_CRLF + // + // commit files (checkin) + // delete working dir files + // reset hard (checkout) + private void setupGitAndDoHardReset(AutoCRLF autoCRLF, EOL eol, + String globalAttributesContent, String infoAttributesContent, + String workDirRootAttributesContent) throws Exception { + Git git = new Git(db); + FileBasedConfig config = db.getConfig(); + if (autoCRLF != null) { + config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_AUTOCRLF, autoCRLF); + } + if (eol != null) { + config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_EOL, eol); + } + if (globalAttributesContent != null) { + File f = new File(db.getDirectory(), "global/attrs"); + write(f, globalAttributesContent); + config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE, + f.getAbsolutePath()); + + } + if (infoAttributesContent != null) { + File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES); + write(f, infoAttributesContent); + } + config.save(); + + if (workDirRootAttributesContent != null) { + dotGitattributes = createAndAddFile(git, + Constants.DOT_GIT_ATTRIBUTES, workDirRootAttributesContent); + } else { + dotGitattributes = null; + } + + fileCRLF = createAndAddFile(git, "file1.txt", "a"); + + fileLF = createAndAddFile(git, "file2.txt", "a"); + + fileMixed = createAndAddFile(git, "file3.txt", "a"); + + RevCommit c = gitCommit(git, "create files"); + + fileCRLF = createAndAddFile(git, "file1.txt", CONTENT_CRLF); + + fileLF = createAndAddFile(git, "file2.txt", CONTENT_LF); + + fileMixed = createAndAddFile(git, "file3.txt", CONTENT_MIXED); + + gitCommit(git, "addFiles"); + + recreateWorktree(git); + + if (smudge) { + DirCache dc = DirCache.lock(git.getRepository().getIndexFile(), + FS.detect()); + DirCacheEditor editor = dc.editor(); + for (int i = 0; i < dc.getEntryCount(); i++) { + editor.add(new DirCacheEditor.PathEdit( + dc.getEntry(i).getPathString()) { + public void apply(DirCacheEntry ent) { + ent.smudgeRacilyClean(); + } + }); + } + editor.commit(); + } + + // @TODO: find out why the following assertion would break the tests + // assertTrue(git.status().call().isClean()); + git.checkout().setName(c.getName()).call(); + git.checkout().setName("master").call(); + } + + private void recreateWorktree(Git git) + throws GitAPIException, CheckoutConflictException, + InterruptedException, IOException, NoFilepatternException { + // re-create file from the repo + for (File f : new File[] { dotGitattributes, fileCRLF, fileLF, fileMixed }) { + if (f == null) + continue; + f.delete(); + Assert.assertFalse(f.exists()); + } + gitResetHard(git); + fsTick(db.getIndexFile()); + gitAdd(git, "."); + } + + protected RevCommit gitCommit(Git git, String msg) throws GitAPIException { + return git.commit().setMessage(msg).call(); + } + + protected void gitAdd(Git git, String path) throws GitAPIException { + git.add().addFilepattern(path).call(); + } + + protected void gitResetHard(Git git) throws GitAPIException { + git.reset().setMode(ResetType.HARD).call(); + } + + protected void gitCheckout(Git git, String revstr) + throws GitAPIException, RevisionSyntaxException, IOException { + git.checkout().setName(db.resolve(revstr).getName()).call(); + } + + // create a file and add it to the repo + private File createAndAddFile(Git git, String path, String content) + throws Exception { + File f; + int pos = path.lastIndexOf('/'); + if (pos < 0) { + f = writeTrashFile(path, content); + } else { + f = writeTrashFile(path.substring(0, pos), path.substring(pos + 1), + content); + } + gitAdd(git, path); + Assert.assertTrue(f.exists()); + return f; + } + + private void collectRepositoryState() throws Exception { + dirCache = db.readDirCache(); + walk = beginWalk(); + if (dotGitattributes != null) + collectEntryContentAndAttributes(F, ".gitattributes", null); + collectEntryContentAndAttributes(F, fileCRLF.getName(), entryCRLF); + collectEntryContentAndAttributes(F, fileLF.getName(), entryLF); + collectEntryContentAndAttributes(F, fileMixed.getName(), entryMixed); + endWalk(); + } + + private TreeWalk beginWalk() throws Exception { + TreeWalk newWalk = new TreeWalk(db); + newWalk.addTree(new FileTreeIterator(db)); + newWalk.addTree(new DirCacheIterator(db.readDirCache())); + return newWalk; + } + + private void endWalk() throws IOException { + assertFalse("Not all files tested", walk.next()); + } + + private void collectEntryContentAndAttributes(FileMode type, String pathName, + ActualEntry e) throws IOException { + assertTrue("walk has entry", walk.next()); + + assertEquals(pathName, walk.getPathString()); + assertEquals(type, walk.getFileMode(0)); + + if (e != null) { + e.attrs = ""; + for (Attribute a : walk.getAttributes().getAll()) { + e.attrs += " " + a.toString(); + } + e.attrs = e.attrs.trim(); + e.file = new String( + IO.readFully(new File(db.getWorkTree(), pathName))); + DirCacheEntry dce = dirCache.getEntry(pathName); + ObjectLoader open = walk.getObjectReader().open(dce.getObjectId()); + e.index = new String(open.getBytes()); + e.indexContentLength = dce.getLength(); + } + + if (D.equals(type)) + walk.enterSubtree(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java new file mode 100644 index 0000000000..8ca1b3175d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.api; + +import static org.junit.Assert.assertArrayEquals; +import static org.eclipse.jgit.lib.CoreConfig.EolStreamType.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.io.EolStreamTypeUtil; +import org.junit.Test; + +/** + * Unit tests for end-of-line conversion streams + */ +public class EolStreamTypeUtilTest { + + @Test + public void testCheckoutDirect() throws Exception { + testCheckout(DIRECT, DIRECT, "", ""); + testCheckout(DIRECT, DIRECT, "\r", "\r"); + testCheckout(DIRECT, DIRECT, "\n", "\n"); + + testCheckout(DIRECT, DIRECT, "\r\n", "\r\n"); + testCheckout(DIRECT, DIRECT, "\n\r", "\n\r"); + + testCheckout(DIRECT, DIRECT, "\n\r\n", "\n\r\n"); + testCheckout(DIRECT, DIRECT, "\r\n\r", "\r\n\r"); + + testCheckout(DIRECT, DIRECT, "a\nb\n", "a\nb\n"); + testCheckout(DIRECT, DIRECT, "a\rb\r", "a\rb\r"); + testCheckout(DIRECT, DIRECT, "a\n\rb\n\r", "a\n\rb\n\r"); + testCheckout(DIRECT, DIRECT, "a\r\nb\r\n", "a\r\nb\r\n"); + } + + @Test + public void testCheckoutLF() throws Exception { + testCheckout(TEXT_LF, AUTO_LF, "", ""); + testCheckout(TEXT_LF, AUTO_LF, "\r", "\r"); + testCheckout(TEXT_LF, AUTO_LF, "\n", "\n"); + + testCheckout(TEXT_LF, AUTO_LF, "\r\n", "\n"); + testCheckout(TEXT_LF, AUTO_LF, "\n\r", "\n\r"); + + testCheckout(TEXT_LF, AUTO_LF, "\n\r\n", "\n\n"); + testCheckout(TEXT_LF, AUTO_LF, "\r\n\r", "\n\r"); + + testCheckout(TEXT_LF, AUTO_LF, "a\nb\n", "a\nb\n"); + testCheckout(TEXT_LF, AUTO_LF, "a\rb\r", "a\rb\r"); + testCheckout(TEXT_LF, AUTO_LF, "a\n\rb\n\r", "a\n\rb\n\r"); + testCheckout(TEXT_LF, AUTO_LF, "a\r\nb\r\n", "a\nb\n"); + } + + @Test + public void testCheckoutCRLF() throws Exception { + testCheckout(TEXT_CRLF, AUTO_CRLF, "", ""); + testCheckout(TEXT_CRLF, AUTO_CRLF, "\r", "\r"); + testCheckout(TEXT_CRLF, AUTO_CRLF, "\n", "\r\n"); + + testCheckout(TEXT_CRLF, AUTO_CRLF, "\r\n", "\r\n"); + testCheckout(TEXT_CRLF, AUTO_CRLF, "\n\r", "\r\n\r"); + + testCheckout(TEXT_CRLF, AUTO_CRLF, "\n\r\n", "\r\n\r\n"); + testCheckout(TEXT_CRLF, AUTO_CRLF, "\r\n\r", "\r\n\r"); + + testCheckout(TEXT_CRLF, AUTO_CRLF, "a\nb\n", "a\r\nb\r\n"); + testCheckout(TEXT_CRLF, AUTO_CRLF, "a\rb\r", "a\rb\r"); + testCheckout(TEXT_CRLF, AUTO_CRLF, "a\n\rb\n\r", "a\r\n\rb\r\n\r"); + testCheckout(TEXT_CRLF, AUTO_CRLF, "a\r\nb\r\n", "a\r\nb\r\n"); + } + + /** + * Test stream type detection based on stream content. + * <p> + * Tests three things with the output text: + * <p> + * 1) conversion if output was declared as text + * <p> + * 2) conversion if output was declared as potentially text (AUTO_...) and + * is in fact text + * <p> + * 3) conversion if modified output (now with binary characters) was + * declared as potentially text but now contains binary characters + * <p> + * + * @param streamTypeText + * is the enum meaning that the output is definitely text (no + * binary check at all) + * @param streamTypeWithBinaryCheck + * is the enum meaning that the output may be text (binary check + * is done) + * @param output + * is a text output without binary characters + * @param expectedConversion + * is the expected converted output without binary characters + * @throws Exception + */ + private void testCheckout(EolStreamType streamTypeText, + EolStreamType streamTypeWithBinaryCheck, String output, + String expectedConversion) throws Exception { + ByteArrayOutputStream b; + byte[] outputBytes = output.getBytes(StandardCharsets.UTF_8); + byte[] expectedConversionBytes = expectedConversion + .getBytes(StandardCharsets.UTF_8); + + // test using output text and assuming it was declared TEXT + b = new ByteArrayOutputStream(); + try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b, + streamTypeText)) { + out.write(outputBytes); + } + assertArrayEquals(expectedConversionBytes, b.toByteArray()); + + // test using ouput text and assuming it was declared AUTO, using binary + // detection + b = new ByteArrayOutputStream(); + try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b, + streamTypeWithBinaryCheck)) { + out.write(outputBytes); + } + assertArrayEquals(expectedConversionBytes, b.toByteArray()); + + // now pollute output text with some binary bytes + outputBytes = extendWithBinaryData(outputBytes); + expectedConversionBytes = extendWithBinaryData(expectedConversionBytes); + + // again, test using output text and assuming it was declared TEXT + b = new ByteArrayOutputStream(); + try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b, + streamTypeText)) { + out.write(outputBytes); + } + assertArrayEquals(expectedConversionBytes, b.toByteArray()); + + // again, test using ouput text and assuming it was declared AUTO, using + // binary + // detection + b = new ByteArrayOutputStream(); + try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b, + streamTypeWithBinaryCheck)) { + out.write(outputBytes); + } + // expect no conversion + assertArrayEquals(outputBytes, b.toByteArray()); + } + + @Test + public void testCheckinDirect() throws Exception { + testCheckin(DIRECT, DIRECT, "", ""); + testCheckin(DIRECT, DIRECT, "\r", "\r"); + testCheckin(DIRECT, DIRECT, "\n", "\n"); + + testCheckin(DIRECT, DIRECT, "\r\n", "\r\n"); + testCheckin(DIRECT, DIRECT, "\n\r", "\n\r"); + + testCheckin(DIRECT, DIRECT, "\n\r\n", "\n\r\n"); + testCheckin(DIRECT, DIRECT, "\r\n\r", "\r\n\r"); + + testCheckin(DIRECT, DIRECT, "a\nb\n", "a\nb\n"); + testCheckin(DIRECT, DIRECT, "a\rb\r", "a\rb\r"); + testCheckin(DIRECT, DIRECT, "a\n\rb\n\r", "a\n\rb\n\r"); + testCheckin(DIRECT, DIRECT, "a\r\nb\r\n", "a\r\nb\r\n"); + } + + @Test + public void testCheckinLF() throws Exception { + testCheckin(TEXT_LF, AUTO_LF, "", ""); + testCheckin(TEXT_LF, AUTO_LF, "\r", "\r"); + testCheckin(TEXT_LF, AUTO_LF, "\n", "\n"); + + testCheckin(TEXT_LF, AUTO_LF, "\r\n", "\n"); + testCheckin(TEXT_LF, AUTO_LF, "\n\r", "\n\r"); + + testCheckin(TEXT_LF, AUTO_LF, "\n\r\n", "\n\n"); + testCheckin(TEXT_LF, AUTO_LF, "\r\n\r", "\n\r"); + + testCheckin(TEXT_LF, AUTO_LF, "a\nb\n", "a\nb\n"); + testCheckin(TEXT_LF, AUTO_LF, "a\rb\r", "a\rb\r"); + testCheckin(TEXT_LF, AUTO_LF, "a\n\rb\n\r", "a\n\rb\n\r"); + testCheckin(TEXT_LF, AUTO_LF, "a\r\nb\r\n", "a\nb\n"); + } + + @Test + public void testCheckinCRLF() throws Exception { + testCheckin(TEXT_CRLF, AUTO_CRLF, "", ""); + testCheckin(TEXT_CRLF, AUTO_CRLF, "\r", "\r"); + testCheckin(TEXT_CRLF, AUTO_CRLF, "\n", "\r\n"); + + testCheckin(TEXT_CRLF, AUTO_CRLF, "\r\n", "\r\n"); + testCheckin(TEXT_CRLF, AUTO_CRLF, "\n\r", "\r\n\r"); + + testCheckin(TEXT_CRLF, AUTO_CRLF, "\n\r\n", "\r\n\r\n"); + testCheckin(TEXT_CRLF, AUTO_CRLF, "\r\n\r", "\r\n\r"); + + testCheckin(TEXT_CRLF, AUTO_CRLF, "a\nb\n", "a\r\nb\r\n"); + testCheckin(TEXT_CRLF, AUTO_CRLF, "a\rb\r", "a\rb\r"); + testCheckin(TEXT_CRLF, AUTO_CRLF, "a\n\rb\n\r", "a\r\n\rb\r\n\r"); + testCheckin(TEXT_CRLF, AUTO_CRLF, "a\r\nb\r\n", "a\r\nb\r\n"); + } + + /** + * Test stream type detection based on stream content. + * <p> + * Tests three things with the input text: + * <p> + * 1) conversion if input was declared as text + * <p> + * 2) conversion if input was declared as potentially text (AUTO_...) and is + * in fact text + * <p> + * 3) conversion if modified input (now with binary characters) was declared + * as potentially text but now contains binary characters + * <p> + * + * @param streamTypeText + * is the enum meaning that the input is definitely text (no + * binary check at all) + * @param streamTypeWithBinaryCheck + * is the enum meaning that the input may be text (binary check + * is done) + * @param input + * is a text input without binary characters + * @param expectedConversion + * is the expected converted input without binary characters + * @throws Exception + */ + private void testCheckin(EolStreamType streamTypeText, + EolStreamType streamTypeWithBinaryCheck, String input, + String expectedConversion) throws Exception { + byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8); + byte[] expectedConversionBytes = expectedConversion + .getBytes(StandardCharsets.UTF_8); + + // test using input text and assuming it was declared TEXT + try (InputStream in = EolStreamTypeUtil.wrapInputStream( + new ByteArrayInputStream(inputBytes), + streamTypeText)) { + byte[] b = new byte[1024]; + int len = IO.readFully(in, b, 0); + assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len)); + } + + // test using input text and assuming it was declared AUTO, using binary + // detection + try (InputStream in = EolStreamTypeUtil.wrapInputStream( + new ByteArrayInputStream(inputBytes), + streamTypeWithBinaryCheck)) { + byte[] b = new byte[1024]; + int len = IO.readFully(in, b, 0); + assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len)); + } + + // now pollute input text with some binary bytes + inputBytes = extendWithBinaryData(inputBytes); + expectedConversionBytes = extendWithBinaryData(expectedConversionBytes); + + // again, test using input text and assuming it was declared TEXT + try (InputStream in = EolStreamTypeUtil.wrapInputStream( + new ByteArrayInputStream(inputBytes), streamTypeText)) { + byte[] b = new byte[1024]; + int len = IO.readFully(in, b, 0); + assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len)); + } + + // again, test using input text and assuming it was declared AUTO, using + // binary + // detection + try (InputStream in = EolStreamTypeUtil.wrapInputStream( + new ByteArrayInputStream(inputBytes), + streamTypeWithBinaryCheck)) { + byte[] b = new byte[1024]; + int len = IO.readFully(in, b, 0); + // expect no conversion + assertArrayEquals(inputBytes, Arrays.copyOf(b, len)); + } + } + + private byte[] extendWithBinaryData(byte[] data) throws Exception { + int n = 3; + byte[] dataEx = new byte[data.length + n]; + System.arraycopy(data, 0, dataEx, 0, data.length); + for (int i = 0; i < n; i++) { + dataEx[data.length + i] = (byte) i; + } + return dataEx; + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java new file mode 100644 index 0000000000..ca456b3c8a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java @@ -0,0 +1,339 @@ +/* + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.attributes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.junit.Test; + +/** + * Tests {@link AttributesHandler} + */ +public class AttributesHandlerTest extends RepositoryTestCase { + private static final FileMode D = FileMode.TREE; + + private static final FileMode F = FileMode.REGULAR_FILE; + + private TreeWalk walk; + + @Test + public void testExpandNonMacro1() throws Exception { + setupRepo(null, null, null, "*.txt text"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("text")); + endWalk(); + } + + @Test + public void testExpandNonMacro2() throws Exception { + setupRepo(null, null, null, "*.txt -text"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("-text")); + endWalk(); + } + + @Test + public void testExpandNonMacro3() throws Exception { + setupRepo(null, null, null, "*.txt !text"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("")); + endWalk(); + } + + @Test + public void testExpandNonMacro4() throws Exception { + setupRepo(null, null, null, "*.txt text=auto"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("text=auto")); + endWalk(); + } + + @Test + public void testExpandBuiltInMacro1() throws Exception { + setupRepo(null, null, null, "*.txt binary"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("binary -diff -merge -text")); + endWalk(); + } + + @Test + public void testExpandBuiltInMacro2() throws Exception { + setupRepo(null, null, null, "*.txt -binary"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("-binary diff merge text")); + endWalk(); + } + + @Test + public void testExpandBuiltInMacro3() throws Exception { + setupRepo(null, null, null, "*.txt !binary"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("")); + endWalk(); + } + + @Test + public void testCustomGlobalMacro1() throws Exception { + setupRepo( + "[attr]foo a -b !c d=e", null, null, "*.txt foo"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo a -b d=e")); + endWalk(); + } + + @Test + public void testCustomGlobalMacro2() throws Exception { + setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt -foo"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("-foo -a b d=e")); + endWalk(); + } + + @Test + public void testCustomGlobalMacro3() throws Exception { + setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt !foo"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("")); + endWalk(); + } + + @Test + public void testCustomGlobalMacro4() throws Exception { + setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt foo=bar"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo=bar a -b d=bar")); + endWalk(); + } + + @Test + public void testInfoOverridesGlobal() throws Exception { + setupRepo("[attr]foo bar1", + "[attr]foo bar2", null, "*.txt foo"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo bar2")); + endWalk(); + } + + @Test + public void testWorkDirRootOverridesGlobal() throws Exception { + setupRepo("[attr]foo bar1", + null, + "[attr]foo bar3", "*.txt foo"); + + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo bar3")); + endWalk(); + } + + @Test + public void testInfoOverridesWorkDirRoot() throws Exception { + setupRepo("[attr]foo bar1", + "[attr]foo bar2", "[attr]foo bar3", "*.txt foo"); + + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo bar2")); + endWalk(); + } + + @Test + public void testRecursiveMacro() throws Exception { + setupRepo( + "[attr]foo x bar -foo", + null, null, "*.txt foo"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo x bar")); + endWalk(); + } + + @Test + public void testCyclicMacros() throws Exception { + setupRepo( + "[attr]foo x -bar\n[attr]bar y -foo", null, null, "*.txt foo"); + + walk = beginWalk(); + assertIteration(D, "sub"); + assertIteration(F, "sub/.gitattributes"); + assertIteration(F, "sub/a.txt", attrs("foo x -bar -y")); + endWalk(); + } + + private static Collection<Attribute> attrs(String s) { + return new AttributesRule("*", s).getAttributes(); + } + + private void assertIteration(FileMode type, String pathName) + throws IOException { + assertIteration(type, pathName, Collections.<Attribute> emptyList()); + } + + private void assertIteration(FileMode type, String pathName, + Collection<Attribute> expectedAttrs) throws IOException { + assertTrue("walk has entry", walk.next()); + assertEquals(pathName, walk.getPathString()); + assertEquals(type, walk.getFileMode(0)); + + if (expectedAttrs != null) { + assertEquals(new ArrayList<>(expectedAttrs), + new ArrayList<>(walk.getAttributes().getAll())); + } + + if (D.equals(type)) + walk.enterSubtree(); + } + + /** + * @param globalAttributesContent + * @param infoAttributesContent + * @param rootAttributesContent + * @param subDirAttributesContent + * @throws Exception + * Setup a repo with .gitattributes files and a test file + * sub/a.txt + */ + private void setupRepo( + String globalAttributesContent, + String infoAttributesContent, String rootAttributesContent, String subDirAttributesContent) + throws Exception { + FileBasedConfig config = db.getConfig(); + if (globalAttributesContent != null) { + File f = new File(db.getDirectory(), "global/attributes"); + write(f, globalAttributesContent); + config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE, + f.getAbsolutePath()); + + } + if (infoAttributesContent != null) { + File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES); + write(f, infoAttributesContent); + } + config.save(); + + if (rootAttributesContent != null) { + writeAttributesFile(Constants.DOT_GIT_ATTRIBUTES, + rootAttributesContent); + } + + if (subDirAttributesContent != null) { + writeAttributesFile("sub/" + Constants.DOT_GIT_ATTRIBUTES, + subDirAttributesContent); + } + + writeTrashFile("sub/a.txt", "a"); + } + + private void writeAttributesFile(String name, String... rules) + throws IOException { + StringBuilder data = new StringBuilder(); + for (String line : rules) + data.append(line + "\n"); + writeTrashFile(name, data.toString()); + } + + private TreeWalk beginWalk() { + TreeWalk newWalk = new TreeWalk(db); + newWalk.addTree(new FileTreeIterator(db)); + return newWalk; + } + + private void endWalk() throws IOException { + assertFalse("Not all files tested", walk.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java index 9f82b8a1e9..e8dd952324 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java @@ -293,28 +293,28 @@ public class AttributesMatcherTest { public void testGetters() { AttributesRule r = new AttributesRule("/pattern/", ""); assertFalse(r.isNameOnly()); - assertTrue(r.dirOnly()); + assertTrue(r.isDirOnly()); assertNotNull(r.getAttributes()); assertTrue(r.getAttributes().isEmpty()); assertEquals(r.getPattern(), "/pattern"); r = new AttributesRule("/patter?/", ""); assertFalse(r.isNameOnly()); - assertTrue(r.dirOnly()); + assertTrue(r.isDirOnly()); assertNotNull(r.getAttributes()); assertTrue(r.getAttributes().isEmpty()); assertEquals(r.getPattern(), "/patter?"); r = new AttributesRule("patt*", ""); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertTrue(r.getAttributes().isEmpty()); assertEquals(r.getPattern(), "patt*"); r = new AttributesRule("pattern", "attribute1"); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertFalse(r.getAttributes().isEmpty()); assertEquals(r.getAttributes().size(), 1); @@ -322,28 +322,28 @@ public class AttributesMatcherTest { r = new AttributesRule("pattern", "attribute1 -attribute2"); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertEquals(r.getAttributes().size(), 2); assertEquals(r.getPattern(), "pattern"); r = new AttributesRule("pattern", "attribute1 \t-attribute2 \t"); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertEquals(r.getAttributes().size(), 2); assertEquals(r.getPattern(), "pattern"); r = new AttributesRule("pattern", "attribute1\t-attribute2\t"); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertEquals(r.getAttributes().size(), 2); assertEquals(r.getPattern(), "pattern"); r = new AttributesRule("pattern", "attribute1\t -attribute2\t "); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertEquals(r.getAttributes().size(), 2); assertEquals(r.getPattern(), "pattern"); @@ -351,7 +351,7 @@ public class AttributesMatcherTest { r = new AttributesRule("pattern", "attribute1 -attribute2 attribute3=value "); assertTrue(r.isNameOnly()); - assertFalse(r.dirOnly()); + assertFalse(r.isDirOnly()); assertNotNull(r.getAttributes()); assertEquals(r.getAttributes().size(), 3); assertEquals(r.getPattern(), "pattern"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java index 0e595e61f8..7421e907dc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java @@ -251,14 +251,17 @@ public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase { } private void assertAttributesNode(String pathName, - AttributesNode attributesNode, List<Attribute> nodeAttrs) { + AttributesNode attributesNode, List<Attribute> nodeAttrs) + throws IOException { if (attributesNode == null) assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { Attributes entryAttributes = new Attributes(); - attributesNode.getAttributes(pathName, - false, entryAttributes); + new AttributesHandler(walk).mergeAttributes(attributesNode, + pathName, + false, + entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java index d478a7cf08..ec2370e67f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java @@ -50,6 +50,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.treewalk.TreeWalk; import org.junit.After; import org.junit.Test; @@ -57,6 +60,8 @@ import org.junit.Test; * Test {@link AttributesNode} */ public class AttributesNodeTest { + private static final TreeWalk DUMMY_WALK = new TreeWalk( + new InMemoryRepository(new DfsRepositoryDescription("FooBar"))); private static final Attribute A_SET_ATTR = new Attribute("A", SET); @@ -162,9 +167,10 @@ public class AttributesNodeTest { } private void assertAttribute(String path, AttributesNode node, - Attributes attrs) { + Attributes attrs) throws IOException { Attributes attributes = new Attributes(); - node.getAttributes(path, false, attributes); + new AttributesHandler(DUMMY_WALK).mergeAttributes(node, path, false, + attributes); assertEquals(attrs, attributes); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java index 4215ba23e5..b159cca0d5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java @@ -219,14 +219,16 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase { } private void assertAttributesNode(String pathName, - AttributesNode attributesNode, List<Attribute> nodeAttrs) { + AttributesNode attributesNode, List<Attribute> nodeAttrs) + throws IOException { if (attributesNode == null) assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { Attributes entryAttributes = new Attributes(); - attributesNode.getAttributes(pathName, - false, entryAttributes); + new AttributesHandler(walk).mergeAttributes(attributesNode, + pathName, false, + entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java index 77ef1a6466..2cec34ba89 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java @@ -751,6 +751,59 @@ public class RepoCommandTest extends RepositoryTestCase { } } + @Test + public void testRemoteRevision() throws Exception { + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\".\" />") + .append("<remote name=\"remote2\" fetch=\".\" revision=\"") + .append(BRANCH) + .append("\" />") + .append("<default remote=\"remote1\" revision=\"master\" />") + .append("<project path=\"foo\" remote=\"remote2\" name=\"") + .append(defaultUri) + .append("\" />") + .append("</manifest>"); + writeTrashFile("manifest.xml", xmlContent.toString()); + RepoCommand command = new RepoCommand(db); + command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri) + .call(); + File hello = new File(db.getWorkTree(), "foo/hello.txt"); + BufferedReader reader = new BufferedReader(new FileReader(hello)); + String content = reader.readLine(); + reader.close(); + assertEquals("submodule content should be as expected", + "branch world", content); + } + + @Test + public void testDefaultRemoteRevision() throws Exception { + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\".\" revision=\"") + .append(BRANCH) + .append("\" />") + .append("<default remote=\"remote1\" />") + .append("<project path=\"foo\" name=\"") + .append(defaultUri) + .append("\" />") + .append("</manifest>"); + writeTrashFile("manifest.xml", xmlContent.toString()); + RepoCommand command = new RepoCommand(db); + command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri) + .call(); + File hello = new File(db.getWorkTree(), "foo/hello.txt"); + BufferedReader reader = new BufferedReader(new FileReader(hello)); + String content = reader.readLine(); + reader.close(); + assertEquals("submodule content should be as expected", + "branch world", content); + } + private void resolveRelativeUris() { // Find the longest common prefix ends with "/" as rootUri. defaultUri = defaultDb.getDirectory().toURI().toString(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java index f549fb5cdf..41a1a5d3f5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java @@ -213,9 +213,20 @@ public class GcBasicPackingTest extends GcTestCase { assertEquals(9, stats.numberOfPackedObjects); assertEquals(2, stats.numberOfPackFiles); + // repack again but now without a grace period for loose objects. Since + // we don't have loose objects anymore this shouldn't change anything + gc.setExpireAgeMillis(0); + gc.gc(); + stats = gc.getStatistics(); + assertEquals(0, stats.numberOfLooseObjects); + // if objects exist in multiple packFiles then they are counted multiple + // times + assertEquals(9, stats.numberOfPackedObjects); + assertEquals(2, stats.numberOfPackFiles); + // repack again but now without a grace period for packfiles. We should // end up with one packfile - gc.setExpireAgeMillis(0); + gc.setPackExpireAgeMillis(0); gc.gc(); stats = gc.getStatistics(); assertEquals(0, stats.numberOfLooseObjects); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java index 7adf074216..4be1d6d370 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java @@ -104,9 +104,14 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase { private void delete(final RefUpdate ref, final Result expected, final boolean exists, final boolean removed) throws IOException { - assertEquals(exists, db.getAllRefs().containsKey(ref.getName())); + delete(db, ref, expected, exists, removed); + } + + private void delete(Repository repo, final RefUpdate ref, final Result expected, + final boolean exists, final boolean removed) throws IOException { + assertEquals(exists, repo.getAllRefs().containsKey(ref.getName())); assertEquals(expected, ref.delete()); - assertEquals(!removed, db.getAllRefs().containsKey(ref.getName())); + assertEquals(!removed, repo.getAllRefs().containsKey(ref.getName())); } @Test @@ -232,6 +237,17 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase { assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); } + @Test + public void testDeleteHeadInBareRepo() throws IOException { + try (Repository bareRepo = createBareRepository()) { + RefUpdate ref = bareRepo.updateRef(Constants.HEAD); + ref.setNewObjectId(ObjectId.fromString("0123456789012345678901234567890123456789")); + // Create the HEAD ref so we can delete it. + assertEquals(Result.NEW, ref.update()); + ref = bareRepo.updateRef(Constants.HEAD); + delete(bareRepo, ref, Result.NO_CHANGE, true, true); + } + } /** * Delete a loose ref and make sure the directory in refs is deleted too, * and the reflog dir too diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java index 5fda070867..49e1f6f3da 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java @@ -95,7 +95,8 @@ public class GcCommitSelectionTest extends GcTestCase { } currentCommits = nextCommitCount; - gc.setExpireAgeMillis(0); // immediately delete old packs + gc.setPackExpireAgeMillis(0); // immediately delete old packs + gc.setExpireAgeMillis(0); gc.gc(); assertEquals(currentCommits * 3, // commit/tree/object gc.getStatistics().numberOfPackedObjects); @@ -158,7 +159,8 @@ public class GcCommitSelectionTest extends GcTestCase { } currentCommits = nextCommitCount; - gc.setExpireAgeMillis(0); // immediately delete old packs + gc.setPackExpireAgeMillis(0); // immediately delete old packs + gc.setExpireAgeMillis(0); gc.gc(); assertEquals(currentCommits + " commits: ", expectedBitmapCount, gc.getStatistics().numberOfBitmaps); @@ -198,7 +200,8 @@ public class GcCommitSelectionTest extends GcTestCase { final int commitsForShallowBranches = 100; // Excessive branch history pruning, one old branch. - gc.setExpireAgeMillis(0); // immediately delete old packs + gc.setPackExpireAgeMillis(0); // immediately delete old packs + gc.setExpireAgeMillis(0); gc.gc(); assertEquals( commitsForSparseBranch + commitsForFullBranch diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java new file mode 100644 index 0000000000..47f70d7bdb --- /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 020d1b1b51..e4d0f1d291 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 class RefTreeDatabaseTest { 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 class RefTreeDatabaseTest { } @Test + public void testUpdate_RefusesOrigHeadOnBare() throws IOException { + assertTrue(refdb.getRepository().isBare()); + ObjectId txnId = getTxnCommitted(); + + RefUpdate orig = refdb.newUpdate(ORIG_HEAD, true); + orig.setNewObjectId(B); + assertEquals(RefUpdate.Result.LOCK_FAILURE, orig.update()); + assertEquals(txnId, getTxnCommitted()); + + ReceiveCommand cmd = command(null, B, ORIG_HEAD); + BatchRefUpdate batch = refdb.newBatchUpdate(); + batch.addCommand(cmd); + batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE); + assertEquals(REJECTED_OTHER_REASON, cmd.getResult()); + assertEquals( + MessageFormat.format(JGitText.get().invalidRefName, ORIG_HEAD), + cmd.getMessage()); + assertEquals(txnId, getTxnCommitted()); + } + + @Test public void testBatchRefUpdate_NonFastForwardAborts() throws IOException { update("refs/heads/master", A); update("refs/heads/masters", B); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index c1b882a656..5578c03d4a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -65,6 +65,7 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEntry; @@ -113,7 +114,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { return dco.getRemoved(); } - private Map<String, String> getUpdated() { + private Map<String, CheckoutMetadata> getUpdated() { return dco.getUpdated(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java index 6c6292558e..a1cec2d914 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java @@ -173,4 +173,39 @@ public class RepositoryCacheTest extends RepositoryTestCase { assertEquals(0, RepositoryCache.getRegisteredKeys().size()); } + @Test + public void testRepositoryUsageCount() throws Exception { + FileKey loc = FileKey.exact(db.getDirectory(), db.getFS()); + Repository d2 = RepositoryCache.open(loc); + assertEquals(1, d2.useCnt.get()); + RepositoryCache.open(FileKey.exact(loc.getFile(), db.getFS())); + assertEquals(2, d2.useCnt.get()); + d2.close(); + assertEquals(1, d2.useCnt.get()); + d2.close(); + assertEquals(0, d2.useCnt.get()); + } + + @Test + public void testRepositoryUsageCountWithRegisteredRepository() { + assertEquals(1, ((Repository) db).useCnt.get()); + RepositoryCache.register(db); + assertEquals(1, ((Repository) db).useCnt.get()); + db.close(); + assertEquals(0, ((Repository) db).useCnt.get()); + } + + public void testRepositoryUnregisteringWhenClosing() throws Exception { + FileKey loc = FileKey.exact(db.getDirectory(), db.getFS()); + Repository d2 = RepositoryCache.open(loc); + assertEquals(1, d2.useCnt.get()); + assertThat(RepositoryCache.getRegisteredKeys(), + hasItem(FileKey.exact(db.getDirectory(), db.getFS()))); + assertEquals(1, RepositoryCache.getRegisteredKeys().size()); + + d2.close(); + + assertEquals(0, d2.useCnt.get()); + assertEquals(0, RepositoryCache.getRegisteredKeys().size()); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java index df17a3e2fc..bf1d0e6d23 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java @@ -62,15 +62,19 @@ import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.treewalk.WorkingTreeIterator.MetadataDiff; import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.RawParseUtils; import org.junit.Before; @@ -281,6 +285,37 @@ public class FileTreeIteratorTest extends RepositoryTestCase { } @Test + public void testTreewalkEnterSubtree() throws Exception { + try (Git git = new Git(db)) { + writeTrashFile("b/c", "b/c"); + writeTrashFile("z/.git", "gitdir: /tmp/somewhere"); + git.add().addFilepattern(".").call(); + git.rm().addFilepattern("a,").addFilepattern("a,b") + .addFilepattern("a0b").call(); + assertEquals("[a/b, mode:100644][b/c, mode:100644][z, mode:160000]", + indexState(0)); + FileUtils.delete(new File(db.getWorkTree(), "b"), + FileUtils.RECURSIVE); + + TreeWalk tw = new TreeWalk(db); + tw.addTree(new DirCacheIterator(db.readDirCache())); + tw.addTree(new FileTreeIterator(db)); + assertTrue(tw.next()); + assertEquals("a", tw.getPathString()); + tw.enterSubtree(); + tw.next(); + assertEquals("a/b", tw.getPathString()); + tw.next(); + assertEquals("b", tw.getPathString()); + tw.enterSubtree(); + tw.next(); + assertEquals("b/c", tw.getPathString()); + assertNotNull(tw.getTree(0, AbstractTreeIterator.class)); + assertNotNull(tw.getTree(EmptyTreeIterator.class)); + } + } + + @Test public void testIsModifiedSymlinkAsFile() throws Exception { writeTrashFile("symlink", "content"); try (Git git = new Git(db)) { @@ -346,7 +381,7 @@ public class FileTreeIteratorTest extends RepositoryTestCase { DirCache cache = db.lockDirCache(); DirCacheEditor editor = cache.editor(); editor.add(new PathEdit(path) { - + public void apply(DirCacheEntry ent) { ent.setFileMode(FileMode.GITLINK); ent.setObjectId(id); @@ -363,7 +398,7 @@ public class FileTreeIteratorTest extends RepositoryTestCase { walk.addTree(indexIter); walk.addTree(workTreeIter); walk.setFilter(PathFilter.create(path)); - + assertTrue(walk.next()); assertTrue(indexIter.idEqual(workTreeIter)); } @@ -537,6 +572,87 @@ public class FileTreeIteratorTest extends RepositoryTestCase { } } + private final FileTreeIterator.FileModeStrategy NO_GITLINKS_STRATEGY = + new FileTreeIterator.FileModeStrategy() { + @Override + public FileMode getMode(File f, FS.Attributes attributes) { + if (attributes.isSymbolicLink()) { + return FileMode.SYMLINK; + } else if (attributes.isDirectory()) { + // NOTE: in the production DefaultFileModeStrategy, there is + // a check here for a subdirectory called '.git', and if it + // exists, we create a GITLINK instead of recursing into the + // tree. In this custom strategy, we ignore nested git dirs + // and treat all directories the same. + return FileMode.TREE; + } else if (attributes.isExecutable()) { + return FileMode.EXECUTABLE_FILE; + } else { + return FileMode.REGULAR_FILE; + } + } + }; + + private Repository createNestedRepo() throws IOException { + File gitdir = createUniqueTestGitDir(false); + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + builder.setGitDir(gitdir); + Repository nestedRepo = builder.build(); + nestedRepo.create(); + + JGitTestUtil.writeTrashFile(nestedRepo, "sub", "a.txt", "content"); + + File nestedRepoPath = new File(nestedRepo.getWorkTree(), "sub/nested"); + FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder(); + nestedBuilder.setWorkTree(nestedRepoPath); + nestedBuilder.build().create(); + + JGitTestUtil.writeTrashFile(nestedRepo, "sub/nested", "b.txt", + "content b"); + + return nestedRepo; + } + + @Test + public void testCustomFileModeStrategy() throws Exception { + Repository nestedRepo = createNestedRepo(); + + Git git = new Git(nestedRepo); + // validate that our custom strategy is honored + WorkingTreeIterator customIterator = + new FileTreeIterator(nestedRepo, NO_GITLINKS_STRATEGY); + git.add().setWorkingTreeIterator(customIterator) + .addFilepattern(".").call(); + assertEquals( + "[sub/a.txt, mode:100644, content:content]" + + "[sub/nested/b.txt, mode:100644, content:content b]", + indexState(nestedRepo, CONTENT)); + + } + + @Test + public void testCustomFileModeStrategyFromParentIterator() throws Exception { + Repository nestedRepo = createNestedRepo(); + + Git git = new Git(nestedRepo); + + FileTreeIterator customIterator = + new FileTreeIterator(nestedRepo, NO_GITLINKS_STRATEGY); + File r = new File(nestedRepo.getWorkTree(), "sub"); + + // here we want to validate that if we create a new iterator using the + // constructor that accepts a parent iterator, that the child iterator + // correctly inherits the FileModeStrategy from the parent iterator. + FileTreeIterator childIterator = + new FileTreeIterator(customIterator, r, nestedRepo.getFS()); + git.add().setWorkingTreeIterator(childIterator).addFilepattern(".").call(); + assertEquals( + "[sub/a.txt, mode:100644, content:content]" + + "[sub/nested/b.txt, mode:100644, content:content b]", + indexState(nestedRepo, CONTENT)); + } + + private static void assertEntry(String sha1string, String path, TreeWalk tw) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java index 06cb11b37a..7e11a61035 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 class NBTest { } @Test + public void testCompareUInt64() { + assertTrue(NB.compareUInt64(0, 0) == 0); + assertTrue(NB.compareUInt64(1, 0) > 0); + assertTrue(NB.compareUInt64(0, 1) < 0); + assertTrue(NB.compareUInt64(-1, 0) > 0); + assertTrue(NB.compareUInt64(0, -1) < 0); + assertTrue(NB.compareUInt64(-1, 1) > 0); + assertTrue(NB.compareUInt64(1, -1) < 0); + } + + @Test public void testDecodeUInt16() { assertEquals(0, NB.decodeUInt16(b(0, 0), 0)); assertEquals(0, NB.decodeUInt16(padb(3, 0, 0), 3)); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java index ed2a4f2102..40cac93f3b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2010, Marc Strapetz <marc.strapetz@syntevo.com> + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -52,7 +53,7 @@ import java.io.UnsupportedEncodingException; import org.junit.Test; -public class EolCanonicalizingInputStreamTest { +public class AutoLFInputStreamTest { @Test public void testLF() throws IOException { @@ -97,7 +98,7 @@ public class EolCanonicalizingInputStreamTest { private static void test(byte[] input, byte[] expected, boolean detectBinary) throws IOException { final InputStream bis1 = new ByteArrayInputStream(input); - final InputStream cis1 = new EolCanonicalizingInputStream(bis1, detectBinary); + final InputStream cis1 = new AutoLFInputStream(bis1, detectBinary); int index1 = 0; for (int b = cis1.read(); b != -1; b = cis1.read()) { assertEquals(expected[index1], (byte) b); @@ -109,7 +110,7 @@ public class EolCanonicalizingInputStreamTest { for (int bufferSize = 1; bufferSize < 10; bufferSize++) { final byte[] buffer = new byte[bufferSize]; final InputStream bis2 = new ByteArrayInputStream(input); - final InputStream cis2 = new EolCanonicalizingInputStream(bis2, detectBinary); + final InputStream cis2 = new AutoLFInputStream(bis2, detectBinary); int read = 0; for (int readNow = cis2.read(buffer, 0, buffer.length); readNow != -1 diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index f9e1a39904..db339e073c 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF @@ -3,14 +3,14 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Vendor: %provider_name Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Export-Package: org.eclipse.jgit.awtui;version="4.2.1" -Import-Package: org.eclipse.jgit.errors;version="[4.2.1,4.3.0)", - org.eclipse.jgit.lib;version="[4.2.1,4.3.0)", - org.eclipse.jgit.nls;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revplot;version="[4.2.1,4.3.0)", - org.eclipse.jgit.revwalk;version="[4.2.1,4.3.0)", - org.eclipse.jgit.transport;version="[4.2.1,4.3.0)", - org.eclipse.jgit.util;version="[4.2.1,4.3.0)" +Export-Package: org.eclipse.jgit.awtui;version="4.3.2" +Import-Package: org.eclipse.jgit.errors;version="[4.3.2,4.4.0)", + org.eclipse.jgit.lib;version="[4.3.2,4.4.0)", + org.eclipse.jgit.nls;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revplot;version="[4.3.2,4.4.0)", + org.eclipse.jgit.revwalk;version="[4.3.2,4.4.0)", + org.eclipse.jgit.transport;version="[4.3.2,4.4.0)", + org.eclipse.jgit.util;version="[4.3.2,4.4.0)" diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index c80f31ff49..fdd6dad2df 100644 --- a/org.eclipse.jgit.ui/pom.xml +++ b/org.eclipse.jgit.ui/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ui</artifactId> diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 36041f8144..c0dbc779f3 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -1,115 +1,39 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <component id="org.eclipse.jgit" version="2"> - <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.FileTreeEntry"> - <filter id="305324134"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.FileTreeEntry"/> - <message_argument value="org.eclipse.jgit_4.2.0"/> - </message_arguments> - </filter> - </resource> - <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.GitlinkTreeEntry"> - <filter id="305324134"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.GitlinkTreeEntry"/> - <message_argument value="org.eclipse.jgit_4.2.0"/> - </message_arguments> - </filter> - </resource> - <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.SymlinkTreeEntry"> - <filter id="305324134"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.SymlinkTreeEntry"/> - <message_argument value="org.eclipse.jgit_4.2.0"/> - </message_arguments> - </filter> - </resource> - <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.Tree"> - <filter id="305324134"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.Tree"/> - <message_argument value="org.eclipse.jgit_4.2.0"/> - </message_arguments> - </filter> - </resource> - <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.TreeEntry"> - <filter id="305324134"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.TreeEntry"/> - <message_argument value="org.eclipse.jgit_4.2.0"/> - </message_arguments> - </filter> - </resource> <resource path="src/org/eclipse/jgit/attributes/AttributesNode.java" type="org.eclipse.jgit.attributes.AttributesNode"> - <filter comment="attributes weren't really usable in earlier versions" id="338792546"> + <filter comment="moved to new AttributesManager" id="338792546"> <message_arguments> <message_argument value="org.eclipse.jgit.attributes.AttributesNode"/> - <message_argument value="getAttributes(String, boolean, Map<String,Attribute>)"/> + <message_argument value="getAttributes(String, boolean, Attributes)"/> </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$BitmapBuilder"> - <filter comment="interface is implemented by extenders but not clients of the API" id="403804204"> + <resource path="src/org/eclipse/jgit/attributes/AttributesRule.java" type="org.eclipse.jgit.attributes.AttributesRule"> + <filter comment="used only in tests: bean naming" id="338792546"> <message_arguments> - <message_argument value="org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder"/> - <message_argument value="addObject(AnyObjectId, int)"/> - </message_arguments> - </filter> - <filter comment="interface is implemented by extenders but not clients of the API" id="403804204"> - <message_arguments> - <message_argument value="org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder"/> - <message_argument value="getBitmapIndex()"/> - </message_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_argument value="org.eclipse.jgit.attributes.AttributesRule"/> + <message_argument value="dirOnly()"/> </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"> + <resource path="src/org/eclipse/jgit/dircache/DirCacheCheckout.java" type="org.eclipse.jgit.dircache.DirCacheCheckout"> + <filter comment="add eol stream type conversion" id="338792546"> <message_arguments> - <message_argument value="org.eclipse.jgit.transport.PushCertificate"/> - <message_argument value="PushCertificate()"/> + <message_argument value="org.eclipse.jgit.dircache.DirCacheCheckout"/> + <message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean)"/> </message_arguments> </filter> - <filter comment="PushCertificate wasn't really usable in 4.0" id="338792546"> + <filter comment="add eol stream type conversion" id="338792546"> <message_arguments> - <message_argument value="org.eclipse.jgit.transport.PushCertificate"/> - <message_argument value="getCommandList()"/> + <message_argument value="org.eclipse.jgit.dircache.DirCacheCheckout"/> + <message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, String)"/> </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"> + <filter comment="add eol stream type conversion" id="1141899266"> <message_arguments> - <message_argument value="org.eclipse.jgit.util.FileUtils"/> - <message_argument value="createSymLink(File, String)"/> + <message_argument value="4.2"/> + <message_argument value="4.3"/> + <message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, DirCacheCheckout.CheckoutMetadata)"/> </message_arguments> </filter> </resource> diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index d0f03ff3ca..f94c22c0f5 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -2,12 +2,12 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 4.2.1.qualifier +Bundle-Version: 4.3.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.annotations;version="4.2.1", - org.eclipse.jgit.api;version="4.2.1"; +Export-Package: org.eclipse.jgit.annotations;version="4.3.2", + org.eclipse.jgit.api;version="4.3.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, @@ -21,55 +21,57 @@ Export-Package: org.eclipse.jgit.annotations;version="4.2.1", org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="4.2.1", - org.eclipse.jgit.blame;version="4.2.1"; + org.eclipse.jgit.api.errors;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", + org.eclipse.jgit.attributes;version="4.3.2", + org.eclipse.jgit.blame;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="4.2.1"; + org.eclipse.jgit.diff;version="4.3.2"; uses:="org.eclipse.jgit.patch, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="4.2.1"; + org.eclipse.jgit.dircache;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.util, org.eclipse.jgit.events, org.eclipse.jgit.attributes", - org.eclipse.jgit.errors;version="4.2.1"; + org.eclipse.jgit.errors;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.internal.storage.pack, org.eclipse.jgit.transport, org.eclipse.jgit.dircache", - org.eclipse.jgit.events;version="4.2.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="4.2.1", - org.eclipse.jgit.gitrepo;version="4.2.1"; + org.eclipse.jgit.events;version="4.3.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.fnmatch;version="4.3.2", + org.eclipse.jgit.gitrepo;version="4.3.2"; uses:="org.eclipse.jgit.api, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.xml.sax.helpers, org.xml.sax", - org.eclipse.jgit.gitrepo.internal;version="4.2.1";x-internal:=true, - org.eclipse.jgit.hooks;version="4.2.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="4.2.1", - org.eclipse.jgit.ignore.internal;version="4.2.1";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="4.2.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.storage.dfs;version="4.2.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server", - org.eclipse.jgit.internal.storage.file;version="4.2.1"; + org.eclipse.jgit.gitrepo.internal;version="4.3.2";x-internal:=true, + org.eclipse.jgit.hooks;version="4.3.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="4.3.2", + org.eclipse.jgit.ignore.internal;version="4.3.2";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal;version="4.3.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", + org.eclipse.jgit.internal.ketch;version="4.3.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.dfs;version="4.3.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server", + org.eclipse.jgit.internal.storage.file;version="4.3.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, org.eclipse.jgit.http.server, - org.eclipse.jgit.pgm.test, - org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="4.2.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftree;version="4.2.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.lib;version="4.2.1"; + org.eclipse.jgit.lfs.server, + org.eclipse.jgit.pgm, + org.eclipse.jgit.pgm.test", + org.eclipse.jgit.internal.storage.pack;version="4.3.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.reftree;version="4.3.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.lib;version="4.3.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, @@ -79,32 +81,32 @@ Export-Package: org.eclipse.jgit.annotations;version="4.2.1", org.eclipse.jgit.treewalk, org.eclipse.jgit.transport, org.eclipse.jgit.submodule", - org.eclipse.jgit.merge;version="4.2.1"; + org.eclipse.jgit.merge;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.diff, org.eclipse.jgit.dircache, org.eclipse.jgit.api", - org.eclipse.jgit.nls;version="4.2.1", - org.eclipse.jgit.notes;version="4.2.1"; + org.eclipse.jgit.nls;version="4.3.2", + org.eclipse.jgit.notes;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="4.2.1"; + org.eclipse.jgit.patch;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", + org.eclipse.jgit.revplot;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", + org.eclipse.jgit.revwalk;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, org.eclipse.jgit.revwalk.filter", - org.eclipse.jgit.revwalk.filter;version="4.2.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="4.2.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", - org.eclipse.jgit.transport;version="4.2.1"; + org.eclipse.jgit.revwalk.filter;version="4.3.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.file;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.pack;version="4.3.2";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.submodule;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", + org.eclipse.jgit.transport;version="4.3.2"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, org.eclipse.jgit.internal.storage.pack, @@ -116,22 +118,22 @@ Export-Package: org.eclipse.jgit.annotations;version="4.2.1", org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="4.2.1";uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="4.2.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", - org.eclipse.jgit.treewalk;version="4.2.1"; + org.eclipse.jgit.transport.http;version="4.3.2";uses:="javax.net.ssl", + org.eclipse.jgit.transport.resolver;version="4.3.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", + org.eclipse.jgit.treewalk;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.attributes, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, org.eclipse.jgit.dircache", - org.eclipse.jgit.treewalk.filter;version="4.2.1";uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="4.2.1"; + org.eclipse.jgit.treewalk.filter;version="4.3.2";uses:="org.eclipse.jgit.treewalk", + org.eclipse.jgit.util;version="4.3.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.transport.http, org.eclipse.jgit.storage.file, org.ietf.jgss", - org.eclipse.jgit.util.io;version="4.2.1" + org.eclipse.jgit.util.io;version="4.3.2" Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)" Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)", diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index 98ede31769..3bf02b7772 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.2.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="4.2.1.qualifier";roots="." +Bundle-Version: 4.3.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="4.3.2.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index e931990a79..40d15c87bb 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -53,7 +53,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId> diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties new file mode 100644 index 0000000000..1fbb7cb3b5 --- /dev/null +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/ketch/KetchText.properties @@ -0,0 +1,13 @@ +accepted=accepted. +cannotFetchFromLocalReplica=cannot fetch from LocalReplica +failed=failed! +invalidFollowerUri=invalid follower URI +leaderFailedToStore=leader failed to store +localReplicaRequired=LocalReplica instance is required +mismatchedTxnNamespace=mismatched txnNamespace; expected {0} found {1} +outsideTxnNamespace=ref {0} is outside of txnNamespace {1} +proposingUpdates=Proposing updates +queuedProposalFailedToApply=queued proposal failed to apply +starting=starting! +unsupportedVoterCount=unsupported voter count {0}, expected one of {1} +waitingForQueue=Waiting for queue diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java index 3b94f16f1a..1f37833a41 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.api; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.FileMode.GITLINK; +import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK; import static org.eclipse.jgit.lib.FileMode.TYPE_TREE; import java.io.IOException; @@ -201,7 +202,10 @@ public class AddCommand extends GitCommand<DirCache> { continue; } - if (f.getEntryRawMode() == TYPE_TREE) { + if ((f.getEntryRawMode() == TYPE_TREE + && f.getIndexFileMode(c) != FileMode.GITLINK) || + (f.getEntryRawMode() == TYPE_GITLINK + && f.getIndexFileMode(c) == FileMode.TREE)) { // Index entry exists and is symlink, gitlink or file, // otherwise the tree would have been entered above. // Replace the index entry by diving into tree of files. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java index 676ae03009..8fbf83954c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java @@ -201,10 +201,12 @@ public class ApplyCommand extends GitCommand<ApplyResult> { oldLines.add(rt.getString(i)); List<String> newLines = new ArrayList<String>(oldLines); for (HunkHeader hh : fh.getHunks()) { - StringBuilder hunk = new StringBuilder(); - for (int j = hh.getStartOffset(); j < hh.getEndOffset(); j++) - hunk.append((char) hh.getBuffer()[j]); - RawText hrt = new RawText(hunk.toString().getBytes()); + + byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()]; + System.arraycopy(hh.getBuffer(), hh.getStartOffset(), b, 0, + b.length); + RawText hrt = new RawText(b); + List<String> hunkLines = new ArrayList<String>(hrt.size()); for (int i = 0; i < hrt.size(); i++) hunkLines.add(hrt.getString(i)); @@ -221,12 +223,16 @@ public class ApplyCommand extends GitCommand<ApplyResult> { pos++; break; case '-': - if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals( - hunkLine.substring(1))) { - throw new PatchApplyException(MessageFormat.format( - JGitText.get().patchApplyException, hh)); + if (hh.getNewStartLine() == 0) { + newLines.clear(); + } else { + if (!newLines.get(hh.getNewStartLine() - 1 + pos) + .equals(hunkLine.substring(1))) { + throw new PatchApplyException(MessageFormat.format( + JGitText.get().patchApplyException, hh)); + } + newLines.remove(hh.getNewStartLine() - 1 + pos); } - newLines.remove(hh.getNewStartLine() - 1 + pos); break; case '+': newLines.add(hh.getNewStartLine() - 1 + pos, @@ -248,7 +254,9 @@ public class ApplyCommand extends GitCommand<ApplyResult> { // still there! sb.append(l).append('\n'); } - sb.deleteCharAt(sb.length() - 1); + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } FileWriter fw = new FileWriter(f); fw.write(sb.toString()); fw.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java index a83814eb46..d803efd649 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java @@ -66,7 +66,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.treewalk.WorkingTreeOptions; import org.eclipse.jgit.util.IO; -import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; +import org.eclipse.jgit.util.io.AutoLFInputStream; /** * Blame command for building a {@link BlameResult} for a file path. @@ -248,7 +248,7 @@ public class BlameCommand extends GitCommand<BlameResult> { rawText = new RawText(inTree); break; case TRUE: - EolCanonicalizingInputStream in = new EolCanonicalizingInputStream( + AutoLFInputStream in = new AutoLFInputStream( new FileInputStream(inTree), true); // Canonicalization should lead to same or shorter length // (CRLF to LF), so the file size on disk is an upper size bound diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index 4f918fa357..6c80289452 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -59,6 +59,7 @@ import org.eclipse.jgit.api.errors.RefAlreadyExistsException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEntry; @@ -68,6 +69,7 @@ import org.eclipse.jgit.errors.UnmergedPathException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; @@ -395,7 +397,8 @@ public class CheckoutCommand extends GitCommand<Ref> { RefNotFoundException { DirCache dc = repo.lockDirCache(); try (RevWalk revWalk = new RevWalk(repo); - TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader())) { + TreeWalk treeWalk = new TreeWalk(repo, + revWalk.getObjectReader())) { treeWalk.setRecursive(true); if (!checkoutAllPaths) treeWalk.setFilter(PathFilterGroup.createFromStrings(paths)); @@ -426,20 +429,25 @@ public class CheckoutCommand extends GitCommand<Ref> { if (path.equals(previousPath)) continue; + final EolStreamType eolStreamType = treeWalk.getEolStreamType(); + final String filterCommand = treeWalk + .getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE); editor.add(new PathEdit(path) { public void apply(DirCacheEntry ent) { int stage = ent.getStage(); if (stage > DirCacheEntry.STAGE_0) { if (checkoutStage != null) { if (stage == checkoutStage.number) - checkoutPath(ent, r); + checkoutPath(ent, r, new CheckoutMetadata( + eolStreamType, filterCommand)); } else { UnmergedPathException e = new UnmergedPathException( ent); throw new JGitInternalException(e.getMessage(), e); } } else { - checkoutPath(ent, r); + checkoutPath(ent, r, new CheckoutMetadata(eolStreamType, + filterCommand)); } } }); @@ -457,20 +465,26 @@ public class CheckoutCommand extends GitCommand<Ref> { while (treeWalk.next()) { final ObjectId blobId = treeWalk.getObjectId(0); final FileMode mode = treeWalk.getFileMode(0); + final EolStreamType eolStreamType = treeWalk.getEolStreamType(); + final String filterCommand = treeWalk + .getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE); editor.add(new PathEdit(treeWalk.getPathString()) { public void apply(DirCacheEntry ent) { ent.setObjectId(blobId); ent.setFileMode(mode); - checkoutPath(ent, r); + checkoutPath(ent, r, + new CheckoutMetadata(eolStreamType, filterCommand)); } }); } editor.commit(); } - private void checkoutPath(DirCacheEntry entry, ObjectReader reader) { + private void checkoutPath(DirCacheEntry entry, ObjectReader reader, + CheckoutMetadata checkoutMetadata) { try { - DirCacheCheckout.checkoutEntry(repo, entry, reader, true); + DirCacheCheckout.checkoutEntry(repo, entry, reader, true, + checkoutMetadata); } catch (IOException e) { throw new JGitInternalException(MessageFormat.format( JGitText.get().checkoutConflictWithFile, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java index 8ef550871f..1699b9f3d7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java @@ -54,11 +54,13 @@ import org.eclipse.jgit.api.errors.WrongRepositoryStateException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheCheckout; +import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CheckoutConflictException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; @@ -336,6 +338,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> { // Not in commit, don't create untracked continue; + final EolStreamType eolStreamType = walk.getEolStreamType(); final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath()); entry.setFileMode(cIter.getEntryFileMode()); entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset()); @@ -350,14 +353,17 @@ public class StashApplyCommand extends GitCommand<ObjectId> { } } - checkoutPath(entry, reader); + checkoutPath(entry, reader, + new CheckoutMetadata(eolStreamType, null)); } } } - private void checkoutPath(DirCacheEntry entry, ObjectReader reader) { + private void checkoutPath(DirCacheEntry entry, ObjectReader reader, + CheckoutMetadata checkoutMetadata) { try { - DirCacheCheckout.checkoutEntry(repo, entry, reader, true); + DirCacheCheckout.checkoutEntry(repo, entry, reader, true, + checkoutMetadata); } catch (IOException e) { throw new JGitInternalException(MessageFormat.format( JGitText.get().checkoutConflictWithFile, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java index 2cdaf24019..ef32ac929a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java @@ -245,12 +245,14 @@ public class StashCreateCommand extends GitCommand<RevCommit> { DirCache cache = repo.lockDirCache(); ObjectId commitId; try (ObjectInserter inserter = repo.newObjectInserter(); - TreeWalk treeWalk = new TreeWalk(reader)) { + TreeWalk treeWalk = new TreeWalk(repo, reader)) { treeWalk.setRecursive(true); treeWalk.addTree(headCommit.getTree()); treeWalk.addTree(new DirCacheIterator(cache)); treeWalk.addTree(new FileTreeIterator(repo)); + treeWalk.getTree(2, FileTreeIterator.class) + .setDirCacheIterator(treeWalk, 1); treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter( 1), new IndexDiffFilter(1, 2))); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java new file mode 100644 index 0000000000..19e4afdf92 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesHandler.java @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.attributes; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.attributes.Attribute.State; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.treewalk.AbstractTreeIterator; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; + +/** + * The attributes handler knows how to retrieve, parse and merge attributes from + * the various gitattributes files. Furthermore it collects and expands macro + * expressions. The method {@link #getAttributes()} yields the ready processed + * attributes for the current path represented by the {@link TreeWalk} + * <p> + * The implementation is based on the specifications in + * http://git-scm.com/docs/gitattributes + * + * @since 4.3 + */ +public class AttributesHandler { + private static final String MACRO_PREFIX = "[attr]"; //$NON-NLS-1$ + + private static final String BINARY_RULE_KEY = "binary"; //$NON-NLS-1$ + + /** + * This is the default <b>binary</b> rule that is present in any git folder + * <code>[attr]binary -diff -merge -text</code> + */ + private static final List<Attribute> BINARY_RULE_ATTRIBUTES = new AttributesRule( + MACRO_PREFIX + BINARY_RULE_KEY, "-diff -merge -text") //$NON-NLS-1$ + .getAttributes(); + + private final TreeWalk treeWalk; + + private final AttributesNode globalNode; + + private final AttributesNode infoNode; + + private final Map<String, List<Attribute>> expansions = new HashMap<>(); + + /** + * Create an {@link AttributesHandler} with default rules as well as merged + * rules from global, info and worktree root attributes + * + * @param treeWalk + * @throws IOException + */ + public AttributesHandler(TreeWalk treeWalk) throws IOException { + this.treeWalk = treeWalk; + AttributesNodeProvider attributesNodeProvider =treeWalk.getAttributesNodeProvider(); + this.globalNode = attributesNodeProvider != null + ? attributesNodeProvider.getGlobalAttributesNode() : null; + this.infoNode = attributesNodeProvider != null + ? attributesNodeProvider.getInfoAttributesNode() : null; + + AttributesNode rootNode = attributesNode(treeWalk, + rootOf( + treeWalk.getTree(WorkingTreeIterator.class)), + rootOf( + treeWalk.getTree(DirCacheIterator.class)), + rootOf(treeWalk + .getTree(CanonicalTreeParser.class))); + + expansions.put(BINARY_RULE_KEY, BINARY_RULE_ATTRIBUTES); + for (AttributesNode node : new AttributesNode[] { globalNode, rootNode, + infoNode }) { + if (node == null) { + continue; + } + for (AttributesRule rule : node.getRules()) { + if (rule.getPattern().startsWith(MACRO_PREFIX)) { + expansions.put(rule.getPattern() + .substring(MACRO_PREFIX.length()).trim(), + rule.getAttributes()); + } + } + } + } + + /** + * see {@link TreeWalk#getAttributes()} + * + * @return the {@link Attributes} for the current path represented by the + * {@link TreeWalk} + * @throws IOException + */ + public Attributes getAttributes() throws IOException { + String entryPath = treeWalk.getPathString(); + boolean isDirectory = (treeWalk.getFileMode() == FileMode.TREE); + Attributes attributes = new Attributes(); + + // Gets the info attributes + mergeInfoAttributes(entryPath, isDirectory, attributes); + + // Gets the attributes located on the current entry path + mergePerDirectoryEntryAttributes(entryPath, isDirectory, + treeWalk.getTree(WorkingTreeIterator.class), + treeWalk.getTree(DirCacheIterator.class), + treeWalk.getTree(CanonicalTreeParser.class), + attributes); + + // Gets the attributes located in the global attribute file + mergeGlobalAttributes(entryPath, isDirectory, attributes); + + // now after all attributes are collected - in the correct hierarchy + // order - remove all unspecified entries (the ! marker) + for (Attribute a : attributes.getAll()) { + if (a.getState() == State.UNSPECIFIED) + attributes.remove(a.getKey()); + } + + return attributes; + } + + /** + * Merges the matching GLOBAL attributes for an entry path. + * + * @param entryPath + * the path to test. The path must be relative to this attribute + * node's own repository path, and in repository path format + * (uses '/' and not '\'). + * @param isDirectory + * true if the target item is a directory. + * @param result + * that will hold the attributes matching this entry path. This + * method will NOT override any existing entry in attributes. + */ + private void mergeGlobalAttributes(String entryPath, boolean isDirectory, + Attributes result) { + mergeAttributes(globalNode, entryPath, isDirectory, result); + } + + /** + * Merges the matching INFO attributes for an entry path. + * + * @param entryPath + * the path to test. The path must be relative to this attribute + * node's own repository path, and in repository path format + * (uses '/' and not '\'). + * @param isDirectory + * true if the target item is a directory. + * @param result + * that will hold the attributes matching this entry path. This + * method will NOT override any existing entry in attributes. + */ + private void mergeInfoAttributes(String entryPath, boolean isDirectory, + Attributes result) { + mergeAttributes(infoNode, entryPath, isDirectory, result); + } + + /** + * Merges the matching working directory attributes for an entry path. + * + * @param entryPath + * the path to test. The path must be relative to this attribute + * node's own repository path, and in repository path format + * (uses '/' and not '\'). + * @param isDirectory + * true if the target item is a directory. + * @param workingTreeIterator + * @param dirCacheIterator + * @param otherTree + * @param result + * that will hold the attributes matching this entry path. This + * method will NOT override any existing entry in attributes. + * @throws IOException + */ + private void mergePerDirectoryEntryAttributes(String entryPath, + boolean isDirectory, + @Nullable WorkingTreeIterator workingTreeIterator, + @Nullable DirCacheIterator dirCacheIterator, + @Nullable CanonicalTreeParser otherTree, Attributes result) + throws IOException { + // Prevents infinite recurrence + if (workingTreeIterator != null || dirCacheIterator != null + || otherTree != null) { + AttributesNode attributesNode = attributesNode( + treeWalk, workingTreeIterator, dirCacheIterator, otherTree); + if (attributesNode != null) { + mergeAttributes(attributesNode, entryPath, isDirectory, result); + } + mergePerDirectoryEntryAttributes(entryPath, isDirectory, + parentOf(workingTreeIterator), parentOf(dirCacheIterator), + parentOf(otherTree), result); + } + } + + /** + * Merges the matching node attributes for an entry path. + * + * @param node + * the node to scan for matches to entryPath + * @param entryPath + * the path to test. The path must be relative to this attribute + * node's own repository path, and in repository path format + * (uses '/' and not '\'). + * @param isDirectory + * true if the target item is a directory. + * @param result + * that will hold the attributes matching this entry path. This + * method will NOT override any existing entry in attributes. + */ + protected void mergeAttributes(@Nullable AttributesNode node, + String entryPath, + boolean isDirectory, Attributes result) { + if (node == null) + return; + List<AttributesRule> rules = node.getRules(); + // Parse rules in the reverse order that they were read since the last + // entry should be used + ListIterator<AttributesRule> ruleIterator = rules + .listIterator(rules.size()); + while (ruleIterator.hasPrevious()) { + AttributesRule rule = ruleIterator.previous(); + if (rule.isMatch(entryPath, isDirectory)) { + ListIterator<Attribute> attributeIte = rule.getAttributes() + .listIterator(rule.getAttributes().size()); + // Parses the attributes in the reverse order that they were + // read since the last entry should be used + while (attributeIte.hasPrevious()) { + expandMacro(attributeIte.previous(), result); + } + } + } + } + + /** + * @param attr + * @param result + * contains the (recursive) expanded and merged macro attributes + * including the attribute iself + */ + protected void expandMacro(Attribute attr, Attributes result) { + // loop detection = exists check + if (result.containsKey(attr.getKey())) + return; + + // also add macro to result set, same does native git + result.put(attr); + + List<Attribute> expansion = expansions.get(attr.getKey()); + if (expansion == null) { + return; + } + switch (attr.getState()) { + case UNSET: { + for (Attribute e : expansion) { + switch (e.getState()) { + case SET: + expandMacro(new Attribute(e.getKey(), State.UNSET), result); + break; + case UNSET: + expandMacro(new Attribute(e.getKey(), State.SET), result); + break; + case UNSPECIFIED: + expandMacro(new Attribute(e.getKey(), State.UNSPECIFIED), + result); + break; + case CUSTOM: + default: + expandMacro(e, result); + } + } + break; + } + case CUSTOM: { + for (Attribute e : expansion) { + switch (e.getState()) { + case SET: + case UNSET: + case UNSPECIFIED: + expandMacro(e, result); + break; + case CUSTOM: + default: + expandMacro(new Attribute(e.getKey(), attr.getValue()), + result); + } + } + break; + } + case UNSPECIFIED: { + for (Attribute e : expansion) { + expandMacro(new Attribute(e.getKey(), State.UNSPECIFIED), + result); + } + break; + } + case SET: + default: + for (Attribute e : expansion) { + expandMacro(e, result); + } + break; + } + } + + /** + * Get the {@link AttributesNode} for the current entry. + * <p> + * This method implements the fallback mechanism between the index and the + * working tree depending on the operation type + * </p> + * + * @param treeWalk + * @param workingTreeIterator + * @param dirCacheIterator + * @param otherTree + * @return a {@link AttributesNode} of the current entry, + * {@link NullPointerException} otherwise. + * @throws IOException + * It raises an {@link IOException} if a problem appears while + * parsing one on the attributes file. + */ + private static AttributesNode attributesNode(TreeWalk treeWalk, + @Nullable WorkingTreeIterator workingTreeIterator, + @Nullable DirCacheIterator dirCacheIterator, + @Nullable CanonicalTreeParser otherTree) throws IOException { + AttributesNode attributesNode = null; + switch (treeWalk.getOperationType()) { + case CHECKIN_OP: + if (workingTreeIterator != null) { + attributesNode = workingTreeIterator.getEntryAttributesNode(); + } + if (attributesNode == null && dirCacheIterator != null) { + attributesNode = dirCacheIterator + .getEntryAttributesNode(treeWalk.getObjectReader()); + } + if (attributesNode == null && otherTree != null) { + attributesNode = otherTree + .getEntryAttributesNode(treeWalk.getObjectReader()); + } + break; + case CHECKOUT_OP: + if (otherTree != null) { + attributesNode = otherTree + .getEntryAttributesNode(treeWalk.getObjectReader()); + } + if (attributesNode == null && dirCacheIterator != null) { + attributesNode = dirCacheIterator + .getEntryAttributesNode(treeWalk.getObjectReader()); + } + if (attributesNode == null && workingTreeIterator != null) { + attributesNode = workingTreeIterator.getEntryAttributesNode(); + } + break; + default: + throw new IllegalStateException( + "The only supported operation types are:" //$NON-NLS-1$ + + OperationType.CHECKIN_OP + "," //$NON-NLS-1$ + + OperationType.CHECKOUT_OP); + } + + return attributesNode; + } + + private static <T extends AbstractTreeIterator> T parentOf(@Nullable T node) { + if(node==null) return null; + @SuppressWarnings("unchecked") + Class<T> type = (Class<T>) node.getClass(); + AbstractTreeIterator parent = node.parent; + if (type.isInstance(parent)) { + return type.cast(parent); + } + return null; + } + + private static <T extends AbstractTreeIterator> T rootOf( + @Nullable T node) { + if(node==null) return null; + AbstractTreeIterator t=node; + while (t!= null && t.parent != null) { + t= t.parent; + } + @SuppressWarnings("unchecked") + Class<T> type = (Class<T>) node.getClass(); + if (type.isInstance(t)) { + return type.cast(t); + } + return null; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java index 5c0aba2e0e..7196502112 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java @@ -49,7 +49,6 @@ import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.ListIterator; import org.eclipse.jgit.lib.Constants; @@ -122,40 +121,4 @@ public class AttributesNode { return Collections.unmodifiableList(rules); } - /** - * Returns the matching attributes for an entry path. - * - * @param entryPath - * the path to test. The path must be relative to this attribute - * node's own repository path, and in repository path format - * (uses '/' and not '\'). - * @param isDirectory - * true if the target item is a directory. - * @param attributes - * Map that will hold the attributes matching this entry path. If - * it is not empty, this method will NOT override any existing - * entry. - * @since 4.2 - */ - public void getAttributes(String entryPath, - boolean isDirectory, Attributes attributes) { - // Parse rules in the reverse order that they were read since the last - // entry should be used - ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules - .size()); - while (ruleIterator.hasPrevious()) { - AttributesRule rule = ruleIterator.previous(); - if (rule.isMatch(entryPath, isDirectory)) { - ListIterator<Attribute> attributeIte = rule.getAttributes() - .listIterator(rule.getAttributes().size()); - // Parses the attributes in the reverse order that they were - // read since the last entry should be used - while (attributeIte.hasPrevious()) { - Attribute attr = attributeIte.previous(); - if (!attributes.containsKey(attr.getKey())) - attributes.put(attr); - } - } - } - } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java index 35d18c4b2a..0532250f9c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java @@ -109,10 +109,11 @@ public class AttributesRule { private final String pattern; private final List<Attribute> attributes; - private boolean nameOnly; - private boolean dirOnly; + private final boolean nameOnly; - private IMatcher matcher; + private final boolean dirOnly; + + private final IMatcher matcher; /** * Create a new attribute rule with the given pattern. Assumes that the @@ -128,38 +129,43 @@ public class AttributesRule { */ public AttributesRule(String pattern, String attributes) { this.attributes = parseAttributes(attributes); - nameOnly = false; - dirOnly = false; if (pattern.endsWith("/")) { //$NON-NLS-1$ pattern = pattern.substring(0, pattern.length() - 1); dirOnly = true; + } else { + dirOnly = false; } - boolean hasSlash = pattern.contains("/"); //$NON-NLS-1$ + int slashIndex = pattern.indexOf('/'); - if (!hasSlash) + if (slashIndex < 0) { nameOnly = true; - else if (!pattern.startsWith("/")) { //$NON-NLS-1$ + } else if (slashIndex == 0) { + nameOnly = false; + } else { + nameOnly = false; // Contains "/" but does not start with one // Adding / to the start should not interfere with matching pattern = "/" + pattern; //$NON-NLS-1$ } + IMatcher candidateMatcher = NO_MATCH; try { - matcher = PathMatcher.createPathMatcher(pattern, + candidateMatcher = PathMatcher.createPathMatcher(pattern, Character.valueOf(FastIgnoreRule.PATH_SEPARATOR), dirOnly); } catch (InvalidPatternException e) { - matcher = NO_MATCH; + // ignore: invalid patterns are silently ignored } - + this.matcher = candidateMatcher; this.pattern = pattern; } /** * @return True if the pattern should match directories only + * @since 4.3 */ - public boolean dirOnly() { + public boolean isDirOnly() { return dirOnly; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index a1e1d15ac6..fc4cc90937 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -62,6 +62,7 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.NullProgressMonitor; @@ -84,16 +85,43 @@ import org.eclipse.jgit.util.FS.ExecutionResult; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.SystemReader; -import org.eclipse.jgit.util.io.AutoCRLFOutputStream; +import org.eclipse.jgit.util.io.EolStreamTypeUtil; /** * This class handles checking out one or two trees merging with the index. */ public class DirCacheCheckout { private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024; + + /** + * Metadata used in checkout process + * + * @since 4.3 + */ + public static class CheckoutMetadata { + /** git attributes */ + public final EolStreamType eolStreamType; + + /** filter command to apply */ + public final String smudgeFilterCommand; + + /** + * @param eolStreamType + * @param smudgeFilterCommand + */ + public CheckoutMetadata(EolStreamType eolStreamType, + String smudgeFilterCommand) { + this.eolStreamType = eolStreamType; + this.smudgeFilterCommand = smudgeFilterCommand; + } + + static CheckoutMetadata EMPTY = new CheckoutMetadata( + EolStreamType.DIRECT, null); + } + private Repository repo; - private HashMap<String, String> updated = new HashMap<String, String>(); + private HashMap<String, CheckoutMetadata> updated = new HashMap<String, CheckoutMetadata>(); private ArrayList<String> conflicts = new ArrayList<String>(); @@ -120,7 +148,7 @@ public class DirCacheCheckout { /** * @return a list of updated paths and smudgeFilterCommands */ - public Map<String, String> getUpdated() { + public Map<String, CheckoutMetadata> getUpdated() { return updated; } @@ -252,8 +280,9 @@ public class DirCacheCheckout { addTree(walk, headCommitTree); addTree(walk, mergeCommitTree); - walk.addTree(new DirCacheBuildIterator(builder)); + int dciPos = walk.addTree(new DirCacheBuildIterator(builder)); walk.addTree(workingTree); + workingTree.setDirCacheIterator(walk, dciPos); while (walk.next()) { processEntry(walk.getTree(0, CanonicalTreeParser.class), @@ -292,8 +321,9 @@ public class DirCacheCheckout { walk = new NameConflictTreeWalk(repo); addTree(walk, mergeCommitTree); - walk.addTree(new DirCacheBuildIterator(builder)); + int dciPos = walk.addTree(new DirCacheBuildIterator(builder)); walk.addTree(workingTree); + workingTree.setDirCacheIterator(walk, dciPos); while (walk.next()) { processEntry(walk.getTree(0, CanonicalTreeParser.class), @@ -450,11 +480,12 @@ public class DirCacheCheckout { if (file != null) removeEmptyParents(file); - for (String path : updated.keySet()) { + for (Map.Entry<String, CheckoutMetadata> e : updated.entrySet()) { + String path = e.getKey(); + CheckoutMetadata meta = e.getValue(); DirCacheEntry entry = dc.getEntry(path); if (!FileMode.GITLINK.equals(entry.getRawMode())) - checkoutEntry(repo, entry, objectReader, false, - updated.get(path)); + checkoutEntry(repo, entry, objectReader, false, meta); } // commit the index builder - a new index is persisted @@ -1006,8 +1037,8 @@ public class DirCacheCheckout { private void update(String path, ObjectId mId, FileMode mode) throws IOException { if (!FileMode.TREE.equals(mode)) { - updated.put(path, - walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)); + updated.put(path, new CheckoutMetadata(walk.getEolStreamType(), + walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE))); DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0); entry.setObjectId(mId); @@ -1064,8 +1095,10 @@ public class DirCacheCheckout { private boolean isModifiedSubtree_IndexWorkingtree(String path) throws CorruptObjectException, IOException { try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) { - tw.addTree(new DirCacheIterator(dc)); - tw.addTree(new FileTreeIterator(repo)); + int dciPos = tw.addTree(new DirCacheIterator(dc)); + FileTreeIterator fti = new FileTreeIterator(repo); + tw.addTree(fti); + fti.setDirCacheIterator(tw, dciPos); tw.setRecursive(true); tw.setFilter(PathFilter.create(path)); DirCacheIterator dcIt; @@ -1190,52 +1223,22 @@ public class DirCacheCheckout { * @param deleteRecursive * true to recursively delete final path if it exists on the file * system - * - * @throws IOException - * @since 4.2 - */ - public static void checkoutEntry(Repository repo, DirCacheEntry entry, - ObjectReader or, boolean deleteRecursive) throws IOException { - checkoutEntry(repo, entry, or, deleteRecursive, null); - } - - /** - * Updates the file in the working tree with content and mode from an entry - * in the index. The new content is first written to a new temporary file in - * the same directory as the real file. Then that new file is renamed to the - * final filename. - * - * <p> - * <b>Note:</b> if the entry path on local file system exists as a file, it - * will be deleted and if it exists as a directory, it will be deleted - * recursively, independently if has any content. - * </p> - * - * <p> - * TODO: this method works directly on File IO, we may need another - * abstraction (like WorkingTreeIterator). This way we could tell e.g. - * Eclipse that Files in the workspace got changed - * </p> - * - * @param repo - * repository managing the destination work tree. - * @param entry - * the entry containing new mode and content - * @param or - * object reader to use for checkout - * @param deleteRecursive - * true to recursively delete final path if it exists on the file - * system - * @param smudgeFilterCommand - * the filter command to be run for smudging the entry to be - * checked out + * @param checkoutMetadata + * containing + * <ul> + * <li>smudgeFilterCommand to be run for smudging the entry to be + * checked out</li> + * <li>eolStreamType used for stream conversion</li> + * </ul> * * @throws IOException * @since 4.2 */ public static void checkoutEntry(Repository repo, DirCacheEntry entry, ObjectReader or, boolean deleteRecursive, - String smudgeFilterCommand) throws IOException { + CheckoutMetadata checkoutMetadata) throws IOException { + if (checkoutMetadata == null) + checkoutMetadata = CheckoutMetadata.EMPTY; ObjectLoader ol = or.open(entry.getObjectId()); File f = new File(repo.getWorkTree(), entry.getPathString()); File parentDir = f.getParentFile(); @@ -1257,12 +1260,19 @@ public class DirCacheCheckout { File tmpFile = File.createTempFile( "._" + f.getName(), null, parentDir); //$NON-NLS-1$ - OutputStream channel = new FileOutputStream(tmpFile); - if (opt.getAutoCRLF() == AutoCRLF.TRUE) - channel = new AutoCRLFOutputStream(channel); - if (smudgeFilterCommand != null) { - ProcessBuilder filterProcessBuilder = fs - .runInShell(smudgeFilterCommand, new String[0]); + EolStreamType nonNullEolStreamType; + if (checkoutMetadata.eolStreamType != null) { + nonNullEolStreamType = checkoutMetadata.eolStreamType; + } else if (opt.getAutoCRLF() == AutoCRLF.TRUE) { + nonNullEolStreamType = EolStreamType.AUTO_CRLF; + } else { + nonNullEolStreamType = EolStreamType.DIRECT; + } + OutputStream channel = EolStreamTypeUtil.wrapOutputStream( + new FileOutputStream(tmpFile), nonNullEolStreamType); + if (checkoutMetadata.smudgeFilterCommand != null) { + ProcessBuilder filterProcessBuilder = fs.runInShell( + checkoutMetadata.smudgeFilterCommand, new String[0]); filterProcessBuilder.directory(repo.getWorkTree()); filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, repo.getDirectory().getAbsolutePath()); @@ -1278,14 +1288,16 @@ public class DirCacheCheckout { } } catch (IOException | InterruptedException e) { throw new IOException(new FilterFailedException(e, - smudgeFilterCommand, entry.getPathString())); + checkoutMetadata.smudgeFilterCommand, + entry.getPathString())); } finally { channel.close(); } if (rc != 0) { throw new IOException(new FilterFailedException(rc, - smudgeFilterCommand, entry.getPathString(), + checkoutMetadata.smudgeFilterCommand, + entry.getPathString(), result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE), RawParseUtils.decode(result.getStderr() .toByteArray(MAX_EXCEPTION_TEXT_SIZE)))); @@ -1301,10 +1313,11 @@ public class DirCacheCheckout { // was filtered (either by autocrlf handling or smudge filters) ask the // filesystem again for the length. Otherwise the objectloader knows the // size - if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) { - entry.setLength(tmpFile.length()); - } else { + if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT + && checkoutMetadata.smudgeFilterCommand == null) { entry.setLength(ol.getSize()); + } else { + entry.setLength(tmpFile.length()); } if (opt.isFileMode() && fs.supportsExecute()) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java index 0cbb83d6e2..8bcf4bf3e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java @@ -249,7 +249,15 @@ public class DirCacheTree { return children[i]; } - ObjectId getObjectId() { + /** + * Get the tree's ObjectId. + * <p> + * If {@link #isValid()} returns false this method will return null. + * + * @return ObjectId of this tree or null. + * @since 4.3 + */ + public ObjectId getObjectId() { return id; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java index 7eb955006e..796b422bb2 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 class ManifestParser extends DefaultHandler { } } - remotes = new HashMap<String, String>(); + remotes = new HashMap<String, Remote>(); projects = new ArrayList<RepoProject>(); filteredProjects = new ArrayList<RepoProject>(); } @@ -195,14 +195,14 @@ public class ManifestParser extends DefaultHandler { } 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 class ManifestParser extends DefaultHandler { } 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 class ManifestParser extends DefaultHandler { 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 @@ public class ManifestParser extends DefaultHandler { } return false; } + + private static class Remote { + final String fetch; + final String revision; + + Remote(String fetch, String revision) { + this.fetch = fetch; + this.revision = revision; + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java index ff9f233aa5..ee937f585b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -54,6 +54,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.SubmoduleAddCommand; @@ -103,7 +104,6 @@ import org.eclipse.jgit.util.FileUtils; * @since 3.4 */ public class RepoCommand extends GitCommand<RevCommit> { - private String path; private String uri; private String groups; @@ -114,6 +114,7 @@ public class RepoCommand extends GitCommand<RevCommit> { private RemoteReader callback; private InputStream inputStream; private IncludedFileReader includedReader; + private boolean ignoreRemoteFailures = false; private List<RepoProject> bareProjects; private Git git; @@ -137,9 +138,11 @@ public class RepoCommand extends GitCommand<RevCommit> { * The URI of the remote repository * @param ref * The ref (branch/tag/etc.) to read - * @return the sha1 of the remote repository + * @return the sha1 of the remote repository, or null if the ref does + * not exist. * @throws GitAPIException */ + @Nullable public ObjectId sha1(String uri, String ref) throws GitAPIException; /** @@ -318,7 +321,7 @@ public class RepoCommand extends GitCommand<RevCommit> { } /** - * Set whether the branch name should be recorded in .gitmodules + * Set whether the branch name should be recorded in .gitmodules. * <p> * Submodule entries in .gitmodules can include a "branch" field * to indicate what remote branch each submodule tracks. @@ -355,6 +358,26 @@ public class RepoCommand extends GitCommand<RevCommit> { } /** + * Set whether to skip projects whose commits don't exist remotely. + * <p> + * When set to true, we'll just skip the manifest entry and continue + * on to the next one. + * <p> + * When set to false (default), we'll throw an error when remote + * failures occur. + * <p> + * Not implemented for non-bare repositories. + * + * @param ignore Whether to ignore the remote failures. + * @return this command + * @since 4.3 + */ + public RepoCommand setIgnoreRemoteFailures(boolean ignore) { + this.ignoreRemoteFailures = ignore; + return this; + } + + /** * Set the author/committer for the bare repository commit. * <p> * For non-bare repositories, the current user will be used and this will be @@ -452,22 +475,29 @@ public class RepoCommand extends GitCommand<RevCommit> { for (RepoProject proj : bareProjects) { String name = proj.getPath(); String nameUri = proj.getName(); - cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$ - cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$ - // create gitlink - DirCacheEntry dcEntry = new DirCacheEntry(name); ObjectId objectId; - if (ObjectId.isId(proj.getRevision())) { + if (ObjectId.isId(proj.getRevision()) + && !ignoreRemoteFailures) { objectId = ObjectId.fromString(proj.getRevision()); } else { objectId = callback.sha1(nameUri, proj.getRevision()); - if (recordRemoteBranch) + if (objectId == null) { + if (ignoreRemoteFailures) { + continue; + } + throw new RemoteUnavailableException(nameUri); + } + if (recordRemoteBranch) { // can be branch or tag cfg.setString("submodule", name, "branch", //$NON-NLS-1$ //$NON-NLS-2$ proj.getRevision()); + } } - if (objectId == null) - throw new RemoteUnavailableException(nameUri); + cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$ + cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$ + + // create gitlink + DirCacheEntry dcEntry = new DirCacheEntry(name); dcEntry.setObjectId(objectId); dcEntry.setFileMode(FileMode.GITLINK); builder.add(dcEntry); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ElectionRound.java new file mode 100644 index 0000000000..014eab2b45 --- /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 0000000000..171c059db1 --- /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 0000000000..3bcd6bcb24 --- /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 0000000000..ba033c1a42 --- /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 0000000000..1b4307f3fb --- /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 0000000000..a30bbb260a --- /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 0000000000..71e872e3fa --- /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 0000000000..b6c3bc92c5 --- /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 0000000000..35327ea0b3 --- /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 0000000000..28a49df97a --- /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 0000000000..e297bca45e --- /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 0000000000..350c8ed62e --- /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 0000000000..0876eb5dbd --- /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 0000000000..d34477ab26 --- /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 0000000000..6f4a178673 --- /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 0000000000..e16e63aa7e --- /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 0000000000..201d9e9743 --- /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 0000000000..691b1424f4 --- /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 0000000000..8c3de027d2 --- /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 0000000000..1335b85cca --- /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 0000000000..61871a494f --- /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 0000000000..dfe03752ca --- /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 2e170a5d0f..ecd4b23c25 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 @@ public final class DfsBlockCache { 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 784507d88c..33be3b15a8 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 class DfsGarbageCollector { 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 @@ public class DfsGarbageCollector { } } + 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 2ce0d47348..9cdb753221 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -111,6 +111,8 @@ import org.eclipse.jgit.util.SystemReader; public class GC { private static final String PRUNE_EXPIRE_DEFAULT = "2.weeks.ago"; //$NON-NLS-1$ + private static final String PRUNE_PACK_EXPIRE_DEFAULT = "1.hour.ago"; //$NON-NLS-1$ + private final FileRepository repo; private ProgressMonitor pm; @@ -119,6 +121,10 @@ public class GC { private Date expire; + private long packExpireAgeMillis = -1; + + private Date packExpire; + private PackConfig pconfig = null; /** @@ -186,7 +192,7 @@ public class GC { */ private void deleteOldPacks(Collection<PackFile> oldPacks, Collection<PackFile> newPacks) throws ParseException { - long expireDate = getExpireDate(); + long packExpireDate = getPackExpireDate(); oldPackLoop: for (PackFile oldPack : oldPacks) { String oldName = oldPack.getPackName(); // check whether an old pack file is also among the list of new @@ -196,7 +202,7 @@ public class GC { continue oldPackLoop; if (!oldPack.shouldBeKept() - && oldPack.getPackFile().lastModified() < expireDate) { + && oldPack.getPackFile().lastModified() < packExpireDate) { oldPack.close(); prunePack(oldName); } @@ -449,6 +455,26 @@ public class GC { return expireDate; } + private long getPackExpireDate() throws ParseException { + long packExpireDate = Long.MAX_VALUE; + + if (packExpire == null && packExpireAgeMillis == -1) { + String prunePackExpireStr = repo.getConfig().getString( + ConfigConstants.CONFIG_GC_SECTION, null, + ConfigConstants.CONFIG_KEY_PRUNEPACKEXPIRE); + if (prunePackExpireStr == null) + prunePackExpireStr = PRUNE_PACK_EXPIRE_DEFAULT; + packExpire = GitDateParser.parse(prunePackExpireStr, null, + SystemReader.getInstance().getLocale()); + packExpireAgeMillis = -1; + } + if (packExpire != null) + packExpireDate = packExpire.getTime(); + if (packExpireAgeMillis != -1) + packExpireDate = System.currentTimeMillis() - packExpireAgeMillis; + return packExpireDate; + } + /** * Remove all entries from a map which key is the id of an object referenced * by the given ObjectWalk @@ -629,15 +655,16 @@ public class GC { } /** - * Returns a map of all refs and additional refs (e.g. FETCH_HEAD, + * Returns a collection of all refs and additional refs (e.g. FETCH_HEAD, * MERGE_HEAD, ...) * - * @return a map where names of refs point to ref objects + * @return a collection of refs pointing to live objects. * @throws IOException */ private Collection<Ref> getAllRefs() throws IOException { - Collection<Ref> refs = RefTreeNames.allRefs(repo.getRefDatabase()); - List<Ref> addl = repo.getRefDatabase().getAdditionalRefs(); + RefDatabase refdb = repo.getRefDatabase(); + Collection<Ref> refs = refdb.getRefs(RefDatabase.ALL).values(); + List<Ref> addl = refdb.getAdditionalRefs(); if (!addl.isEmpty()) { List<Ref> all = new ArrayList<>(refs.size() + addl.size()); all.addAll(refs); @@ -982,6 +1009,20 @@ public class GC { } /** + * During gc() or prune() packfiles which are created or modified in the + * last <code>packExpireAgeMillis</code> milliseconds will not be deleted. + * Only older packfiles may be deleted. If set to 0 then every packfile is a + * candidate for deletion. + * + * @param packExpireAgeMillis + * minimal age of packfiles to be deleted in milliseconds. + */ + public void setPackExpireAgeMillis(long packExpireAgeMillis) { + this.packExpireAgeMillis = packExpireAgeMillis; + expire = null; + } + + /** * Set the PackConfig used when (re-)writing packfiles. This allows to * influence how packs are written and to implement something similar to * "git gc --aggressive" @@ -1010,4 +1051,18 @@ public class GC { this.expire = expire; expireAgeMillis = -1; } + + /** + * During gc() or prune() packfiles which are created or modified after or + * at <code>packExpire</code> will not be deleted. Only older packfiles may + * be deleted. If set to null then every packfile is a candidate for + * deletion. + * + * @param packExpire + * instant in time which defines packfile expiration + */ + public void setPackExpire(Date packExpire) { + this.packExpire = packExpire; + packExpireAgeMillis = -1; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index b8c2fb4a51..4bb2982b48 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -203,10 +203,10 @@ public class RefDirectory extends RefDatabase { @Override public void close() { - // We have no resources to close. + clearReferences(); } - void rescan() { + private void clearReferences() { looseRefs.set(RefList.<LooseRef> emptyList()); packedRefs.set(PackedRefList.NO_PACKED_REFS); } @@ -214,7 +214,7 @@ public class RefDirectory extends RefDatabase { @Override public void refresh() { super.refresh(); - rescan(); + clearReferences(); } @Override @@ -312,11 +312,10 @@ public class RefDirectory extends RefDatabase { @Override public Map<String, Ref> getRefs(String prefix) throws IOException { - final RefList<Ref> packed = getPackedRefs(); final RefList<LooseRef> oldLoose = looseRefs.get(); - LooseScanner scan = new LooseScanner(oldLoose); scan.scan(prefix); + final RefList<Ref> packed = getPackedRefs(); RefList<LooseRef> loose; if (scan.newLoose != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java index dc60311102..df93ce88af 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 class RefTreeDatabase extends RefDatabase { @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 @@ public class RefTreeDatabase extends RefDatabase { @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 class RefTreeDatabase extends RefDatabase { @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 @@ public class RefTreeDatabase extends RefDatabase { 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 239a745277..c53d6deb21 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 class RefTreeNames { return false; } - /** - * Snapshot all references from a RefTreeDatabase and its bootstrap. - * <p> - * There may be name conflicts with multiple {@link Ref} objects containing - * the same name in the returned collection. - * - * @param refdb - * database instance. - * @return all known references. - * @throws IOException - * references cannot be enumerated. - */ - public static Collection<Ref> allRefs(RefDatabase refdb) - throws IOException { - Collection<Ref> refs = refdb.getRefs(ALL).values(); - if (!(refdb instanceof RefTreeDatabase)) { - return refs; - } - - RefDatabase bootstrap = ((RefTreeDatabase) refdb).getBootstrap(); - Collection<Ref> br = bootstrap.getRefs(ALL).values(); - List<Ref> all = new ArrayList<>(refs.size() + br.size()); - all.addAll(refs); - all.addAll(br); - return all; - } - private RefTreeNames() { } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index a89bcee730..9e3e0b78fd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -107,6 +107,13 @@ public class ConfigConstants { /** The "autocrlf" key */ public static final String CONFIG_KEY_AUTOCRLF = "autocrlf"; + /** + * The "eol" key + * + * @since 4.3 + */ + public static final String CONFIG_KEY_EOL = "eol"; + /** The "bare" key */ public static final String CONFIG_KEY_BARE = "bare"; @@ -228,6 +235,12 @@ public class ConfigConstants { */ public static final String CONFIG_KEY_HIDEDOTFILES = "hidedotfiles"; + /** + * The "dirnogitlinks" key + * @since 4.3 + */ + public static final String CONFIG_KEY_DIRNOGITLINKS = "dirNoGitLinks"; + /** The "precomposeunicode" key */ public static final String CONFIG_KEY_PRECOMPOSEUNICODE = "precomposeunicode"; @@ -235,6 +248,12 @@ public class ConfigConstants { public static final String CONFIG_KEY_PRUNEEXPIRE = "pruneexpire"; /** + * The "prunepackexpire" key + * @since 4.3 + */ + public static final String CONFIG_KEY_PRUNEPACKEXPIRE = "prunepackexpire"; + + /** * The "aggressiveDepth" key * @since 3.6 */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java index 5a7634a6f1..83efd43aa0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java @@ -76,6 +76,46 @@ public class CoreConfig { } /** + * Permissible values for {@code core.eol}. + * <p> + * https://git-scm.com/docs/gitattributes + * + * @since 4.3 + */ + public static enum EOL { + /** checkin with LF, checkout with CRLF. */ + CRLF, + + /** checkin with LF, checkout without conversion. */ + LF, + + /** use the platform's native line ending. */ + NATIVE; + } + + /** + * EOL stream conversion protocol + * + * @since 4.3 + */ + public static enum EolStreamType { + /** convert to CRLF without binary detection */ + TEXT_CRLF, + + /** convert to LF without binary detection */ + TEXT_LF, + + /** convert to CRLF with binary detection */ + AUTO_CRLF, + + /** convert to LF with binary detection */ + AUTO_LF, + + /** do not convert */ + DIRECT; + } + + /** * Permissible values for {@code core.checkstat} * * @since 3.0 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java index 4316cd0a1f..c1027f0f75 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java @@ -552,7 +552,8 @@ public abstract class RefUpdate { */ public Result delete(final RevWalk walk) throws IOException { final String myName = getRef().getLeaf().getName(); - if (myName.startsWith(Constants.R_HEADS)) { + if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) { + // Don't allow the currently checked out branch to be deleted. Ref head = getRefDatabase().getRef(Constants.HEAD); while (head != null && head.isSymbolic()) { head = head.getTarget(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index f8266133a6..ba0dea39f5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -110,7 +110,8 @@ public abstract class Repository implements AutoCloseable { return globalListeners; } - private final AtomicInteger useCnt = new AtomicInteger(1); + /** Use counter */ + final AtomicInteger useCnt = new AtomicInteger(1); /** Metadata directory holding the repository's critical files. */ private final File gitDir; @@ -864,6 +865,7 @@ public abstract class Repository implements AutoCloseable { public void close() { if (useCnt.decrementAndGet() == 0) { doClose(); + RepositoryCache.unregister(this); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java index 23cc264c1c..22b5fcd112 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -130,10 +130,10 @@ public class RepositoryCache { } /** - * Remove a repository from the cache. + * Close and remove a repository from the cache. * <p> - * Removes a repository from the cache, if it is still registered here, - * permitting it to close. + * Removes a repository from the cache, if it is still registered here, and + * close it. * * @param db * repository to unregister. @@ -141,15 +141,35 @@ public class RepositoryCache { public static void close(final Repository db) { if (db.getDirectory() != null) { FileKey key = FileKey.exact(db.getDirectory(), db.getFS()); - cache.unregisterRepository(key); + cache.unregisterAndCloseRepository(key); } } /** * Remove a repository from the cache. * <p> - * Removes a repository from the cache, if it is still registered here, - * permitting it to close. + * Removes a repository from the cache, if it is still registered here. This + * method will not close the repository, only remove it from the cache. See + * {@link RepositoryCache#close(Repository)} to remove and close the + * repository. + * + * @param db + * repository to unregister. + * @since 4.3 + */ + public static void unregister(final Repository db) { + if (db.getDirectory() != null) { + unregister(FileKey.exact(db.getDirectory(), db.getFS())); + } + } + + /** + * Remove a repository from the cache. + * <p> + * Removes a repository from the cache, if it is still registered here. This + * method will not close the repository, only remove it from the cache. See + * {@link RepositoryCache#close(Repository)} to remove and close the + * repository. * * @param location * location of the repository to remove. @@ -196,15 +216,17 @@ public class RepositoryCache { db = location.open(mustExist); ref = new SoftReference<Repository>(db); cacheMap.put(location, ref); + } else { + db.incrementOpen(); } } + } else { + db.incrementOpen(); } - db.incrementOpen(); return db; } private void registerRepository(final Key location, final Repository db) { - db.incrementOpen(); SoftReference<Repository> newRef = new SoftReference<Repository>(db); Reference<Repository> oldRef = cacheMap.put(location, newRef); Repository oldDb = oldRef != null ? oldRef.get() : null; @@ -212,11 +234,16 @@ public class RepositoryCache { oldDb.close(); } - private void unregisterRepository(final Key location) { + private Repository unregisterRepository(final Key location) { Reference<Repository> oldRef = cacheMap.remove(location); - Repository oldDb = oldRef != null ? oldRef.get() : null; - if (oldDb != null) + return oldRef != null ? oldRef.get() : null; + } + + private void unregisterAndCloseRepository(final Key location) { + Repository oldDb = unregisterRepository(location); + if (oldDb != null) { oldDb.close(); + } } private Collection<Key> getKeys() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index de08e4b6a0..e224d71d4b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -1005,13 +1005,14 @@ public class ResolveMerger extends ThreeWayMerger { builder = dircache.builder(); DirCacheBuildIterator buildIt = new DirCacheBuildIterator(builder); - tw = new NameConflictTreeWalk(reader); + tw = new NameConflictTreeWalk(db, reader); tw.addTree(baseTree); tw.addTree(headTree); tw.addTree(mergeTree); - tw.addTree(buildIt); + int dciPos = tw.addTree(buildIt); if (workingTreeIterator != null) { tw.addTree(workingTreeIterator); + workingTreeIterator.setDirCacheIterator(tw, dciPos); } else { tw.setFilter(TreeFilter.ANY_DIFF); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java index f18a522131..ec903c139d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java @@ -106,7 +106,7 @@ public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy { InCoreMerger(final Repository local) { super(local); - tw = new NameConflictTreeWalk(reader); + tw = new NameConflictTreeWalk(local, reader); cache = DirCache.newInCore(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java index 3941d3c552..739ddccc73 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 d9e0b937e8..1beec906ed 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 class Daemon { } /** + * 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 594827886b..414e8790ca 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.MalformedURLException; 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.TreeMap; 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 @@ public class TransportHttp extends HttpTransport implements WalkTransport, 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 @@ public class TransportHttp extends HttpTransport implements WalkTransport, 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 @@ public class TransportHttp extends HttpTransport implements WalkTransport, in.add(openInputStream(conn)); } } - - private static class DummyX509TrustManager implements X509TrustManager { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { - // no check - } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { - // no check - } - } - - private static class DummyHostnameVerifier implements HostnameVerifier { - public boolean verify(String hostname, SSLSession session) { - // always accept - return true; - } - } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java index 3ee2feb140..3c5c8daddd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java @@ -376,8 +376,10 @@ public class URIish implements Serializable { public URIish(final URL u) { scheme = u.getProtocol(); path = u.getPath(); + path = cleanLeadingSlashes(path, scheme); try { rawPath = u.toURI().getRawPath(); + rawPath = cleanLeadingSlashes(rawPath, scheme); } catch (URISyntaxException e) { throw new RuntimeException(e); // Impossible } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 101057fb4f..8b642bb980 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -742,10 +742,6 @@ public class UploadPack { if (!clientShallowCommits.isEmpty()) walk.assumeShallow(clientShallowCommits); sendPack = negotiate(); - } catch (PackProtocolException err) { - reportErrorDuringNegotiate(err.getMessage()); - throw err; - } catch (ServiceMayNotContinueException err) { if (!err.isOutput() && err.getMessage() != null) { try { @@ -756,15 +752,20 @@ public class UploadPack { } } throw err; - - } catch (IOException err) { - reportErrorDuringNegotiate(JGitText.get().internalServerError); - throw err; - } catch (RuntimeException err) { - reportErrorDuringNegotiate(JGitText.get().internalServerError); - throw err; - } catch (Error err) { - reportErrorDuringNegotiate(JGitText.get().internalServerError); + } catch (IOException | RuntimeException | Error err) { + boolean output = false; + try { + String msg = err instanceof PackProtocolException + ? err.getMessage() + : JGitText.get().internalServerError; + pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + output = true; + } catch (Throwable err2) { + // Ignore this secondary failure, leave output false. + } + if (output) { + throw new UploadPackInternalServerErrorException(err); + } throw err; } @@ -781,14 +782,6 @@ public class UploadPack { return ids; } - private void reportErrorDuringNegotiate(String msg) { - try { - pckOut.writeString("ERR " + msg + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ - } catch (Throwable err) { - // Ignore this secondary failure. - } - } - private void processShallow() throws IOException { try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk( walk.getObjectReader(), depth)) { @@ -1186,9 +1179,7 @@ public class UploadPack { } wantIds.clear(); } catch (MissingObjectException notFound) { - ObjectId id = notFound.getObjectId(); - throw new PackProtocolException(MessageFormat.format( - JGitText.get().wantNotValid, id.name()), notFound); + throw new WantNotValidException(notFound.getObjectId(), notFound); } finally { q.release(); } @@ -1213,8 +1204,7 @@ public class UploadPack { if (!up.isBiDirectionalPipe()) new ReachableCommitRequestValidator().checkWants(up, wants); else if (!wants.isEmpty()) - throw new PackProtocolException(MessageFormat.format( - JGitText.get().wantNotValid, wants.iterator().next().name())); + throw new WantNotValidException(wants.iterator().next()); } } @@ -1247,8 +1237,7 @@ public class UploadPack { refIdSet(up.getRepository().getRefDatabase().getRefs(ALL).values()); for (ObjectId obj : wants) { if (!refIds.contains(obj)) - throw new PackProtocolException(MessageFormat.format( - JGitText.get().wantNotValid, obj.name())); + throw new WantNotValidException(obj); } } } @@ -1294,14 +1283,11 @@ public class UploadPack { RevObject obj; while ((obj = q.next()) != null) { if (!(obj instanceof RevCommit)) - throw new PackProtocolException(MessageFormat.format( - JGitText.get().wantNotValid, obj.name())); + throw new WantNotValidException(obj); walk.markStart((RevCommit) obj); } } catch (MissingObjectException notFound) { - ObjectId id = notFound.getObjectId(); - throw new PackProtocolException(MessageFormat.format( - JGitText.get().wantNotValid, id.name()), notFound); + throw new WantNotValidException(notFound.getObjectId(), notFound); } finally { q.release(); } @@ -1315,9 +1301,7 @@ public class UploadPack { RevCommit bad = walk.next(); if (bad != null) { - throw new PackProtocolException(MessageFormat.format( - JGitText.get().wantNotValid, - bad.name())); + throw new WantNotValidException(bad); } walk.reset(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WantNotValidException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WantNotValidException.java new file mode 100644 index 0000000000..f0f64344b8 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WantNotValidException.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport; + +import java.text.MessageFormat; + +import org.eclipse.jgit.errors.PackProtocolException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.AnyObjectId; + +/** + * Indicates client requested an object the server does not want to serve. + * <p> + * Typically visible only inside of the server implementation; clients are + * usually looking at the text message from the server in a generic + * {@link PackProtocolException}. + * + * @since 4.3 + */ +public class WantNotValidException extends PackProtocolException { + private static final long serialVersionUID = 1L; + + /** + * Construct a {@code "want $id not valid"} exception. + * + * @param id + * invalid object identifier received from the client. + */ + public WantNotValidException(AnyObjectId id) { + super(msg(id)); + } + + /** + * Construct a {@code "want $id not valid"} exception. + * + * @param id + * invalid object identifier received from the client. + * @param cause + * root cause of the object being invalid, such as an IOException + * from the storage system. + */ + public WantNotValidException(AnyObjectId id, Throwable cause) { + super(msg(id), cause); + } + + private static String msg(AnyObjectId id) { + return MessageFormat.format(JGitText.get().wantNotValid, id.name()); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java index 58136355eb..07fc829db4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java @@ -50,6 +50,7 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.attributes.AttributesHandler; import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -88,8 +89,14 @@ public abstract class AbstractTreeIterator { /** A dummy object id buffer that matches the zero ObjectId. */ protected static final byte[] zeroid = new byte[Constants.OBJECT_ID_LENGTH]; - /** Iterator for the parent tree; null if we are the root iterator. */ - final AbstractTreeIterator parent; + /** + * Iterator for the parent tree; null if we are the root iterator. + * <p> + * Used by {@link TreeWalk} and {@link AttributesHandler} + * + * @since 4.3 + */ + public final AbstractTreeIterator parent; /** The iterator this current entry is path equal to. */ AbstractTreeIterator matches; @@ -722,4 +729,13 @@ public abstract class AbstractTreeIterator { public String toString() { return getClass().getSimpleName() + "[" + getEntryPathString() + "]"; //$NON-NLS-1$ } + + /** + * @return whether or not this Iterator is iterating through the Work Tree + * + * @since 4.3 + */ + public boolean isWorkTree() { + return false; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index accf4956f6..db81e1af9b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -79,14 +79,42 @@ public class FileTreeIterator extends WorkingTreeIterator { protected final FS fs; /** + * the strategy used to compute the FileMode for a FileEntry. Can be used to + * control things such as whether to recurse into a directory or create a + * gitlink. + * + * @since 4.3 + */ + protected final FileModeStrategy fileModeStrategy; + + /** * Create a new iterator to traverse the work tree and its children. * * @param repo * the repository whose working tree will be scanned. */ public FileTreeIterator(Repository repo) { + this(repo, + repo.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks() ? + NoGitlinksStrategy.INSTANCE : + DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new iterator to traverse the work tree and its children. + * + * @param repo + * the repository whose working tree will be scanned. + * @param fileModeStrategy + * the strategy to use to determine the FileMode for a FileEntry; + * controls gitlinks etc. + * + * @since 4.3 + */ + public FileTreeIterator(Repository repo, FileModeStrategy fileModeStrategy) { this(repo.getWorkTree(), repo.getFS(), - repo.getConfig().get(WorkingTreeOptions.KEY)); + repo.getConfig().get(WorkingTreeOptions.KEY), + fileModeStrategy); initRootIterator(repo); } @@ -103,9 +131,32 @@ public class FileTreeIterator extends WorkingTreeIterator { * working tree options to be used */ public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options) { + this(root, fs, options, DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new iterator to traverse the given directory and its children. + * + * @param root + * the starting directory. This directory should correspond to + * the root of the repository. + * @param fs + * the file system abstraction which will be necessary to perform + * certain file system operations. + * @param options + * working tree options to be used + * @param fileModeStrategy + * the strategy to use to determine the FileMode for a FileEntry; + * controls gitlinks etc. + * + * @since 4.3 + */ + public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options, + FileModeStrategy fileModeStrategy) { super(options); directory = root; this.fs = fs; + this.fileModeStrategy = fileModeStrategy; init(entries()); } @@ -114,25 +165,72 @@ public class FileTreeIterator extends WorkingTreeIterator { * * @param p * the parent iterator we were created from. + * @param root + * the subdirectory. This should be a directory contained within + * the parent directory. * @param fs * the file system abstraction which will be necessary to perform * certain file system operations. + * @since 4.3 + * @deprecated use {@link #FileTreeIterator(FileTreeIterator, File, FS)} + * instead. + */ + @Deprecated + protected FileTreeIterator(final WorkingTreeIterator p, final File root, + FS fs) { + this(p, root, fs, DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new iterator to traverse a subdirectory. + * + * @param p + * the parent iterator we were created from. * @param root * the subdirectory. This should be a directory contained within * the parent directory. + * @param fs + * the file system abstraction which will be necessary to perform + * certain file system operations. + * + * @since 4.3 */ - protected FileTreeIterator(final WorkingTreeIterator p, final File root, + protected FileTreeIterator(final FileTreeIterator p, final File root, FS fs) { + this(p, root, fs, p.fileModeStrategy); + } + + /** + * Create a new iterator to traverse a subdirectory, given the specified + * FileModeStrategy. + * + * @param p + * the parent iterator we were created from. + * @param root + * the subdirectory. This should be a directory contained within + * the parent directory + * @param fs + * the file system abstraction which will be necessary to perform + * certain file system operations. + * @param fileModeStrategy + * the strategy to use to determine the FileMode for a given + * FileEntry. + * + * @since 4.3 + */ + protected FileTreeIterator(final WorkingTreeIterator p, final File root, + FS fs, FileModeStrategy fileModeStrategy) { super(p); directory = root; this.fs = fs; + this.fileModeStrategy = fileModeStrategy; init(entries()); } @Override public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader) throws IncorrectObjectTypeException, IOException { - return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs); + return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs, fileModeStrategy); } private Entry[] entries() { @@ -141,11 +239,92 @@ public class FileTreeIterator extends WorkingTreeIterator { return EOF; final Entry[] r = new Entry[all.length]; for (int i = 0; i < r.length; i++) - r[i] = new FileEntry(all[i], fs); + r[i] = new FileEntry(all[i], fs, fileModeStrategy); return r; } /** + * An interface representing the methods used to determine the FileMode for + * a FileEntry. + * + * @since 4.3 + */ + public interface FileModeStrategy { + /** + * Compute the FileMode for a given File, based on its attributes. + * + * @param f + * the file to return a FileMode for + * @param attributes + * the attributes of a file + * @return a FileMode indicating whether the file is a regular file, a + * directory, a gitlink, etc. + */ + FileMode getMode(File f, FS.Attributes attributes); + } + + /** + * A default implementation of a FileModeStrategy; defaults to treating + * nested .git directories as gitlinks, etc. + * + * @since 4.3 + */ + static public class DefaultFileModeStrategy implements FileModeStrategy { + /** + * a singleton instance of the default FileModeStrategy + */ + public final static DefaultFileModeStrategy INSTANCE = + new DefaultFileModeStrategy(); + + @Override + public FileMode getMode(File f, FS.Attributes attributes) { + if (attributes.isSymbolicLink()) { + return FileMode.SYMLINK; + } else if (attributes.isDirectory()) { + if (new File(f, Constants.DOT_GIT).exists()) { + return FileMode.GITLINK; + } else { + return FileMode.TREE; + } + } else if (attributes.isExecutable()) { + return FileMode.EXECUTABLE_FILE; + } else { + return FileMode.REGULAR_FILE; + } + } + } + + /** + * A FileModeStrategy that implements native git's DIR_NO_GITLINKS + * behavior. This is the same as the default FileModeStrategy, except + * all directories will be treated as directories regardless of whether + * or not they contain a .git directory or file. + * + * @since 4.3 + */ + static public class NoGitlinksStrategy implements FileModeStrategy { + + /** + * a singleton instance of the default FileModeStrategy + */ + public final static NoGitlinksStrategy INSTANCE = new NoGitlinksStrategy(); + + @Override + public FileMode getMode(File f, FS.Attributes attributes) { + if (attributes.isSymbolicLink()) { + return FileMode.SYMLINK; + } else if (attributes.isDirectory()) { + return FileMode.TREE; + } else if (attributes.isExecutable()) { + return FileMode.EXECUTABLE_FILE; + } else { + return FileMode.REGULAR_FILE; + } + } + } + + + /** * Wrapper for a standard Java IO file */ static public class FileEntry extends Entry { @@ -164,20 +343,27 @@ public class FileTreeIterator extends WorkingTreeIterator { * file system */ public FileEntry(File f, FS fs) { + this(f, fs, DefaultFileModeStrategy.INSTANCE); + } + + /** + * Create a new file entry given the specified FileModeStrategy + * + * @param f + * file + * @param fs + * file system + * @param fileModeStrategy + * the strategy to use when determining the FileMode of a + * file; controls gitlinks etc. + * + * @since 4.3 + */ + public FileEntry(File f, FS fs, FileModeStrategy fileModeStrategy) { this.fs = fs; f = fs.normalize(f); attributes = fs.getAttributes(f); - if (attributes.isSymbolicLink()) - mode = FileMode.SYMLINK; - else if (attributes.isDirectory()) { - if (new File(f, Constants.DOT_GIT).exists()) - mode = FileMode.GITLINK; - else - mode = FileMode.TREE; - } else if (attributes.isExecutable()) - mode = FileMode.EXECUTABLE_FILE; - else - mode = FileMode.REGULAR_FILE; + mode = fileModeStrategy.getMode(f, attributes); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java index d2195a874c..b9293ebfb6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java @@ -104,6 +104,19 @@ public class NameConflictTreeWalk extends TreeWalk { /** * Create a new tree walker for a given repository. * + * @param repo + * the repository the walker will obtain data from. + * @param or + * the reader the walker will obtain tree data from. + * @since 4.3 + */ + public NameConflictTreeWalk(Repository repo, final ObjectReader or) { + super(repo, or); + } + + /** + * Create a new tree walker for a given repository. + * * @param or * the reader the walker will obtain tree data from. */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 5cd713da78..7dac50a89a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -52,12 +52,11 @@ import java.util.Set; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.attributes.Attribute; -import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.attributes.Attributes; -import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.attributes.AttributesProvider; import org.eclipse.jgit.dircache.DirCacheBuildIterator; +import org.eclipse.jgit.attributes.AttributesHandler; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -66,6 +65,7 @@ import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectId; @@ -76,6 +76,7 @@ import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.jgit.util.QuotedString; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.io.EolStreamTypeUtil; /** * Walks one or more {@link AbstractTreeIterator}s in parallel. @@ -163,7 +164,44 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { public static TreeWalk forPath(final ObjectReader reader, final String path, final AnyObjectId... trees) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { - TreeWalk tw = new TreeWalk(reader); + return forPath(null, reader, path, trees); + } + + /** + * Open a tree walk and filter to exactly one path. + * <p> + * The returned tree walk is already positioned on the requested path, so + * the caller should not need to invoke {@link #next()} unless they are + * looking for a possible directory/file name conflict. + * + * @param repo + * repository to read config data and + * {@link AttributesNodeProvider} from. + * @param reader + * the reader the walker will obtain tree data from. + * @param path + * single path to advance the tree walk instance into. + * @param trees + * one or more trees to walk through, all with the same root. + * @return a new tree walk configured for exactly this one path; null if no + * path was found in any of the trees. + * @throws IOException + * reading a pack file or loose object failed. + * @throws CorruptObjectException + * an tree object could not be read as its data stream did not + * appear to be a tree, or could not be inflated. + * @throws IncorrectObjectTypeException + * an object we expected to be a tree was not a tree. + * @throws MissingObjectException + * a tree object was not found. + * @since 4.3 + */ + public static TreeWalk forPath(final @Nullable Repository repo, + final ObjectReader reader, final String path, + final AnyObjectId... trees) + throws MissingObjectException, IncorrectObjectTypeException, + CorruptObjectException, IOException { + TreeWalk tw = new TreeWalk(repo, reader); PathFilter f = PathFilter.create(path); tw.setFilter(f); tw.reset(trees); @@ -208,7 +246,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { final AnyObjectId... trees) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { try (ObjectReader reader = db.newObjectReader()) { - return forPath(reader, path, trees); + return forPath(db, reader, path, trees); } } @@ -270,6 +308,9 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { /** Cached attribute for the current entry */ private Attributes attrs = null; + /** Cached attributes handler */ + private AttributesHandler attributesHandler; + private Config config; /** @@ -281,9 +322,23 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * when the walker is closed. */ public TreeWalk(final Repository repo) { - this(repo.newObjectReader(), true); - config = repo.getConfig(); - attributesNodeProvider = repo.createAttributesNodeProvider(); + this(repo, repo.newObjectReader(), true); + } + + /** + * Create a new tree walker for a given repository. + * + * @param repo + * the repository the walker will obtain data from. An + * ObjectReader will be created by the walker, and will be closed + * when the walker is closed. + * @param or + * the reader the walker will obtain tree data from. The reader + * is not closed when the walker is closed. + * @since 4.3 + */ + public TreeWalk(final @Nullable Repository repo, final ObjectReader or) { + this(repo, or, false); } /** @@ -294,10 +349,18 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * is not closed when the walker is closed. */ public TreeWalk(final ObjectReader or) { - this(or, false); + this(null, or, false); } - private TreeWalk(final ObjectReader or, final boolean closeReader) { + private TreeWalk(final @Nullable Repository repo, final ObjectReader or, + final boolean closeReader) { + if (repo != null) { + config = repo.getConfig(); + attributesNodeProvider = repo.createAttributesNodeProvider(); + } else { + config = null; + attributesNodeProvider = null; + } reader = or; filter = TreeFilter.ALL; trees = NO_TREES; @@ -310,6 +373,14 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** + * @return the {@link OperationType} + * @since 4.3 + */ + public OperationType getOperationType() { + return operationType; + } + + /** * Release any resources used by this walker's reader. * <p> * A walker that has been released can be used again, but may need to be @@ -435,9 +506,110 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { attributesNodeProvider = provider; } + /** + * @return the {@link AttributesNodeProvider} for this {@link TreeWalk}. + * @since 4.3 + */ + public AttributesNodeProvider getAttributesNodeProvider() { + return attributesNodeProvider; + } + + /** + * Retrieve the git attributes for the current entry. + * + * <h4>Git attribute computation</h4> + * + * <ul> + * <li>Get the attributes matching the current path entry from the info file + * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li> + * <li>Completes the list of attributes using the .gitattributes files + * located on the current path (the further the directory that contains + * .gitattributes is from the path in question, the lower its precedence). + * For a checkin operation, it will look first on the working tree (if any). + * If there is no attributes file, it will fallback on the index. For a + * checkout operation, it will first use the index entry and then fallback + * on the working tree if none.</li> + * <li>In the end, completes the list of matching attributes using the + * global attribute file define in the configuration (see + * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li> + * + * </ul> + * + * + * <h4>Iterator constraints</h4> + * + * <p> + * In order to have a correct list of attributes for the current entry, this + * {@link TreeWalk} requires to have at least one + * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An + * {@link AttributesNodeProvider} is used to retrieve the attributes from + * the info attributes file and the global attributes file. The + * {@link DirCacheIterator} is used to retrieve the .gitattributes files + * stored in the index. A {@link WorkingTreeIterator} can also be provided + * to access the local version of the .gitattributes files. If none is + * provided it will fallback on the {@link DirCacheIterator}. + * </p> + * + * @return a {@link Set} of {@link Attribute}s that match the current entry. + * @since 4.2 + */ + public Attributes getAttributes() { + if (attrs != null) + return attrs; + + if (attributesNodeProvider == null) { + // The work tree should have a AttributesNodeProvider to be able to + // retrieve the info and global attributes node + throw new IllegalStateException( + "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$ + } + + try { + // Lazy create the attributesHandler on the first access of + // attributes. This requires the info, global and root + // attributes nodes + if (attributesHandler == null) { + attributesHandler = new AttributesHandler(this); + } + attrs = attributesHandler.getAttributes(); + return attrs; + } catch (IOException e) { + throw new JGitInternalException("Error while parsing attributes", //$NON-NLS-1$ + e); + } + } + + /** + * @param opType + * the operationtype (checkin/checkout) which should be used + * @return the EOL stream type of the current entry using the config and + * {@link #getAttributes()} Note that this method may return null if + * the {@link TreeWalk} is not based on a working tree + */ + // TODO(msohn) make this method public in 4.4 + @Nullable + EolStreamType getEolStreamType(OperationType opType) { + if (attributesNodeProvider == null || config == null) + return null; + return EolStreamTypeUtil.detectStreamType(opType, + config.get(WorkingTreeOptions.KEY), getAttributes()); + } + + /** + * @return the EOL stream type of the current entry using the config and + * {@link #getAttributes()} Note that this method may return null if + * the {@link TreeWalk} is not based on a working tree + * @since 4.3 + */ + // TODO(msohn) deprecate this method in 4.4 + public @Nullable EolStreamType getEolStreamType() { + return (getEolStreamType(operationType)); + } + /** Reset this walker so new tree iterators can be added to it. */ public void reset() { attrs = null; + attributesHandler = null; trees = NO_TREES; advance = false; depth = 0; @@ -740,6 +912,16 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** + * Obtain the {@link FileMode} for the current entry on the currentHead tree + * + * @return mode for the current entry of the currentHead tree. + * @since 4.3 + */ + public FileMode getFileMode() { + return FileMode.fromBits(currentHead.mode); + } + + /** * Obtain the ObjectId for the current entry. * <p> * Using this method to compare ObjectId values between trees of this walker @@ -1019,7 +1201,12 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { for (int i = 0; i < trees.length; i++) { final AbstractTreeIterator t = trees[i]; final AbstractTreeIterator n; - if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode)) + // If we find a GITLINK when attempting to enter a subtree, then the + // GITLINK must exist as a TREE in the index, meaning the working tree + // entry should be treated as a TREE + if (t.matches == ch && !t.eof() && + (FileMode.TREE.equals(t.mode) + || (FileMode.GITLINK.equals(t.mode) && t.isWorkTree()))) n = t.createSubtreeIterator(reader, idBuffer); else n = t.createEmptyTreeIterator(); @@ -1109,156 +1296,13 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** - * Retrieve the git attributes for the current entry. - * - * <h4>Git attribute computation</h4> - * - * <ul> - * <li>Get the attributes matching the current path entry from the info file - * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li> - * <li>Completes the list of attributes using the .gitattributes files - * located on the current path (the further the directory that contains - * .gitattributes is from the path in question, the lower its precedence). - * For a checkin operation, it will look first on the working tree (if any). - * If there is no attributes file, it will fallback on the index. For a - * checkout operation, it will first use the index entry and then fallback - * on the working tree if none.</li> - * <li>In the end, completes the list of matching attributes using the - * global attribute file define in the configuration (see - * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li> - * - * </ul> - * - * - * <h4>Iterator constraints</h4> - * - * <p> - * In order to have a correct list of attributes for the current entry, this - * {@link TreeWalk} requires to have at least one - * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An - * {@link AttributesNodeProvider} is used to retrieve the attributes from - * the info attributes file and the global attributes file. The - * {@link DirCacheIterator} is used to retrieve the .gitattributes files - * stored in the index. A {@link WorkingTreeIterator} can also be provided - * to access the local version of the .gitattributes files. If none is - * provided it will fallback on the {@link DirCacheIterator}. - * </p> - * - * @return a {@link Set} of {@link Attribute}s that match the current entry. - * @since 4.2 - */ - public Attributes getAttributes() { - if (attrs != null) - return attrs; - - if (attributesNodeProvider == null) { - // The work tree should have a AttributesNodeProvider to be able to - // retrieve the info and global attributes node - throw new IllegalStateException( - "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$ - } - - WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class); - DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class); - CanonicalTreeParser other = getTree(CanonicalTreeParser.class); - - if (workingTreeIterator == null && dirCacheIterator == null - && other == null) { - // Can not retrieve the attributes without at least one of the above - // iterators. - return new Attributes(); - } - - String path = currentHead.getEntryPathString(); - final boolean isDir = FileMode.TREE.equals(currentHead.mode); - Attributes attributes = new Attributes(); - try { - // Gets the global attributes node - AttributesNode globalNodeAttr = attributesNodeProvider - .getGlobalAttributesNode(); - // Gets the info attributes node - AttributesNode infoNodeAttr = attributesNodeProvider - .getInfoAttributesNode(); - - // Gets the info attributes - if (infoNodeAttr != null) { - infoNodeAttr.getAttributes(path, isDir, attributes); - } - - // Gets the attributes located on the current entry path - getPerDirectoryEntryAttributes(path, isDir, operationType, - workingTreeIterator, dirCacheIterator, other, attributes); - - // Gets the attributes located in the global attribute file - if (globalNodeAttr != null) { - globalNodeAttr.getAttributes(path, isDir, attributes); - } - } catch (IOException e) { - throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$ - } - // now after all attributes are collected - in the correct hierarchy - // order - remove all unspecified entries (the ! marker) - for (Attribute a : attributes.getAll()) { - if (a.getState() == State.UNSPECIFIED) - attributes.remove(a.getKey()); - } - return attributes; - } - - /** - * Get the attributes located on the current entry path. - * - * @param path - * current entry path - * @param isDir - * holds true if the current entry is a directory - * @param opType - * type of operation - * @param workingTreeIterator - * a {@link WorkingTreeIterator} matching the current entry - * @param dirCacheIterator - * a {@link DirCacheIterator} matching the current entry - * @param other - * a {@link CanonicalTreeParser} matching the current entry - * @param attributes - * Non null map holding the existing attributes. This map will be - * augmented with new entry. None entry will be overrided. - * @throws IOException - * It raises an {@link IOException} if a problem appears while - * parsing one on the attributes file. + * @param type + * of the tree to be queried + * @return the tree of that type or null if none is present + * @since 4.3 */ - private void getPerDirectoryEntryAttributes(String path, boolean isDir, - OperationType opType, WorkingTreeIterator workingTreeIterator, - DirCacheIterator dirCacheIterator, CanonicalTreeParser other, - Attributes attributes) - throws IOException { - // Prevents infinite recurrence - if (workingTreeIterator != null || dirCacheIterator != null - || other != null) { - AttributesNode currentAttributesNode = getCurrentAttributesNode( - opType, workingTreeIterator, dirCacheIterator, other); - if (currentAttributesNode != null) { - currentAttributesNode.getAttributes(path, isDir, attributes); - } - getPerDirectoryEntryAttributes(path, isDir, opType, - getParent(workingTreeIterator, WorkingTreeIterator.class), - getParent(dirCacheIterator, DirCacheIterator.class), - getParent(other, CanonicalTreeParser.class), attributes); - } - } - - private static <T extends AbstractTreeIterator> T getParent(T current, + public <T extends AbstractTreeIterator> T getTree( Class<T> type) { - if (current != null) { - AbstractTreeIterator parent = current.parent; - if (type.isInstance(parent)) { - return type.cast(parent); - } - } - return null; - } - - private <T extends AbstractTreeIterator> T getTree(Class<T> type) { for (int i = 0; i < trees.length; i++) { AbstractTreeIterator tree = trees[i]; if (type.isInstance(tree)) { @@ -1269,76 +1313,6 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { } /** - * Get the {@link AttributesNode} for the current entry. - * <p> - * This method implements the fallback mechanism between the index and the - * working tree depending on the operation type - * </p> - * - * @param opType - * @param workingTreeIterator - * @param dirCacheIterator - * @param other - * @return a {@link AttributesNode} of the current entry, - * {@link NullPointerException} otherwise. - * @throws IOException - * It raises an {@link IOException} if a problem appears while - * parsing one on the attributes file. - */ - private AttributesNode getCurrentAttributesNode(OperationType opType, - @Nullable WorkingTreeIterator workingTreeIterator, - @Nullable DirCacheIterator dirCacheIterator, - @Nullable CanonicalTreeParser other) - throws IOException { - AttributesNode attributesNode = null; - switch (opType) { - case CHECKIN_OP: - if (workingTreeIterator != null) { - attributesNode = workingTreeIterator.getEntryAttributesNode(); - } - if (attributesNode == null && dirCacheIterator != null) { - attributesNode = getAttributesNode(dirCacheIterator - .getEntryAttributesNode(getObjectReader()), - attributesNode); - } - if (attributesNode == null && other != null) { - attributesNode = getAttributesNode( - other.getEntryAttributesNode(getObjectReader()), - attributesNode); - } - break; - case CHECKOUT_OP: - if (other != null) { - attributesNode = other - .getEntryAttributesNode(getObjectReader()); - } - if (dirCacheIterator != null) { - attributesNode = getAttributesNode(dirCacheIterator - .getEntryAttributesNode(getObjectReader()), - attributesNode); - } - if (attributesNode == null && workingTreeIterator != null) { - attributesNode = getAttributesNode( - workingTreeIterator.getEntryAttributesNode(), - attributesNode); - } - break; - default: - throw new IllegalStateException( - "The only supported operation types are:" //$NON-NLS-1$ - + OperationType.CHECKIN_OP + "," //$NON-NLS-1$ - + OperationType.CHECKOUT_OP); - } - - return attributesNode; - } - - private static AttributesNode getAttributesNode(AttributesNode value, - AttributesNode defaultValue) { - return (value == null) ? defaultValue : value; - } - - /** * Inspect config and attributes to return a filtercommand applicable for * the current path * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 0d617ee7f9..c8de3de83c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -77,8 +77,8 @@ import org.eclipse.jgit.ignore.IgnoreNode; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig; -import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.CheckStat; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; @@ -86,12 +86,15 @@ import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.submodule.SubmoduleWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS.ExecutionResult; +import org.eclipse.jgit.util.Holder; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.Paths; import org.eclipse.jgit.util.RawParseUtils; -import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; +import org.eclipse.jgit.util.io.AutoLFInputStream; +import org.eclipse.jgit.util.io.EolStreamTypeUtil; /** * Walks a working directory tree as part of a {@link TreeWalk}. @@ -140,7 +143,17 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** If there is a .gitignore file present, the parsed rules from it. */ private IgnoreNode ignoreNode; - private String cleanFilterCommand; + /** + * cached clean filter command. Use a Ref in order to distinguish between + * the ref not cached yet and the value null + */ + private Holder<String> cleanFilterCommandHolder; + + /** + * cached eol stream type. Use a Ref in order to distinguish between the ref + * not cached yet and the value null + */ + private Holder<EolStreamType> eolStreamTypeHolder; /** Repository that is the root level being iterated over */ protected Repository repository; @@ -252,7 +265,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { // the cached index information for the path. // DirCacheIterator i = state.walk.getTree(state.dirCacheTree, - DirCacheIterator.class); + DirCacheIterator.class); if (i != null) { DirCacheEntry ent = i.getDirCacheEntry(); if (ent != null && compareMetadata(ent) == MetadataDiff.EQUAL) { @@ -277,6 +290,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return zeroid; } + @Override + public boolean isWorkTree() { + return true; + } + /** * Get submodule id for given entry. * @@ -344,7 +362,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { state.initializeDigestAndReadBuffer(); final long len = e.getLength(); - InputStream filteredIs = possiblyFilteredInputStream(e, is, len); + InputStream filteredIs = possiblyFilteredInputStream(e, is, len, + OperationType.CHECKIN_OP); return computeHash(filteredIs, canonLen); } finally { safeClose(is); @@ -357,8 +376,15 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { private InputStream possiblyFilteredInputStream(final Entry e, final InputStream is, final long len) throws IOException { - boolean mightNeedCleaning = mightNeedCleaning(); - if (!mightNeedCleaning) { + return possiblyFilteredInputStream(e, is, len, null); + + } + + private InputStream possiblyFilteredInputStream(final Entry e, + final InputStream is, final long len, OperationType opType) + throws IOException { + if (getCleanFilterCommand() == null + && getEolStreamType(opType) == EolStreamType.DIRECT) { canonLen = len; return is; } @@ -368,7 +394,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { byte[] raw = rawbuf.array(); int n = rawbuf.limit(); if (!isBinary(raw, n)) { - rawbuf = filterClean(raw, n); + rawbuf = filterClean(raw, n, opType); raw = rawbuf.array(); n = rawbuf.limit(); } @@ -376,19 +402,19 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return new ByteArrayInputStream(raw, 0, n); } - // TODO: fix autocrlf causing mightneedcleaning - if (!mightNeedCleaning && isBinary(e)) { - canonLen = len; - return is; - } + if (getCleanFilterCommand() == null && isBinary(e)) { + canonLen = len; + return is; + } - final InputStream lenIs = filterClean(e.openInputStream()); + final InputStream lenIs = filterClean(e.openInputStream(), + opType); try { canonLen = computeLength(lenIs); } finally { safeClose(lenIs); } - return filterClean(is); + return filterClean(is, opType); } private static void safeClose(final InputStream in) { @@ -401,20 +427,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } - private boolean mightNeedCleaning() throws IOException { - switch (getOptions().getAutoCRLF()) { - case FALSE: - default: - if (getCleanFilterCommand() != null) - return true; - return false; - - case TRUE: - case INPUT: - return true; - } - } - private static boolean isBinary(byte[] content, int sz) { return RawText.isBinary(content, sz); } @@ -428,17 +440,23 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } - private ByteBuffer filterClean(byte[] src, int n) throws IOException { + private ByteBuffer filterClean(byte[] src, int n, OperationType opType) + throws IOException { InputStream in = new ByteArrayInputStream(src); try { - return IO.readWholeStream(filterClean(in), n); + return IO.readWholeStream(filterClean(in, opType), n); } finally { safeClose(in); } } private InputStream filterClean(InputStream in) throws IOException { - in = handleAutoCRLF(in); + return filterClean(in, null); + } + + private InputStream filterClean(InputStream in, OperationType opType) + throws IOException { + in = handleAutoCRLF(in, opType); String filterCommand = getCleanFilterCommand(); if (filterCommand != null) { FS fs = repository.getFS(); @@ -467,12 +485,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return in; } - private InputStream handleAutoCRLF(InputStream in) { - AutoCRLF autoCRLF = getOptions().getAutoCRLF(); - if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) { - in = new EolCanonicalizingInputStream(in, true); - } - return in; + private InputStream handleAutoCRLF(InputStream in, OperationType opType) + throws IOException { + return EolStreamTypeUtil.wrapInputStream(in, getEolStreamType(opType)); } /** @@ -531,7 +546,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen); pathLen = pathOffset + nameLen; canonLen = -1; - cleanFilterCommand = null; + cleanFilterCommandHolder = null; + eolStreamTypeHolder = null; } /** @@ -594,10 +610,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { */ public InputStream openEntryStream() throws IOException { InputStream rawis = current().openInputStream(); - if (mightNeedCleaning()) - return filterClean(rawis); - else + if (getCleanFilterCommand() == null + && getEolStreamType() == EolStreamType.DIRECT) return rawis; + else + return filterClean(rawis); } /** @@ -921,17 +938,31 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { */ public FileMode getIndexFileMode(final DirCacheIterator indexIter) { final FileMode wtMode = getEntryFileMode(); - if (indexIter == null) - return wtMode; - if (getOptions().isFileMode()) + if (indexIter == null) { return wtMode; + } final FileMode iMode = indexIter.getEntryFileMode(); - if (FileMode.REGULAR_FILE == wtMode - && FileMode.EXECUTABLE_FILE == iMode) + if (getOptions().isFileMode() && iMode != FileMode.GITLINK && iMode != FileMode.TREE) { + return wtMode; + } + if (!getOptions().isFileMode()) { + if (FileMode.REGULAR_FILE == wtMode + && FileMode.EXECUTABLE_FILE == iMode) { + return iMode; + } + if (FileMode.EXECUTABLE_FILE == wtMode + && FileMode.REGULAR_FILE == iMode) { + return iMode; + } + } + if (FileMode.GITLINK == iMode + && FileMode.TREE == wtMode) { return iMode; - if (FileMode.EXECUTABLE_FILE == wtMode - && FileMode.REGULAR_FILE == iMode) + } + if (FileMode.TREE == iMode + && FileMode.GITLINK == wtMode) { return iMode; + } return wtMode; } @@ -971,10 +1002,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { // Content differs: that's a real change, perhaps if (reader == null) // deprecated use, do no further checks return true; - switch (getOptions().getAutoCRLF()) { - case INPUT: - case TRUE: - InputStream dcIn = null; + + switch (getEolStreamType()) { + case DIRECT: + return true; + default: try { ObjectLoader loader = reader.open(entry.getObjectId()); if (loader == null) @@ -982,37 +1014,26 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { // We need to compute the length, but only if it is not // a binary stream. - dcIn = new EolCanonicalizingInputStream( - loader.openStream(), true, true /* abort if binary */); long dcInLen; - try { + try (InputStream dcIn = new AutoLFInputStream( + loader.openStream(), true, + true /* abort if binary */)) { dcInLen = computeLength(dcIn); - } catch (EolCanonicalizingInputStream.IsBinaryException e) { + } catch (AutoLFInputStream.IsBinaryException e) { return true; - } finally { - dcIn.close(); } - dcIn = new EolCanonicalizingInputStream( - loader.openStream(), true); - byte[] autoCrLfHash = computeHash(dcIn, dcInLen); - boolean changed = getEntryObjectId().compareTo( - autoCrLfHash, 0) != 0; - return changed; + try (InputStream dcIn = new AutoLFInputStream( + loader.openStream(), true)) { + byte[] autoCrLfHash = computeHash(dcIn, dcInLen); + boolean changed = getEntryObjectId() + .compareTo(autoCrLfHash, 0) != 0; + return changed; + } } catch (IOException e) { return true; - } finally { - if (dcIn != null) - try { - dcIn.close(); - } catch (IOException e) { - // empty - } } - case FALSE: - break; } - return true; } } @@ -1308,10 +1329,61 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * @since 4.2 */ public String getCleanFilterCommand() throws IOException { - if (cleanFilterCommand == null && state.walk != null) { - cleanFilterCommand = state.walk - .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN); + if (cleanFilterCommandHolder == null) { + String cmd = null; + if (state.walk != null) { + cmd = state.walk + .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN); + } + cleanFilterCommandHolder = new Holder<String>(cmd); + } + return cleanFilterCommandHolder.get(); + } + + /** + * @return the eol stream type for the current entry or <code>null</code> if + * it cannot be determined. When state or state.walk is null or the + * {@link TreeWalk} is not based on a {@link Repository} then null + * is returned. + * @throws IOException + * @since 4.3 + */ + public EolStreamType getEolStreamType() throws IOException { + return getEolStreamType(null); + } + + /** + * @param opType + * The operationtype (checkin/checkout) which should be used + * @return the eol stream type for the current entry or <code>null</code> if + * it cannot be determined. When state or state.walk is null or the + * {@link TreeWalk} is not based on a {@link Repository} then null + * is returned. + * @throws IOException + */ + private EolStreamType getEolStreamType(OperationType opType) + throws IOException { + if (eolStreamTypeHolder == null) { + EolStreamType type=null; + if (state.walk != null) { + if (opType != null) { + type = state.walk.getEolStreamType(opType); + } else { + type=state.walk.getEolStreamType(); + } + } else { + switch (getOptions().getAutoCRLF()) { + case FALSE: + type = EolStreamType.DIRECT; + break; + case TRUE: + case INPUT: + type = EolStreamType.AUTO_LF; + break; + } + } + eolStreamTypeHolder = new Holder<EolStreamType>(type); } - return cleanFilterCommand; + return eolStreamTypeHolder.get(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java index a6dccce031..dea07c1973 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java @@ -48,6 +48,7 @@ import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Config.SectionParser; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.CheckStat; +import org.eclipse.jgit.lib.CoreConfig.EOL; import org.eclipse.jgit.lib.CoreConfig.HideDotFiles; import org.eclipse.jgit.lib.CoreConfig.SymLinks; @@ -64,17 +65,23 @@ public class WorkingTreeOptions { private final AutoCRLF autoCRLF; + private final EOL eol; + private final CheckStat checkStat; private final SymLinks symlinks; private final HideDotFiles hideDotFiles; + private final boolean dirNoGitLinks; + private WorkingTreeOptions(final Config rc) { fileMode = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION, ConfigConstants.CONFIG_KEY_FILEMODE, true); autoCRLF = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.FALSE); + eol = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_EOL, EOL.NATIVE); checkStat = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_CHECKSTAT, CheckStat.DEFAULT); symlinks = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, @@ -82,6 +89,9 @@ public class WorkingTreeOptions { hideDotFiles = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_HIDEDOTFILES, HideDotFiles.DOTGITONLY); + dirNoGitLinks = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, + false); } /** @return true if the execute bit on working files should be trusted. */ @@ -95,6 +105,15 @@ public class WorkingTreeOptions { } /** + * @return how text line endings should be normalized. + * + * @since 4.3 + */ + public EOL getEOL() { + return eol; + } + + /** * @return how stat data is compared * @since 3.0 */ @@ -117,4 +136,12 @@ public class WorkingTreeOptions { public HideDotFiles getHideDotFiles() { return hideDotFiles; } + + /** + * @return whether or not we treat nested repos as directories. + * If true, folders containing .git entries will not be + * treated as gitlinks. + * @since 4.3 + */ + public boolean isDirNoGitLinks() { return dirNoGitLinks; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java new file mode 100644 index 0000000000..3563e1bf1d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.util; + +/** + * Holder of an object. + * + * @param <T> + * the type of value held by this {@link Holder} + * + * @since 4.3 + */ +public class Holder<T> { + private T value; + + /** + * @param value + * is the initial value that is {@link #set(Object)} + */ + public Holder(T value) { + set(value); + } + + /** + * @return the value held by this {@link Holder} + */ + public T get() { + return value; + } + + /** + * @param value + * to be set as new value held by this {@link Holder} + */ + public void set(T value) { + this.value = value; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java index 8b4ad0aa29..7cb2bf6c87 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.ProxySelector; 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$ @@ -173,7 +189,8 @@ public class HttpSupport { try { return c.getResponseCode(); } catch (ConnectException ce) { - final String host = c.getURL().getHost(); + final URL url = c.getURL(); + final String host = (url == null) ? "<null>" : url.getHost(); // The standard J2SE error message is not very useful. // if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$ @@ -200,7 +217,8 @@ public class HttpSupport { try { return c.getResponseCode(); } catch (ConnectException ce) { - final String host = c.getURL().getHost(); + final URL url = c.getURL(); + final String host = (url == null) ? "<null>" : url.getHost(); // The standard J2SE error message is not very useful. // if ("Connection timed out: connect".equals(ce.getMessage())) //$NON-NLS-1$ @@ -234,6 +252,50 @@ public class HttpSupport { } } + /** + * 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 85c1648a0a..8536f1dc25 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 final class NB { } /** + * 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 f2955f7e6b..86777b9cdc 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 final class RawParseUtils { } /** + * Parse 16 character base 16 (hex) formatted string to unsigned long. + * <p> + * The number is read in network byte order, that is, most significant + * nibble first. + * + * @param bs + * buffer to parse digits from; positions {@code [p, p+16)} will + * be parsed. + * @param p + * first position within the buffer to parse. + * @return the integer value. + * @throws ArrayIndexOutOfBoundsException + * if the string is not hex formatted. + * @since 4.3 + */ + public static final long parseHexInt64(final byte[] bs, final int p) { + long r = digits16[bs[p]] << 4; + + r |= digits16[bs[p + 1]]; + r <<= 4; + + r |= digits16[bs[p + 2]]; + r <<= 4; + + r |= digits16[bs[p + 3]]; + r <<= 4; + + r |= digits16[bs[p + 4]]; + r <<= 4; + + r |= digits16[bs[p + 5]]; + r <<= 4; + + r |= digits16[bs[p + 6]]; + r <<= 4; + + r |= digits16[bs[p + 7]]; + r <<= 4; + + r |= digits16[bs[p + 8]]; + r <<= 4; + + r |= digits16[bs[p + 9]]; + r <<= 4; + + r |= digits16[bs[p + 10]]; + r <<= 4; + + r |= digits16[bs[p + 11]]; + r <<= 4; + + r |= digits16[bs[p + 12]]; + r <<= 4; + + r |= digits16[bs[p + 13]]; + r <<= 4; + + r |= digits16[bs[p + 14]]; + + final int last = digits16[bs[p + 15]]; + if (r < 0 || last < 0) + throw new ArrayIndexOutOfBoundsException(); + return (r << 4) | last; + } + + /** * Parse a single hex digit to its numeric value (0-15). * * @param digit diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java index 98c5477de1..30f9ce95fc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java @@ -50,7 +50,7 @@ import java.io.InputStream; import org.eclipse.jgit.diff.RawText; /** - * An OutputStream that expands LF to CRLF. + * An InputStream that expands LF to CRLF. * * Existing CRLF are not expanded to CRCRLF, but retained as is. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java index f05da1c73c..3a72f7e1dc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java @@ -50,8 +50,11 @@ import org.eclipse.jgit.diff.RawText; /** * An OutputStream that expands LF to CRLF. - * <p> + * * Existing CRLF are not expanded to CRCRLF, but retained as is. + * + * A binary check on the first 8000 bytes is performed and in case of binary + * files, canonicalization is turned off (for the complete file). */ public class AutoCRLFOutputStream extends OutputStream { @@ -67,13 +70,26 @@ public class AutoCRLFOutputStream extends OutputStream { private int binbufcnt = 0; + private boolean detectBinary; + private boolean isBinary; /** * @param out */ public AutoCRLFOutputStream(OutputStream out) { + this(out, true); + } + + /** + * @param out + * @param detectBinary + * whether binaries should be detected + * @since 4.3 + */ + public AutoCRLFOutputStream(OutputStream out, boolean detectBinary) { this.out = out; + this.detectBinary = detectBinary; } @Override @@ -141,7 +157,10 @@ public class AutoCRLFOutputStream extends OutputStream { } private void decideMode() throws IOException { - isBinary = RawText.isBinary(binbuf, binbufcnt); + if (detectBinary) { + isBinary = RawText.isBinary(binbuf, binbufcnt); + detectBinary = false; + } int cachedLen = binbufcnt; binbufcnt = binbuf.length + 1; // full! write(binbuf, 0, cachedLen); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java new file mode 100644 index 0000000000..6e33f99127 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com> + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.util.io; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jgit.diff.RawText; + +/** + * An InputStream that normalizes CRLF to LF. + * + * Existing single CR are not changed to LF, but retained as is. + * + * Optionally, a binary check on the first 8000 bytes is performed and in case + * of binary files, canonicalization is turned off (for the complete file). + * <p> + * This is the former EolCanonicalizingInputStream with a new name in order to + * have same naming for all LF / CRLF streams + * + * @since 4.3 + */ +public class AutoLFInputStream extends InputStream { + private final byte[] single = new byte[1]; + + private final byte[] buf = new byte[8096]; + + private final InputStream in; + + private int cnt; + + private int ptr; + + private boolean isBinary; + + private boolean detectBinary; + + private boolean abortIfBinary; + + /** + * A special exception thrown when {@link AutoLFInputStream} is told to + * throw an exception when attempting to read a binary file. The exception + * may be thrown at any stage during reading. + * + * @since 3.3 + */ + public static class IsBinaryException extends IOException { + private static final long serialVersionUID = 1L; + + IsBinaryException() { + super(); + } + } + + /** + * Creates a new InputStream, wrapping the specified stream + * + * @param in + * raw input stream + * @param detectBinary + * whether binaries should be detected + * @since 2.0 + */ + public AutoLFInputStream(InputStream in, boolean detectBinary) { + this(in, detectBinary, false); + } + + /** + * Creates a new InputStream, wrapping the specified stream + * + * @param in + * raw input stream + * @param detectBinary + * whether binaries should be detected + * @param abortIfBinary + * throw an IOException if the file is binary + * @since 3.3 + */ + public AutoLFInputStream(InputStream in, boolean detectBinary, + boolean abortIfBinary) { + this.in = in; + this.detectBinary = detectBinary; + this.abortIfBinary = abortIfBinary; + } + + @Override + public int read() throws IOException { + final int read = read(single, 0, 1); + return read == 1 ? single[0] & 0xff : -1; + } + + @Override + public int read(byte[] bs, final int off, final int len) + throws IOException { + if (len == 0) + return 0; + + if (cnt == -1) + return -1; + + int i = off; + final int end = off + len; + + while (i < end) { + if (ptr == cnt && !fillBuffer()) { + break; + } + + byte b = buf[ptr++]; + if (isBinary || b != '\r') { + // Logic for binary files ends here + bs[i++] = b; + continue; + } + + if (ptr == cnt && !fillBuffer()) { + bs[i++] = '\r'; + break; + } + + if (buf[ptr] == '\n') { + bs[i++] = '\n'; + ptr++; + } else + bs[i++] = '\r'; + } + + return i == off ? -1 : i - off; + } + + /** + * @return true if the stream has detected as a binary so far + * @since 3.3 + */ + public boolean isBinary() { + return isBinary; + } + + @Override + public void close() throws IOException { + in.close(); + } + + private boolean fillBuffer() throws IOException { + cnt = in.read(buf, 0, buf.length); + if (cnt < 1) + return false; + if (detectBinary) { + isBinary = RawText.isBinary(buf, cnt); + detectBinary = false; + if (isBinary && abortIfBinary) + throw new IsBinaryException(); + } + ptr = 0; + return true; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java new file mode 100644 index 0000000000..c932b00f3c --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.util.io; + +import java.io.IOException; +import java.io.OutputStream; + +import org.eclipse.jgit.diff.RawText; + +/** + * An OutputStream that reduces CRLF to LF. + * + * Existing single CR are not changed to LF, but retained as is. + * + * A binary check on the first 8000 bytes is performed and in case of binary + * files, canonicalization is turned off (for the complete file). + * + * @since 4.3 + */ +public class AutoLFOutputStream extends OutputStream { + + static final int BUFFER_SIZE = 8000; + + private final OutputStream out; + + private int buf = -1; + + private byte[] binbuf = new byte[BUFFER_SIZE]; + + private byte[] onebytebuf = new byte[1]; + + private int binbufcnt = 0; + + private boolean detectBinary; + + private boolean isBinary; + + /** + * @param out + */ + public AutoLFOutputStream(OutputStream out) { + this(out, true); + } + + /** + * @param out + * @param detectBinary + * whether binaries should be detected + */ + public AutoLFOutputStream(OutputStream out, boolean detectBinary) { + this.out = out; + this.detectBinary = detectBinary; + } + + @Override + public void write(int b) throws IOException { + onebytebuf[0] = (byte) b; + write(onebytebuf, 0, 1); + } + + @Override + public void write(byte[] b) throws IOException { + int overflow = buffer(b, 0, b.length); + if (overflow > 0) { + write(b, b.length - overflow, overflow); + } + } + + @Override + public void write(byte[] b, final int startOff, final int startLen) + throws IOException { + final int overflow = buffer(b, startOff, startLen); + if (overflow < 0) { + return; + } + final int off = startOff + startLen - overflow; + final int len = overflow; + if (len == 0) { + return; + } + int lastw = off; + if (isBinary) { + out.write(b, off, len); + return; + } + for (int i = off; i < off + len; ++i) { + final byte c = b[i]; + if (c == '\r') { + // skip write r but backlog r + if (lastw < i) { + out.write(b, lastw, i - lastw); + } + lastw = i + 1; + buf = '\r'; + } else if (c == '\n') { + if (buf == '\r') { + out.write('\n'); + lastw = i + 1; + buf = -1; + } else { + if (lastw < i + 1) { + out.write(b, lastw, i + 1 - lastw); + } + lastw = i + 1; + } + } else { + if (buf == '\r') { + out.write('\r'); + lastw = i; + } + buf = -1; + } + } + if (lastw < off + len) { + out.write(b, lastw, off + len - lastw); + } + } + + private int buffer(byte[] b, int off, int len) throws IOException { + if (binbufcnt > binbuf.length) { + return len; + } + int copy = Math.min(binbuf.length - binbufcnt, len); + System.arraycopy(b, off, binbuf, binbufcnt, copy); + binbufcnt += copy; + int remaining = len - copy; + if (remaining > 0) { + decideMode(); + } + return remaining; + } + + private void decideMode() throws IOException { + if (detectBinary) { + isBinary = RawText.isBinary(binbuf, binbufcnt); + detectBinary = false; + } + int cachedLen = binbufcnt; + binbufcnt = binbuf.length + 1; // full! + write(binbuf, 0, cachedLen); + } + + @Override + public void flush() throws IOException { + if (binbufcnt <= binbuf.length) { + decideMode(); + } + out.flush(); + } + + @Override + public void close() throws IOException { + flush(); + if (buf == '\r') { + out.write(buf); + buf = -1; + } + out.close(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java index 98485e9090..ee729e893e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java @@ -46,46 +46,16 @@ package org.eclipse.jgit.util.io; import java.io.IOException; import java.io.InputStream; -import org.eclipse.jgit.diff.RawText; - /** * An input stream which canonicalizes EOLs bytes on the fly to '\n'. * - * Optionally, a binary check on the first 8000 bytes is performed - * and in case of binary files, canonicalization is turned off - * (for the complete file). + * Optionally, a binary check on the first 8000 bytes is performed and in case + * of binary files, canonicalization is turned off (for the complete file). + * + * @deprecated use {@link AutoLFInputStream} instead */ -public class EolCanonicalizingInputStream extends InputStream { - private final byte[] single = new byte[1]; - - private final byte[] buf = new byte[8096]; - - private final InputStream in; - - private int cnt; - - private int ptr; - - private boolean isBinary; - - private boolean detectBinary; - - private boolean abortIfBinary; - - /** - * A special exception thrown when {@link EolCanonicalizingInputStream} is - * told to throw an exception when attempting to read a binary file. The - * exception may be thrown at any stage during reading. - * - * @since 3.3 - */ - public static class IsBinaryException extends IOException { - private static final long serialVersionUID = 1L; - - IsBinaryException() { - super(); - } - } +@Deprecated +public class EolCanonicalizingInputStream extends AutoLFInputStream { /** * Creates a new InputStream, wrapping the specified stream @@ -94,10 +64,9 @@ public class EolCanonicalizingInputStream extends InputStream { * raw input stream * @param detectBinary * whether binaries should be detected - * @since 2.0 */ public EolCanonicalizingInputStream(InputStream in, boolean detectBinary) { - this(in, detectBinary, false); + super(in, detectBinary); } /** @@ -109,83 +78,25 @@ public class EolCanonicalizingInputStream extends InputStream { * whether binaries should be detected * @param abortIfBinary * throw an IOException if the file is binary - * @since 3.3 */ public EolCanonicalizingInputStream(InputStream in, boolean detectBinary, boolean abortIfBinary) { - this.in = in; - this.detectBinary = detectBinary; - this.abortIfBinary = abortIfBinary; - } - - @Override - public int read() throws IOException { - final int read = read(single, 0, 1); - return read == 1 ? single[0] & 0xff : -1; - } - - @Override - public int read(byte[] bs, final int off, final int len) throws IOException { - if (len == 0) - return 0; - - if (cnt == -1) - return -1; - - int i = off; - final int end = off + len; - - while (i < end) { - if (ptr == cnt && !fillBuffer()) { - break; - } - - byte b = buf[ptr++]; - if (isBinary || b != '\r') { - // Logic for binary files ends here - bs[i++] = b; - continue; - } - - if (ptr == cnt && !fillBuffer()) { - bs[i++] = '\r'; - break; - } - - if (buf[ptr] == '\n') { - bs[i++] = '\n'; - ptr++; - } else - bs[i++] = '\r'; - } - - return i == off ? -1 : i - off; + super(in, detectBinary, abortIfBinary); } /** - * @return true if the stream has detected as a binary so far + * A special exception thrown when {@link AutoLFInputStream} is told to + * throw an exception when attempting to read a binary file. The exception + * may be thrown at any stage during reading. + * * @since 3.3 */ - public boolean isBinary() { - return isBinary; - } - - @Override - public void close() throws IOException { - in.close(); - } + public static class IsBinaryException extends IOException { + private static final long serialVersionUID = 1L; - private boolean fillBuffer() throws IOException { - cnt = in.read(buf, 0, buf.length); - if (cnt < 1) - return false; - if (detectBinary) { - isBinary = RawText.isBinary(buf, cnt); - detectBinary = false; - if (isBinary && abortIfBinary) - throw new IsBinaryException(); + IsBinaryException() { + super(); } - ptr = 0; - return true; } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java new file mode 100644 index 0000000000..c95992fbc2 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.util.io; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.jgit.attributes.Attributes; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; +import org.eclipse.jgit.treewalk.WorkingTreeOptions; + +/** + * Utility used to create input and output stream wrappers for + * {@link EolStreamType} + * + * @since 4.3 + */ +public final class EolStreamTypeUtil { + private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false; + + private EolStreamTypeUtil() { + } + + /** + * Convenience method used to detect if CRLF conversion has been configured + * using the + * <ul> + * <li>global repo options</li> + * <li>global attributes</li> + * <li>info attributes</li> + * <li>working tree .gitattributes</li> + * + * @param op + * is the {@link OperationType} of the current traversal + * @param options + * are the {@link Config} options with key + * {@link WorkingTreeOptions#KEY} + * @param attrs + * are the {@link Attributes} of the file for which the + * {@link EolStreamType} is to be detected + * + * @return the stream conversion {@link EolStreamType} to be performed for + * the selected {@link OperationType} + */ + public static EolStreamType detectStreamType(OperationType op, + WorkingTreeOptions options, Attributes attrs) { + switch (op) { + case CHECKIN_OP: + return checkInStreamType(options, attrs); + case CHECKOUT_OP: + return checkOutStreamType(options, attrs); + default: + throw new IllegalArgumentException("unknown OperationType " + op); //$NON-NLS-1$ + } + } + + /** + * @param in + * original stream + * @param conversion + * to be performed + * @return the converted stream depending on {@link EolStreamType} + */ + public static InputStream wrapInputStream(InputStream in, + EolStreamType conversion) { + switch (conversion) { + case TEXT_CRLF: + return new AutoCRLFInputStream(in, false); + case TEXT_LF: + return new AutoLFInputStream(in, false); + case AUTO_CRLF: + return new AutoCRLFInputStream(in, true); + case AUTO_LF: + return new AutoLFInputStream(in, true); + default: + return in; + } + } + + /** + * @param out + * original stream + * @param conversion + * to be performed + * @return the converted stream depending on {@link EolStreamType} + */ + public static OutputStream wrapOutputStream(OutputStream out, + EolStreamType conversion) { + switch (conversion) { + case TEXT_CRLF: + return new AutoCRLFOutputStream(out, false); + case AUTO_CRLF: + return new AutoCRLFOutputStream(out, true); + case TEXT_LF: + return new AutoLFOutputStream(out, false); + case AUTO_LF: + return new AutoLFOutputStream(out, true); + default: + return out; + } + } + + private static EolStreamType checkInStreamType(WorkingTreeOptions options, + Attributes attrs) { + // old git system + if (attrs.isSet("crlf")) {//$NON-NLS-1$ + return EolStreamType.TEXT_LF; + } else if (attrs.isUnset("crlf")) {//$NON-NLS-1$ + return EolStreamType.DIRECT; + } else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$ + return EolStreamType.TEXT_LF; + } + + // new git system + if (attrs.isUnset("text")) {//$NON-NLS-1$ + return EolStreamType.DIRECT; + } + String eol = attrs.getValue("eol"); //$NON-NLS-1$ + if (eol != null) + // check-in is always normalized to LF + return EolStreamType.TEXT_LF; + + if (attrs.isSet("text")) { //$NON-NLS-1$ + return EolStreamType.TEXT_LF; + } + + if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$ + return EolStreamType.AUTO_LF; + } + + switch (options.getAutoCRLF()) { + case TRUE: + case INPUT: + return EolStreamType.AUTO_LF; + case FALSE: + return EolStreamType.DIRECT; + } + + return EolStreamType.DIRECT; + } + + private static EolStreamType checkOutStreamType(WorkingTreeOptions options, + Attributes attrs) { + // old git system + if (attrs.isSet("crlf")) {//$NON-NLS-1$ + return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF + : EolStreamType.DIRECT; + } else if (attrs.isUnset("crlf")) {//$NON-NLS-1$ + return EolStreamType.DIRECT; + } else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$ + return EolStreamType.DIRECT; + } + + // new git system + if (attrs.isUnset("text")) {//$NON-NLS-1$ + return EolStreamType.DIRECT; + } + String eol = attrs.getValue("eol"); //$NON-NLS-1$ + if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$ + return EolStreamType.TEXT_CRLF; + if (eol != null && "lf".equals(eol)) //$NON-NLS-1$ + return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF + : EolStreamType.DIRECT; + + if (attrs.isSet("text")) { //$NON-NLS-1$ + switch (options.getAutoCRLF()) { + case TRUE: + return EolStreamType.TEXT_CRLF; + default: + // no decision + } + switch (options.getEOL()) { + case CRLF: + return EolStreamType.TEXT_CRLF; + case LF: + return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF + : EolStreamType.DIRECT; + case NATIVE: + default: + return EolStreamType.DIRECT; + } + } + + if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$ + switch (options.getAutoCRLF()) { + case TRUE: + return EolStreamType.AUTO_CRLF; + default: + // no decision + } + switch (options.getEOL()) { + case CRLF: + return EolStreamType.AUTO_CRLF; + case LF: + return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF + : EolStreamType.DIRECT; + case NATIVE: + default: + return EolStreamType.DIRECT; + } + } + + switch (options.getAutoCRLF()) { + case TRUE: + return EolStreamType.AUTO_CRLF; + default: + // no decision + } + + return EolStreamType.DIRECT; + } + +} @@ -51,7 +51,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> <packaging>pom</packaging> - <version>4.2.1-SNAPSHOT</version> + <version>4.3.2-SNAPSHOT</version> <name>JGit - Parent</name> <url>${jgit-url}</url> @@ -192,7 +192,7 @@ <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format> <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest> - <jgit-last-release-version>4.1.0.201509280440-r</jgit-last-release-version> + <jgit-last-release-version>4.2.0.201601211800-r</jgit-last-release-version> <jsch-version>0.1.53</jsch-version> <javaewah-version>0.7.9</javaewah-version> <junit-version>4.11</junit-version> @@ -208,6 +208,7 @@ <log4j-version>1.2.15</log4j-version> <maven-javadoc-plugin-version>2.10.1</maven-javadoc-plugin-version> <tycho-extras-version>0.23.0</tycho-extras-version> + <gson-version>2.2.4</gson-version> <!-- Properties to enable jacoco code coverage analysis --> <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> @@ -632,6 +633,12 @@ </exclusion> </exclusions> </dependency> + + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>${gson-version}</version> + </dependency> </dependencies> </dependencyManagement> @@ -767,6 +774,8 @@ <module>org.eclipse.jgit.http.apache</module> <module>org.eclipse.jgit.http.server</module> <module>org.eclipse.jgit.pgm</module> + <module>org.eclipse.jgit.lfs</module> + <module>org.eclipse.jgit.lfs.server</module> <module>org.eclipse.jgit.junit</module> <module>org.eclipse.jgit.junit.http</module> @@ -774,6 +783,8 @@ <module>org.eclipse.jgit.ant.test</module> <module>org.eclipse.jgit.http.test</module> <module>org.eclipse.jgit.pgm.test</module> + <module>org.eclipse.jgit.lfs.test</module> + <module>org.eclipse.jgit.lfs.server.test</module> </modules> </project> diff --git a/tools/maven-central/deploy.rb b/tools/maven-central/deploy.rb index 8e7adc8934..2744e772a1 100755 --- a/tools/maven-central/deploy.rb +++ b/tools/maven-central/deploy.rb @@ -55,6 +55,8 @@ artifacts = [group, group + '.http.server', group + '.junit', group + '.junit.http', + group + '.lfs', + group + '.lfs.server', group + '.pgm', group + '.ui'] diff --git a/tools/maven-central/download.rb b/tools/maven-central/download.rb index b7ea2150ba..b6c2671742 100755 --- a/tools/maven-central/download.rb +++ b/tools/maven-central/download.rb @@ -15,6 +15,8 @@ artifacts = [group, group + '.http.server', group + '.junit', group + '.junit.http', + group + '.lfs', + group + '.lfs.server', group + '.pgm', group + '.ui'] |